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