[bugfix] Allow blocked accounts to show in precise search (#2321)
This commit is contained in:
parent
4dc0547dc0
commit
dd4b0241ea
|
@ -38,6 +38,7 @@ import (
|
||||||
"github.com/superseriousbusiness/gotosocial/internal/config"
|
"github.com/superseriousbusiness/gotosocial/internal/config"
|
||||||
"github.com/superseriousbusiness/gotosocial/internal/gtserror"
|
"github.com/superseriousbusiness/gotosocial/internal/gtserror"
|
||||||
"github.com/superseriousbusiness/gotosocial/internal/gtsmodel"
|
"github.com/superseriousbusiness/gotosocial/internal/gtsmodel"
|
||||||
|
"github.com/superseriousbusiness/gotosocial/internal/id"
|
||||||
"github.com/superseriousbusiness/gotosocial/internal/oauth"
|
"github.com/superseriousbusiness/gotosocial/internal/oauth"
|
||||||
"github.com/superseriousbusiness/gotosocial/testrig"
|
"github.com/superseriousbusiness/gotosocial/testrig"
|
||||||
)
|
)
|
||||||
|
@ -1179,8 +1180,9 @@ func (suite *SearchGetTestSuite) TestSearchLocalInstanceAccountPartial() {
|
||||||
}
|
}
|
||||||
|
|
||||||
// Query was a partial namestring from our
|
// Query was a partial namestring from our
|
||||||
// instance, so will return the instance account.
|
// instance, instance account should be
|
||||||
suite.Len(searchResult.Accounts, 1)
|
// excluded from results.
|
||||||
|
suite.Len(searchResult.Accounts, 0)
|
||||||
suite.Len(searchResult.Statuses, 0)
|
suite.Len(searchResult.Statuses, 0)
|
||||||
suite.Len(searchResult.Hashtags, 0)
|
suite.Len(searchResult.Hashtags, 0)
|
||||||
}
|
}
|
||||||
|
@ -1546,6 +1548,189 @@ func (suite *SearchGetTestSuite) TestSearchNotHashtagButWithTypeHashtag() {
|
||||||
suite.Len(searchResult.Hashtags, 1)
|
suite.Len(searchResult.Hashtags, 1)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (suite *SearchGetTestSuite) TestSearchBlockedAccountFullNamestring() {
|
||||||
|
var (
|
||||||
|
requestingAccount = suite.testAccounts["local_account_1"]
|
||||||
|
targetAccount = suite.testAccounts["remote_account_1"]
|
||||||
|
token = suite.testTokens["local_account_1"]
|
||||||
|
user = suite.testUsers["local_account_1"]
|
||||||
|
maxID *string = nil
|
||||||
|
minID *string = nil
|
||||||
|
limit *int = nil
|
||||||
|
offset *int = nil
|
||||||
|
resolve *bool = func() *bool { i := true; return &i }()
|
||||||
|
query = "@" + targetAccount.Username + "@" + targetAccount.Domain
|
||||||
|
queryType *string = func() *string { i := "accounts"; return &i }()
|
||||||
|
following *bool = nil
|
||||||
|
expectedHTTPStatus = http.StatusOK
|
||||||
|
expectedBody = ""
|
||||||
|
)
|
||||||
|
|
||||||
|
// Block the account
|
||||||
|
// we're about to search.
|
||||||
|
if err := suite.db.PutBlock(
|
||||||
|
context.Background(),
|
||||||
|
>smodel.Block{
|
||||||
|
ID: id.NewULID(),
|
||||||
|
URI: "https://example.org/nooooooo",
|
||||||
|
AccountID: requestingAccount.ID,
|
||||||
|
TargetAccountID: targetAccount.ID,
|
||||||
|
},
|
||||||
|
); err != nil {
|
||||||
|
suite.FailNow(err.Error())
|
||||||
|
}
|
||||||
|
|
||||||
|
searchResult, err := suite.getSearch(
|
||||||
|
requestingAccount,
|
||||||
|
token,
|
||||||
|
apiutil.APIv2,
|
||||||
|
user,
|
||||||
|
maxID,
|
||||||
|
minID,
|
||||||
|
limit,
|
||||||
|
offset,
|
||||||
|
query,
|
||||||
|
queryType,
|
||||||
|
resolve,
|
||||||
|
following,
|
||||||
|
expectedHTTPStatus,
|
||||||
|
expectedBody)
|
||||||
|
if err != nil {
|
||||||
|
suite.FailNow(err.Error())
|
||||||
|
}
|
||||||
|
|
||||||
|
// Search was for full namestring;
|
||||||
|
// we should still be able to see
|
||||||
|
// the account we've blocked.
|
||||||
|
if !suite.Len(searchResult.Accounts, 1) {
|
||||||
|
suite.FailNow("expected 1 account in search results but got 0")
|
||||||
|
}
|
||||||
|
|
||||||
|
gotAccount := searchResult.Accounts[0]
|
||||||
|
suite.NotNil(gotAccount)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (suite *SearchGetTestSuite) TestSearchBlockedAccountPartialNamestring() {
|
||||||
|
var (
|
||||||
|
requestingAccount = suite.testAccounts["local_account_1"]
|
||||||
|
targetAccount = suite.testAccounts["remote_account_1"]
|
||||||
|
token = suite.testTokens["local_account_1"]
|
||||||
|
user = suite.testUsers["local_account_1"]
|
||||||
|
maxID *string = nil
|
||||||
|
minID *string = nil
|
||||||
|
limit *int = nil
|
||||||
|
offset *int = nil
|
||||||
|
resolve *bool = func() *bool { i := true; return &i }()
|
||||||
|
query = "@" + targetAccount.Username
|
||||||
|
queryType *string = func() *string { i := "accounts"; return &i }()
|
||||||
|
following *bool = nil
|
||||||
|
expectedHTTPStatus = http.StatusOK
|
||||||
|
expectedBody = ""
|
||||||
|
)
|
||||||
|
|
||||||
|
// Block the account
|
||||||
|
// we're about to search.
|
||||||
|
if err := suite.db.PutBlock(
|
||||||
|
context.Background(),
|
||||||
|
>smodel.Block{
|
||||||
|
ID: id.NewULID(),
|
||||||
|
URI: "https://example.org/nooooooo",
|
||||||
|
AccountID: requestingAccount.ID,
|
||||||
|
TargetAccountID: targetAccount.ID,
|
||||||
|
},
|
||||||
|
); err != nil {
|
||||||
|
suite.FailNow(err.Error())
|
||||||
|
}
|
||||||
|
|
||||||
|
searchResult, err := suite.getSearch(
|
||||||
|
requestingAccount,
|
||||||
|
token,
|
||||||
|
apiutil.APIv2,
|
||||||
|
user,
|
||||||
|
maxID,
|
||||||
|
minID,
|
||||||
|
limit,
|
||||||
|
offset,
|
||||||
|
query,
|
||||||
|
queryType,
|
||||||
|
resolve,
|
||||||
|
following,
|
||||||
|
expectedHTTPStatus,
|
||||||
|
expectedBody)
|
||||||
|
if err != nil {
|
||||||
|
suite.FailNow(err.Error())
|
||||||
|
}
|
||||||
|
|
||||||
|
// Search was for partial namestring;
|
||||||
|
// we should not be able to see
|
||||||
|
// the account we've blocked.
|
||||||
|
if !suite.Empty(searchResult.Accounts) {
|
||||||
|
suite.FailNow("expected 0 accounts in search results")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (suite *SearchGetTestSuite) TestSearchBlockedAccountURI() {
|
||||||
|
var (
|
||||||
|
requestingAccount = suite.testAccounts["local_account_1"]
|
||||||
|
targetAccount = suite.testAccounts["remote_account_1"]
|
||||||
|
token = suite.testTokens["local_account_1"]
|
||||||
|
user = suite.testUsers["local_account_1"]
|
||||||
|
maxID *string = nil
|
||||||
|
minID *string = nil
|
||||||
|
limit *int = nil
|
||||||
|
offset *int = nil
|
||||||
|
resolve *bool = func() *bool { i := true; return &i }()
|
||||||
|
query = targetAccount.URI
|
||||||
|
queryType *string = func() *string { i := "accounts"; return &i }()
|
||||||
|
following *bool = nil
|
||||||
|
expectedHTTPStatus = http.StatusOK
|
||||||
|
expectedBody = ""
|
||||||
|
)
|
||||||
|
|
||||||
|
// Block the account
|
||||||
|
// we're about to search.
|
||||||
|
if err := suite.db.PutBlock(
|
||||||
|
context.Background(),
|
||||||
|
>smodel.Block{
|
||||||
|
ID: id.NewULID(),
|
||||||
|
URI: "https://example.org/nooooooo",
|
||||||
|
AccountID: requestingAccount.ID,
|
||||||
|
TargetAccountID: targetAccount.ID,
|
||||||
|
},
|
||||||
|
); err != nil {
|
||||||
|
suite.FailNow(err.Error())
|
||||||
|
}
|
||||||
|
|
||||||
|
searchResult, err := suite.getSearch(
|
||||||
|
requestingAccount,
|
||||||
|
token,
|
||||||
|
apiutil.APIv2,
|
||||||
|
user,
|
||||||
|
maxID,
|
||||||
|
minID,
|
||||||
|
limit,
|
||||||
|
offset,
|
||||||
|
query,
|
||||||
|
queryType,
|
||||||
|
resolve,
|
||||||
|
following,
|
||||||
|
expectedHTTPStatus,
|
||||||
|
expectedBody)
|
||||||
|
if err != nil {
|
||||||
|
suite.FailNow(err.Error())
|
||||||
|
}
|
||||||
|
|
||||||
|
// Search was for precise URI;
|
||||||
|
// we should still be able to see
|
||||||
|
// the account we've blocked.
|
||||||
|
if !suite.Len(searchResult.Accounts, 1) {
|
||||||
|
suite.FailNow("expected 1 account in search results but got 0")
|
||||||
|
}
|
||||||
|
|
||||||
|
gotAccount := searchResult.Accounts[0]
|
||||||
|
suite.NotNil(gotAccount)
|
||||||
|
}
|
||||||
|
|
||||||
func TestSearchGetTestSuite(t *testing.T) {
|
func TestSearchGetTestSuite(t *testing.T) {
|
||||||
suite.Run(t, &SearchGetTestSuite{})
|
suite.Run(t, &SearchGetTestSuite{})
|
||||||
}
|
}
|
||||||
|
|
|
@ -52,8 +52,9 @@ func (p *Processor) StatusesGet(
|
||||||
}
|
}
|
||||||
|
|
||||||
if blocked {
|
if blocked {
|
||||||
err := errors.New("block exists between accounts")
|
// Block exists between accounts.
|
||||||
return nil, gtserror.NewErrorNotFound(err)
|
// Just return empty statuses.
|
||||||
|
return util.EmptyPageableResponse(), nil
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -29,6 +29,7 @@ import (
|
||||||
"github.com/superseriousbusiness/gotosocial/internal/gtsmodel"
|
"github.com/superseriousbusiness/gotosocial/internal/gtsmodel"
|
||||||
"github.com/superseriousbusiness/gotosocial/internal/id"
|
"github.com/superseriousbusiness/gotosocial/internal/id"
|
||||||
"github.com/superseriousbusiness/gotosocial/internal/log"
|
"github.com/superseriousbusiness/gotosocial/internal/log"
|
||||||
|
"github.com/superseriousbusiness/gotosocial/internal/util"
|
||||||
)
|
)
|
||||||
|
|
||||||
// Accounts does a partial search for accounts that
|
// Accounts does a partial search for accounts that
|
||||||
|
@ -56,6 +57,11 @@ func (p *Processor) Accounts(
|
||||||
// in their search results.
|
// in their search results.
|
||||||
const includeInstanceAccounts = false
|
const includeInstanceAccounts = false
|
||||||
|
|
||||||
|
// We *might* want to include blocked accounts
|
||||||
|
// in this search, but only if it's a search
|
||||||
|
// for a specific account.
|
||||||
|
includeBlockedAccounts := false
|
||||||
|
|
||||||
var (
|
var (
|
||||||
foundAccounts = make([]*gtsmodel.Account, 0, limit)
|
foundAccounts = make([]*gtsmodel.Account, 0, limit)
|
||||||
appendAccount = func(foundAccount *gtsmodel.Account) { foundAccounts = append(foundAccounts, foundAccount) }
|
appendAccount = func(foundAccount *gtsmodel.Account) { foundAccounts = append(foundAccounts, foundAccount) }
|
||||||
|
@ -95,20 +101,35 @@ func (p *Processor) Accounts(
|
||||||
requestingAccount,
|
requestingAccount,
|
||||||
foundAccounts,
|
foundAccounts,
|
||||||
includeInstanceAccounts,
|
includeInstanceAccounts,
|
||||||
|
includeBlockedAccounts,
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Return all accounts we can find that match the
|
// See if we have something that looks like a namestring.
|
||||||
// provided query. If it's not a namestring, this
|
username, domain, err := util.ExtractNamestringParts(query)
|
||||||
// won't return an error, it'll just return 0 results.
|
if err != nil {
|
||||||
if _, err := p.accountsByNamestring(
|
log.Warnf(ctx, "couldn't parse '%s' as namestring: %v", query, err)
|
||||||
|
} else {
|
||||||
|
if domain != "" {
|
||||||
|
// Search was an exact namestring;
|
||||||
|
// we can safely assume caller is
|
||||||
|
// searching for a specific account,
|
||||||
|
// and show it to them even if they
|
||||||
|
// have it blocked.
|
||||||
|
includeBlockedAccounts = true
|
||||||
|
}
|
||||||
|
|
||||||
|
// Get all accounts we can find
|
||||||
|
// that match the provided query.
|
||||||
|
if err := p.accountsByNamestring(
|
||||||
ctx,
|
ctx,
|
||||||
requestingAccount,
|
requestingAccount,
|
||||||
id.Highest,
|
id.Highest,
|
||||||
id.Lowest,
|
id.Lowest,
|
||||||
limit,
|
limit,
|
||||||
offset,
|
offset,
|
||||||
query,
|
username,
|
||||||
|
domain,
|
||||||
resolve,
|
resolve,
|
||||||
following,
|
following,
|
||||||
appendAccount,
|
appendAccount,
|
||||||
|
@ -116,6 +137,7 @@ func (p *Processor) Accounts(
|
||||||
err = gtserror.Newf("error searching by namestring: %w", err)
|
err = gtserror.Newf("error searching by namestring: %w", err)
|
||||||
return nil, gtserror.NewErrorInternalError(err)
|
return nil, gtserror.NewErrorInternalError(err)
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// Return whatever we got (if anything).
|
// Return whatever we got (if anything).
|
||||||
return p.packageAccounts(
|
return p.packageAccounts(
|
||||||
|
@ -123,5 +145,6 @@ func (p *Processor) Accounts(
|
||||||
requestingAccount,
|
requestingAccount,
|
||||||
foundAccounts,
|
foundAccounts,
|
||||||
includeInstanceAccounts,
|
includeInstanceAccounts,
|
||||||
|
includeBlockedAccounts,
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
|
@ -77,6 +77,12 @@ func (p *Processor) Get(
|
||||||
// search in the database in the latter
|
// search in the database in the latter
|
||||||
// parts of this function.
|
// parts of this function.
|
||||||
includeInstanceAccounts = true
|
includeInstanceAccounts = true
|
||||||
|
|
||||||
|
// Assume caller doesn't want to see
|
||||||
|
// blocked accounts. This will change
|
||||||
|
// to 'true' if caller is searching
|
||||||
|
// for a specific account.
|
||||||
|
includeBlockedAccounts = false
|
||||||
)
|
)
|
||||||
|
|
||||||
// Validate query.
|
// Validate query.
|
||||||
|
@ -120,7 +126,9 @@ func (p *Processor) Get(
|
||||||
ctx,
|
ctx,
|
||||||
account,
|
account,
|
||||||
nil, nil, nil, // No results.
|
nil, nil, nil, // No results.
|
||||||
req.APIv1, includeInstanceAccounts,
|
req.APIv1,
|
||||||
|
includeInstanceAccounts,
|
||||||
|
includeBlockedAccounts,
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -131,7 +139,6 @@ func (p *Processor) Get(
|
||||||
appendStatus = func(s *gtsmodel.Status) { foundStatuses = append(foundStatuses, s) }
|
appendStatus = func(s *gtsmodel.Status) { foundStatuses = append(foundStatuses, s) }
|
||||||
appendAccount = func(a *gtsmodel.Account) { foundAccounts = append(foundAccounts, a) }
|
appendAccount = func(a *gtsmodel.Account) { foundAccounts = append(foundAccounts, a) }
|
||||||
appendTag = func(t *gtsmodel.Tag) { foundTags = append(foundTags, t) }
|
appendTag = func(t *gtsmodel.Tag) { foundTags = append(foundTags, t) }
|
||||||
keepLooking bool
|
|
||||||
err error
|
err error
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -152,15 +159,27 @@ func (p *Processor) Get(
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Search using what may or may not be a namestring.
|
// See if we have something that looks like a namestring.
|
||||||
keepLooking, err = p.accountsByNamestring(
|
username, domain, err := util.ExtractNamestringParts(queryC)
|
||||||
|
if err == nil {
|
||||||
|
// We managed to parse query as a namestring.
|
||||||
|
// If domain was set, this is a very specific
|
||||||
|
// search for a particular account, so show
|
||||||
|
// that account to the caller even if they
|
||||||
|
// have it blocked. They might be looking
|
||||||
|
// for it to unblock it again!
|
||||||
|
domainSet := (domain != "")
|
||||||
|
includeBlockedAccounts = domainSet
|
||||||
|
|
||||||
|
err = p.accountsByNamestring(
|
||||||
ctx,
|
ctx,
|
||||||
account,
|
account,
|
||||||
maxID,
|
maxID,
|
||||||
minID,
|
minID,
|
||||||
limit,
|
limit,
|
||||||
offset,
|
offset,
|
||||||
queryC,
|
username,
|
||||||
|
domain,
|
||||||
resolve,
|
resolve,
|
||||||
following,
|
following,
|
||||||
appendAccount,
|
appendAccount,
|
||||||
|
@ -170,8 +189,13 @@ func (p *Processor) Get(
|
||||||
return nil, gtserror.NewErrorInternalError(err)
|
return nil, gtserror.NewErrorInternalError(err)
|
||||||
}
|
}
|
||||||
|
|
||||||
if !keepLooking {
|
// If domain was set, we know this is
|
||||||
// Return whatever we have.
|
// a full namestring, and not a url or
|
||||||
|
// just a username, so we should stop
|
||||||
|
// looking now and just return what we
|
||||||
|
// have (if anything). Otherwise we'll
|
||||||
|
// let the search keep going.
|
||||||
|
if domainSet {
|
||||||
return p.packageSearchResult(
|
return p.packageSearchResult(
|
||||||
ctx,
|
ctx,
|
||||||
account,
|
account,
|
||||||
|
@ -180,28 +204,38 @@ func (p *Processor) Get(
|
||||||
foundTags,
|
foundTags,
|
||||||
req.APIv1,
|
req.APIv1,
|
||||||
includeInstanceAccounts,
|
includeInstanceAccounts,
|
||||||
|
includeBlockedAccounts,
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// Check if the query is a URI with a recognizable
|
// Check if we're searching by a known URI scheme.
|
||||||
// scheme and use it to look for accounts or statuses.
|
// (This might just be a weirdly-parsed URI,
|
||||||
keepLooking, err = p.byURI(
|
// since Go's url package tends to be a bit
|
||||||
|
// trigger-happy when deciding things are URIs).
|
||||||
|
uri, err := url.Parse(query)
|
||||||
|
if err == nil && (uri.Scheme == "https" || uri.Scheme == "http") {
|
||||||
|
// URI is pretty specific so we can safely assume
|
||||||
|
// caller wants to include blocked accounts too.
|
||||||
|
includeBlockedAccounts = true
|
||||||
|
|
||||||
|
if err := p.byURI(
|
||||||
ctx,
|
ctx,
|
||||||
account,
|
account,
|
||||||
query,
|
uri,
|
||||||
queryType,
|
queryType,
|
||||||
resolve,
|
resolve,
|
||||||
appendAccount,
|
appendAccount,
|
||||||
appendStatus,
|
appendStatus,
|
||||||
)
|
); err != nil && !errors.Is(err, db.ErrNoEntries) {
|
||||||
if err != nil && !errors.Is(err, db.ErrNoEntries) {
|
|
||||||
err = gtserror.Newf("error searching by URI: %w", err)
|
err = gtserror.Newf("error searching by URI: %w", err)
|
||||||
return nil, gtserror.NewErrorInternalError(err)
|
return nil, gtserror.NewErrorInternalError(err)
|
||||||
}
|
}
|
||||||
|
|
||||||
if !keepLooking {
|
// This was a URI, so at this point just return
|
||||||
// Return whatever we have.
|
// whatever we have. You can't search hashtags by
|
||||||
|
// URI, and shouldn't do full-text with a URI either.
|
||||||
return p.packageSearchResult(
|
return p.packageSearchResult(
|
||||||
ctx,
|
ctx,
|
||||||
account,
|
account,
|
||||||
|
@ -210,6 +244,7 @@ func (p *Processor) Get(
|
||||||
foundTags,
|
foundTags,
|
||||||
req.APIv1,
|
req.APIv1,
|
||||||
includeInstanceAccounts,
|
includeInstanceAccounts,
|
||||||
|
includeBlockedAccounts,
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -226,7 +261,7 @@ func (p *Processor) Get(
|
||||||
// We know that none of the subsequent searches
|
// We know that none of the subsequent searches
|
||||||
// would show any good results either, and those
|
// would show any good results either, and those
|
||||||
// searches are *much* more expensive.
|
// searches are *much* more expensive.
|
||||||
keepLooking, err = p.hashtag(
|
keepLooking, err := p.hashtag(
|
||||||
ctx,
|
ctx,
|
||||||
maxID,
|
maxID,
|
||||||
minID,
|
minID,
|
||||||
|
@ -251,6 +286,7 @@ func (p *Processor) Get(
|
||||||
foundTags,
|
foundTags,
|
||||||
req.APIv1,
|
req.APIv1,
|
||||||
includeInstanceAccounts,
|
includeInstanceAccounts,
|
||||||
|
includeBlockedAccounts,
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -291,15 +327,15 @@ func (p *Processor) Get(
|
||||||
foundTags,
|
foundTags,
|
||||||
req.APIv1,
|
req.APIv1,
|
||||||
includeInstanceAccounts,
|
includeInstanceAccounts,
|
||||||
|
includeBlockedAccounts,
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
// accountsByNamestring searches for accounts using the
|
// accountsByNamestring searches for accounts using the
|
||||||
// provided namestring query. If domain is not set in
|
// provided username and domain. If domain is not set,
|
||||||
// the namestring, it may return more than one result
|
// it may return more than one result by doing a text
|
||||||
// by doing a text search in the database for accounts
|
// search in the database for accounts matching the query.
|
||||||
// matching the query. Otherwise, it tries to return an
|
// Otherwise, it tries to return an exact match.
|
||||||
// exact match.
|
|
||||||
func (p *Processor) accountsByNamestring(
|
func (p *Processor) accountsByNamestring(
|
||||||
ctx context.Context,
|
ctx context.Context,
|
||||||
requestingAccount *gtsmodel.Account,
|
requestingAccount *gtsmodel.Account,
|
||||||
|
@ -307,26 +343,18 @@ func (p *Processor) accountsByNamestring(
|
||||||
minID string,
|
minID string,
|
||||||
limit int,
|
limit int,
|
||||||
offset int,
|
offset int,
|
||||||
query string,
|
username string,
|
||||||
|
domain string,
|
||||||
resolve bool,
|
resolve bool,
|
||||||
following bool,
|
following bool,
|
||||||
appendAccount func(*gtsmodel.Account),
|
appendAccount func(*gtsmodel.Account),
|
||||||
) (bool, error) {
|
) error {
|
||||||
// See if we have something that looks like a namestring.
|
|
||||||
username, domain, err := util.ExtractNamestringParts(query)
|
|
||||||
if err != nil {
|
|
||||||
// No need to return error; just not a namestring
|
|
||||||
// we can search with. Caller should keep looking
|
|
||||||
// with another search method.
|
|
||||||
return true, nil //nolint:nilerr
|
|
||||||
}
|
|
||||||
|
|
||||||
if domain == "" {
|
if domain == "" {
|
||||||
// No error, but no domain set. That means the query
|
// No error, but no domain set. That means the query
|
||||||
// looked like '@someone' which is not an exact search.
|
// looked like '@someone' which is not an exact search.
|
||||||
// Try to search for any accounts that match the query
|
// Try to search for any accounts that match the query
|
||||||
// string, and let the caller know they should stop.
|
// string, and let the caller know they should stop.
|
||||||
return false, p.accountsByText(
|
return p.accountsByText(
|
||||||
ctx,
|
ctx,
|
||||||
requestingAccount.ID,
|
requestingAccount.ID,
|
||||||
maxID,
|
maxID,
|
||||||
|
@ -355,18 +383,14 @@ func (p *Processor) accountsByNamestring(
|
||||||
// Check for semi-expected error types.
|
// Check for semi-expected error types.
|
||||||
// On one of these, we can continue.
|
// On one of these, we can continue.
|
||||||
if !gtserror.Unretrievable(err) && !gtserror.WrongType(err) {
|
if !gtserror.Unretrievable(err) && !gtserror.WrongType(err) {
|
||||||
err = gtserror.Newf("error looking up %s as account: %w", query, err)
|
err = gtserror.Newf("error looking up @%s@%s as account: %w", username, domain, err)
|
||||||
return false, gtserror.NewErrorInternalError(err)
|
return gtserror.NewErrorInternalError(err)
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
appendAccount(foundAccount)
|
appendAccount(foundAccount)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Regardless of whether we have a hit at this point,
|
return nil
|
||||||
// return false to indicate caller should stop looking;
|
|
||||||
// namestrings are a very specific format so it's unlikely
|
|
||||||
// the caller was looking for something other than an account.
|
|
||||||
return false, nil
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// accountByUsernameDomain looks for one account with the given
|
// accountByUsernameDomain looks for one account with the given
|
||||||
|
@ -443,38 +467,22 @@ func (p *Processor) accountByUsernameDomain(
|
||||||
func (p *Processor) byURI(
|
func (p *Processor) byURI(
|
||||||
ctx context.Context,
|
ctx context.Context,
|
||||||
requestingAccount *gtsmodel.Account,
|
requestingAccount *gtsmodel.Account,
|
||||||
query string,
|
uri *url.URL,
|
||||||
queryType string,
|
queryType string,
|
||||||
resolve bool,
|
resolve bool,
|
||||||
appendAccount func(*gtsmodel.Account),
|
appendAccount func(*gtsmodel.Account),
|
||||||
appendStatus func(*gtsmodel.Status),
|
appendStatus func(*gtsmodel.Status),
|
||||||
) (bool, error) {
|
) error {
|
||||||
uri, err := url.Parse(query)
|
|
||||||
if err != nil {
|
|
||||||
// No need to return error; just not a URI
|
|
||||||
// we can search with. Caller should keep
|
|
||||||
// looking with another search method.
|
|
||||||
return true, nil //nolint:nilerr
|
|
||||||
}
|
|
||||||
|
|
||||||
if !(uri.Scheme == "https" || uri.Scheme == "http") {
|
|
||||||
// This might just be a weirdly-parsed URI,
|
|
||||||
// since Go's url package tends to be a bit
|
|
||||||
// trigger-happy when deciding things are URIs.
|
|
||||||
// Indicate caller should keep looking.
|
|
||||||
return true, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
blocked, err := p.state.DB.IsURIBlocked(ctx, uri)
|
blocked, err := p.state.DB.IsURIBlocked(ctx, uri)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
err = gtserror.Newf("error checking domain block: %w", err)
|
err = gtserror.Newf("error checking domain block: %w", err)
|
||||||
return false, gtserror.NewErrorInternalError(err)
|
return gtserror.NewErrorInternalError(err)
|
||||||
}
|
}
|
||||||
|
|
||||||
if blocked {
|
if blocked {
|
||||||
// Don't search for blocked domains.
|
// Don't search for
|
||||||
// Caller should stop looking.
|
// blocked domains.
|
||||||
return false, nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
if includeAccounts(queryType) {
|
if includeAccounts(queryType) {
|
||||||
|
@ -485,14 +493,13 @@ func (p *Processor) byURI(
|
||||||
// On one of these, we can continue.
|
// On one of these, we can continue.
|
||||||
if !gtserror.Unretrievable(err) && !gtserror.WrongType(err) {
|
if !gtserror.Unretrievable(err) && !gtserror.WrongType(err) {
|
||||||
err = gtserror.Newf("error looking up %s as account: %w", uri, err)
|
err = gtserror.Newf("error looking up %s as account: %w", uri, err)
|
||||||
return false, gtserror.NewErrorInternalError(err)
|
return gtserror.NewErrorInternalError(err)
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
// Hit; return false to indicate caller should
|
// Hit! Return early since it's extremely unlikely
|
||||||
// stop looking, since it's extremely unlikely
|
|
||||||
// a status and an account will have the same URL.
|
// a status and an account will have the same URL.
|
||||||
appendAccount(foundAccount)
|
appendAccount(foundAccount)
|
||||||
return false, nil
|
return nil
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -504,20 +511,19 @@ func (p *Processor) byURI(
|
||||||
// On one of these, we can continue.
|
// On one of these, we can continue.
|
||||||
if !gtserror.Unretrievable(err) && !gtserror.WrongType(err) {
|
if !gtserror.Unretrievable(err) && !gtserror.WrongType(err) {
|
||||||
err = gtserror.Newf("error looking up %s as status: %w", uri, err)
|
err = gtserror.Newf("error looking up %s as status: %w", uri, err)
|
||||||
return false, gtserror.NewErrorInternalError(err)
|
return gtserror.NewErrorInternalError(err)
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
// Hit; return false to indicate caller should
|
// Hit! Return early since it's extremely unlikely
|
||||||
// stop looking, since it's extremely unlikely
|
|
||||||
// a status and an account will have the same URL.
|
// a status and an account will have the same URL.
|
||||||
appendStatus(foundStatus)
|
appendStatus(foundStatus)
|
||||||
return false, nil
|
return nil
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// No errors, but no hits either; since this
|
// No errors, but no hits
|
||||||
// was a URI, caller should stop looking.
|
// either; that's fine.
|
||||||
return false, nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// accountByURI looks for one account with the given URI.
|
// accountByURI looks for one account with the given URI.
|
||||||
|
|
|
@ -51,6 +51,11 @@ func (p *Processor) Lookup(
|
||||||
// accident.
|
// accident.
|
||||||
const includeInstanceAccounts = true
|
const includeInstanceAccounts = true
|
||||||
|
|
||||||
|
// Since lookup is always for a specific
|
||||||
|
// account, it's fine to include a blocked
|
||||||
|
// account in the results.
|
||||||
|
const includeBlockedAccounts = true
|
||||||
|
|
||||||
// Validate query.
|
// Validate query.
|
||||||
query = strings.TrimSpace(query)
|
query = strings.TrimSpace(query)
|
||||||
if query == "" {
|
if query == "" {
|
||||||
|
@ -108,6 +113,7 @@ func (p *Processor) Lookup(
|
||||||
requestingAccount,
|
requestingAccount,
|
||||||
[]*gtsmodel.Account{account},
|
[]*gtsmodel.Account{account},
|
||||||
includeInstanceAccounts,
|
includeInstanceAccounts,
|
||||||
|
includeBlockedAccounts,
|
||||||
)
|
)
|
||||||
if errWithCode != nil {
|
if errWithCode != nil {
|
||||||
return nil, errWithCode
|
return nil, errWithCode
|
||||||
|
|
|
@ -49,6 +49,7 @@ func (p *Processor) packageAccounts(
|
||||||
requestingAccount *gtsmodel.Account,
|
requestingAccount *gtsmodel.Account,
|
||||||
accounts []*gtsmodel.Account,
|
accounts []*gtsmodel.Account,
|
||||||
includeInstanceAccounts bool,
|
includeInstanceAccounts bool,
|
||||||
|
includeBlockedAccounts bool,
|
||||||
) ([]*apimodel.Account, gtserror.WithCode) {
|
) ([]*apimodel.Account, gtserror.WithCode) {
|
||||||
apiAccounts := make([]*apimodel.Account, 0, len(accounts))
|
apiAccounts := make([]*apimodel.Account, 0, len(accounts))
|
||||||
|
|
||||||
|
@ -58,19 +59,26 @@ func (p *Processor) packageAccounts(
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
|
||||||
// Ensure requester can see result account.
|
// Check if block exists between searcher and searchee.
|
||||||
visible, err := p.filter.AccountVisible(ctx, requestingAccount, account)
|
blocked, err := p.state.DB.IsEitherBlocked(ctx, requestingAccount.ID, account.ID)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
err = gtserror.Newf("error checking visibility of account %s for account %s: %w", account.ID, requestingAccount.ID, err)
|
err = gtserror.Newf("error checking block between searching account %s and searched account %s: %w", requestingAccount.ID, account.ID, err)
|
||||||
return nil, gtserror.NewErrorInternalError(err)
|
return nil, gtserror.NewErrorInternalError(err)
|
||||||
}
|
}
|
||||||
|
|
||||||
if !visible {
|
if blocked && !includeBlockedAccounts {
|
||||||
log.Debugf(ctx, "account %s is not visible to account %s, skipping this result", account.ID, requestingAccount.ID)
|
// Don't include
|
||||||
|
// this result.
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
|
||||||
apiAccount, err := p.converter.AccountToAPIAccountPublic(ctx, account)
|
var apiAccount *apimodel.Account
|
||||||
|
if blocked {
|
||||||
|
apiAccount, err = p.converter.AccountToAPIAccountBlocked(ctx, account)
|
||||||
|
} else {
|
||||||
|
apiAccount, err = p.converter.AccountToAPIAccountPublic(ctx, account)
|
||||||
|
}
|
||||||
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Debugf(ctx, "skipping account %s because it couldn't be converted to its api representation: %s", account.ID, err)
|
log.Debugf(ctx, "skipping account %s because it couldn't be converted to its api representation: %s", account.ID, err)
|
||||||
continue
|
continue
|
||||||
|
@ -171,8 +179,15 @@ func (p *Processor) packageSearchResult(
|
||||||
tags []*gtsmodel.Tag,
|
tags []*gtsmodel.Tag,
|
||||||
v1 bool,
|
v1 bool,
|
||||||
includeInstanceAccounts bool,
|
includeInstanceAccounts bool,
|
||||||
|
includeBlockedAccounts bool,
|
||||||
) (*apimodel.SearchResult, gtserror.WithCode) {
|
) (*apimodel.SearchResult, gtserror.WithCode) {
|
||||||
apiAccounts, errWithCode := p.packageAccounts(ctx, requestingAccount, accounts, includeInstanceAccounts)
|
apiAccounts, errWithCode := p.packageAccounts(
|
||||||
|
ctx,
|
||||||
|
requestingAccount,
|
||||||
|
accounts,
|
||||||
|
includeInstanceAccounts,
|
||||||
|
includeBlockedAccounts,
|
||||||
|
)
|
||||||
if errWithCode != nil {
|
if errWithCode != nil {
|
||||||
return nil, errWithCode
|
return nil, errWithCode
|
||||||
}
|
}
|
||||||
|
|
|
@ -277,7 +277,7 @@ func (c *Converter) AccountToAPIAccountBlocked(ctx context.Context, a *gtsmodel.
|
||||||
// de-punify it just in case.
|
// de-punify it just in case.
|
||||||
d, err := util.DePunify(a.Domain)
|
d, err := util.DePunify(a.Domain)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, fmt.Errorf("AccountToAPIAccountBlocked: error de-punifying domain %s for account id %s: %w", a.Domain, a.ID, err)
|
return nil, gtserror.Newf("error de-punifying domain %s for account id %s: %w", a.Domain, a.ID, err)
|
||||||
}
|
}
|
||||||
|
|
||||||
acct = a.Username + "@" + d
|
acct = a.Username + "@" + d
|
||||||
|
@ -288,7 +288,7 @@ func (c *Converter) AccountToAPIAccountBlocked(ctx context.Context, a *gtsmodel.
|
||||||
if !a.IsInstance() {
|
if !a.IsInstance() {
|
||||||
user, err := c.state.DB.GetUserByAccountID(ctx, a.ID)
|
user, err := c.state.DB.GetUserByAccountID(ctx, a.ID)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, fmt.Errorf("AccountToAPIAccountPublic: error getting user from database for account id %s: %w", a.ID, err)
|
return nil, gtserror.Newf("error getting user from database for account id %s: %w", a.ID, err)
|
||||||
}
|
}
|
||||||
|
|
||||||
switch {
|
switch {
|
||||||
|
@ -304,17 +304,25 @@ func (c *Converter) AccountToAPIAccountBlocked(ctx context.Context, a *gtsmodel.
|
||||||
acct = a.Username // omit domain
|
acct = a.Username // omit domain
|
||||||
}
|
}
|
||||||
|
|
||||||
return &apimodel.Account{
|
account := &apimodel.Account{
|
||||||
ID: a.ID,
|
ID: a.ID,
|
||||||
Username: a.Username,
|
Username: a.Username,
|
||||||
Acct: acct,
|
Acct: acct,
|
||||||
DisplayName: a.DisplayName,
|
|
||||||
Bot: *a.Bot,
|
Bot: *a.Bot,
|
||||||
CreatedAt: util.FormatISO8601(a.CreatedAt),
|
CreatedAt: util.FormatISO8601(a.CreatedAt),
|
||||||
URL: a.URL,
|
URL: a.URL,
|
||||||
Suspended: !a.SuspendedAt.IsZero(),
|
Suspended: !a.SuspendedAt.IsZero(),
|
||||||
Role: role,
|
Role: role,
|
||||||
}, nil
|
}
|
||||||
|
|
||||||
|
// Don't show the account's actual
|
||||||
|
// avatar+header since it may be
|
||||||
|
// upsetting to the blocker. Just
|
||||||
|
// show generic avatar+header instead.
|
||||||
|
c.ensureAvatar(account)
|
||||||
|
c.ensureHeader(account)
|
||||||
|
|
||||||
|
return account, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *Converter) AccountToAdminAPIAccount(ctx context.Context, a *gtsmodel.Account) (*apimodel.AdminAccountInfo, error) {
|
func (c *Converter) AccountToAdminAPIAccount(ctx context.Context, a *gtsmodel.Account) (*apimodel.AdminAccountInfo, error) {
|
||||||
|
|
|
@ -313,8 +313,8 @@ func (suite *InternalToFrontendTestSuite) TestLocalInstanceAccountToFrontendBloc
|
||||||
"url": "http://localhost:8080/@localhost:8080",
|
"url": "http://localhost:8080/@localhost:8080",
|
||||||
"avatar": "",
|
"avatar": "",
|
||||||
"avatar_static": "",
|
"avatar_static": "",
|
||||||
"header": "",
|
"header": "http://localhost:8080/assets/default_header.png",
|
||||||
"header_static": "",
|
"header_static": "http://localhost:8080/assets/default_header.png",
|
||||||
"followers_count": 0,
|
"followers_count": 0,
|
||||||
"following_count": 0,
|
"following_count": 0,
|
||||||
"statuses_count": 0,
|
"statuses_count": 0,
|
||||||
|
|
Loading…
Reference in New Issue