[bugfix] Narrow search scope for accounts starting with '@'; don't LOWER SQLite text searches (#2435)

This commit is contained in:
tobi
2023-12-10 14:15:41 +01:00
committed by GitHub
parent d60edf7ec6
commit 3f070a442a
4 changed files with 104 additions and 56 deletions

View File

@@ -74,13 +74,6 @@ func (p *Processor) Accounts(
return nil, gtserror.NewErrorBadRequest(err, err.Error())
}
// Be nice and normalize query by prepending '@'.
// This will make it easier for accountsByNamestring
// to pick this up as a valid namestring.
if query[0] != '@' {
query = "@" + query
}
log.
WithContext(ctx).
WithFields(kv.Fields{
@@ -107,9 +100,7 @@ func (p *Processor) Accounts(
// See if we have something that looks like a namestring.
username, domain, err := util.ExtractNamestringParts(query)
if err != nil {
log.Warnf(ctx, "couldn't parse '%s' as namestring: %v", query, err)
} else {
if err == nil {
if domain != "" {
// Search was an exact namestring;
// we can safely assume caller is
@@ -121,7 +112,7 @@ func (p *Processor) Accounts(
// Get all accounts we can find
// that match the provided query.
if err := p.accountsByNamestring(
if err := p.accountsByUsernameDomain(
ctx,
requestingAccount,
id.Highest,
@@ -137,6 +128,23 @@ func (p *Processor) Accounts(
err = gtserror.Newf("error searching by namestring: %w", err)
return nil, gtserror.NewErrorInternalError(err)
}
} else {
// Query Doesn't look like a
// namestring, use text search.
if err := p.accountsByText(
ctx,
requestingAccount.ID,
id.Highest,
id.Lowest,
limit,
offset,
query,
following,
appendAccount,
); err != nil && !errors.Is(err, db.ErrNoEntries) {
err = gtserror.Newf("error searching by text: %w", err)
return nil, gtserror.NewErrorInternalError(err)
}
}
// Return whatever we got (if anything).

View File

@@ -165,13 +165,15 @@ func (p *Processor) Get(
// 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!
// that account to the caller even if it's an
// instance account and/or even if they have
// it blocked. They might be looking for it
// to unblock it again!
domainSet := (domain != "")
includeInstanceAccounts = domainSet
includeBlockedAccounts = domainSet
err = p.accountsByNamestring(
err = p.accountsByUsernameDomain(
ctx,
account,
maxID,
@@ -189,24 +191,21 @@ func (p *Processor) Get(
return nil, gtserror.NewErrorInternalError(err)
}
// If domain was set, we know this is
// 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(
ctx,
account,
foundAccounts,
foundStatuses,
foundTags,
req.APIv1,
includeInstanceAccounts,
includeBlockedAccounts,
)
}
// Namestrings are a pretty unique format, so
// it's very unlikely that the caller was
// searching for anything except an account.
// As such, return early without falling
// through to broader search.
return p.packageSearchResult(
ctx,
account,
foundAccounts,
foundStatuses,
foundTags,
req.APIv1,
includeInstanceAccounts,
includeBlockedAccounts,
)
}
}
@@ -331,12 +330,12 @@ func (p *Processor) Get(
)
}
// accountsByNamestring searches for accounts using the
// provided username and domain. If domain is not set,
// accountsByUsernameDomain searches for accounts using
// the provided username and domain. If domain is not set,
// it may return more than one result by doing a text
// search in the database for accounts matching the query.
// Otherwise, it tries to return an exact match.
func (p *Processor) accountsByNamestring(
func (p *Processor) accountsByUsernameDomain(
ctx context.Context,
requestingAccount *gtsmodel.Account,
maxID string,
@@ -350,10 +349,10 @@ func (p *Processor) accountsByNamestring(
appendAccount func(*gtsmodel.Account),
) error {
if domain == "" {
// No error, but no domain set. That means the query
// looked like '@someone' which is not an exact search.
// Try to search for any accounts that match the query
// string, and let the caller know they should stop.
// No domain set. That means the query looked
// like '@someone' which is not an exact search,
// but is still a username search. Look for any
// usernames that start with the query string.
return p.accountsByText(
ctx,
requestingAccount.ID,
@@ -361,15 +360,16 @@ func (p *Processor) accountsByNamestring(
minID,
limit,
offset,
// OK to assume username is set now. Use
// it instead of query to omit leading '@'.
username,
// Add @ prefix back in to indicate
// to search function that we want
// an account by its username.
"@"+username,
following,
appendAccount,
)
}
// No error, and domain and username were both set.
// Domain and username were both set.
// Caller is likely trying to search for an exact
// match, from either a remote instance or local.
foundAccount, err := p.accountByUsernameDomain(