1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
|
// GoToSocial
// Copyright (C) GoToSocial Authors admin@gotosocial.org
// SPDX-License-Identifier: AGPL-3.0-or-later
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU Affero General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU Affero General Public License for more details.
//
// You should have received a copy of the GNU Affero General Public License
// along with this program. If not, see <http://www.gnu.org/licenses/>.
package visibility
import (
"context"
"github.com/superseriousbusiness/gotosocial/internal/cache"
"github.com/superseriousbusiness/gotosocial/internal/config"
"github.com/superseriousbusiness/gotosocial/internal/gtserror"
"github.com/superseriousbusiness/gotosocial/internal/gtsmodel"
"github.com/superseriousbusiness/gotosocial/internal/log"
)
// AccountVisible will check if given account is visible to requester, accounting for requester with no auth (i.e is nil), suspensions, disabled local users and account blocks.
func (f *Filter) AccountVisible(ctx context.Context, requester *gtsmodel.Account, account *gtsmodel.Account) (bool, error) {
const vtype = cache.VisibilityTypeAccount
// By default we assume no auth.
requesterID := noauth
if requester != nil {
// Use provided account ID.
requesterID = requester.ID
}
visibility, err := f.state.Caches.Visibility.LoadOne("Type,RequesterID,ItemID", func() (*cache.CachedVisibility, error) {
// Visibility not yet cached, perform visibility lookup.
visible, err := f.isAccountVisibleTo(ctx, requester, account)
if err != nil {
return nil, err
}
// Return visibility value.
return &cache.CachedVisibility{
ItemID: account.ID,
RequesterID: requesterID,
Type: vtype,
Value: visible,
}, nil
}, vtype, requesterID, account.ID)
if err != nil {
return false, err
}
return visibility.Value, nil
}
// isAccountVisibleTo will check if account is visible to requester. It is the "meat" of the logic to Filter{}.AccountVisible() which is called within cache loader callback.
func (f *Filter) isAccountVisibleTo(ctx context.Context, requester *gtsmodel.Account, account *gtsmodel.Account) (bool, error) {
// Check whether target account is visible to anyone.
visible, err := f.isAccountVisible(ctx, account)
if err != nil {
return false, gtserror.Newf("error checking account %s visibility: %w", account.ID, err)
}
if !visible {
log.Trace(ctx, "target account is not visible to anyone")
return false, nil
}
if requester == nil {
// It seems stupid, but when un-authed all accounts are
// visible to allow for federation to work correctly.
return true, nil
}
// If requester is not visible, they cannot *see* either.
visible, err = f.isAccountVisible(ctx, requester)
if err != nil {
return false, gtserror.Newf("error checking account %s visibility: %w", account.ID, err)
}
if !visible {
log.Trace(ctx, "requesting account cannot see other accounts")
return false, nil
}
// Check whether either blocks the other.
blocked, err := f.state.DB.IsEitherBlocked(ctx,
requester.ID,
account.ID,
)
if err != nil {
return false, gtserror.Newf("error checking account blocks: %w", err)
}
if blocked {
log.Trace(ctx, "block exists between accounts")
return false, nil
}
return true, nil
}
// isAccountVisible will check if given account should be visible at all, e.g. it may not be if suspended or disabled.
func (f *Filter) isAccountVisible(ctx context.Context, account *gtsmodel.Account) (bool, error) {
if account.IsLocal() {
// This is a local account.
if account.Username == config.GetHost() {
// This is the instance actor account.
return true, nil
}
// Fetch the local user model for this account.
user, err := f.state.DB.GetUserByAccountID(ctx, account.ID)
if err != nil {
err := gtserror.Newf("db error getting user for account %s: %w", account.ID, err)
return false, err
}
// Make sure that user is active (i.e. not disabled, not approved etc).
if *user.Disabled || !*user.Approved || user.ConfirmedAt.IsZero() {
log.Trace(ctx, "local account not active")
return false, nil
}
} else {
// This is a remote account.
// Check whether remote account's domain is blocked.
blocked, err := f.state.DB.IsDomainBlocked(ctx, account.Domain)
if err != nil {
return false, err
}
if blocked {
log.Trace(ctx, "remote account domain blocked")
return false, nil
}
}
if !account.SuspendedAt.IsZero() {
log.Trace(ctx, "account suspended")
return false, nil
}
return true, nil
}
|