fiddle with it! (that's what she said)

This commit is contained in:
tobi 2025-04-06 12:02:26 +02:00
commit 1bfc1aa7d1
10 changed files with 218 additions and 199 deletions

View file

@ -89,14 +89,30 @@ func accountFresh(
return !time.Now().After(staleAt)
}
// GetAccountByURI will attempt to fetch an accounts by its URI, first checking the database. In the case of a newly-met remote model, or a remote model
// whose last_fetched date is beyond a certain interval, the account will be dereferenced. In the case of dereferencing, some low-priority account information
// may be enqueued for asynchronous fetching, e.g. featured account statuses (pins). An ActivityPub object indicates the account was dereferenced.
func (d *Dereferencer) GetAccountByURI(ctx context.Context, requestUser string, uri *url.URL) (*gtsmodel.Account, ap.Accountable, error) {
// GetAccountByURI will attempt to fetch an accounts by its
// URI, first checking the database. In the case of a newly-met
// remote model, or a remote model whose last_fetched date is
// beyond a certain interval, the account will be dereferenced.
// In the case of dereferencing, some low-priority account info
// may be enqueued for asynchronous fetching, e.g. pinned statuses.
// An ActivityPub object indicates the account was dereferenced.
//
// if tryURL is true, then the database will also check for a *single*
// account where uri == account.url, not just uri == account.uri.
// Because url does not guarantee uniqueness, you should only set
// tryURL to true when doing searches set in motion by a user,
// ie., when it's not important that an exact account is returned.
func (d *Dereferencer) GetAccountByURI(
ctx context.Context,
requestUser string,
uri *url.URL,
tryURL bool,
) (*gtsmodel.Account, ap.Accountable, error) {
// Fetch and dereference account if necessary.
account, accountable, err := d.getAccountByURI(ctx,
requestUser,
uri,
tryURL,
)
if err != nil {
return nil, nil, err
@ -118,8 +134,15 @@ func (d *Dereferencer) GetAccountByURI(ctx context.Context, requestUser string,
return account, accountable, nil
}
// getAccountByURI is a package internal form of .GetAccountByURI() that doesn't bother dereferencing featured posts on update.
func (d *Dereferencer) getAccountByURI(ctx context.Context, requestUser string, uri *url.URL) (*gtsmodel.Account, ap.Accountable, error) {
// getAccountByURI is a package internal form of
// .GetAccountByURI() that doesn't bother dereferencing
// featured posts on update.
func (d *Dereferencer) getAccountByURI(
ctx context.Context,
requestUser string,
uri *url.URL,
tryURL bool,
) (*gtsmodel.Account, ap.Accountable, error) {
var (
account *gtsmodel.Account
uriStr = uri.String()
@ -127,9 +150,8 @@ func (d *Dereferencer) getAccountByURI(ctx context.Context, requestUser string,
)
// Search the database for existing account with URI.
// URI is unique so if we get a hit it's that account for sure.
account, err = d.state.DB.GetAccountByURI(
// request a barebones object, it may be in the
// db but with related models not yet dereferenced.
gtscontext.SetBarebones(ctx),
uriStr,
)
@ -137,9 +159,11 @@ func (d *Dereferencer) getAccountByURI(ctx context.Context, requestUser string,
return nil, nil, gtserror.Newf("error checking database for account %s by uri: %w", uriStr, err)
}
if account == nil {
// Else, search the database for one account with URL.
// Can return multiple hits so check for ErrMultipleEntries.
if account == nil && tryURL {
// Else if we're permitted, search the database for *ONE*
// account with this URL. This can return multiple hits
// so check for ErrMultipleEntries. If we get exactly one
// hit it's *probably* the account we're looking for.
account, err = d.state.DB.GetOneAccountByURL(
gtscontext.SetBarebones(ctx),
uriStr,

View file

@ -54,6 +54,7 @@ func (suite *AccountTestSuite) TestDereferenceGroup() {
context.Background(),
fetchingAccount.Username,
groupURL,
false,
)
suite.NoError(err)
suite.NotNil(group)
@ -78,6 +79,7 @@ func (suite *AccountTestSuite) TestDereferenceService() {
context.Background(),
fetchingAccount.Username,
serviceURL,
false,
)
suite.NoError(err)
suite.NotNil(service)
@ -110,6 +112,7 @@ func (suite *AccountTestSuite) TestDereferenceLocalAccountAsRemoteURL() {
context.Background(),
fetchingAccount.Username,
testrig.URLMustParse(targetAccount.URI),
false,
)
suite.NoError(err)
suite.NotNil(fetchedAccount)
@ -129,6 +132,7 @@ func (suite *AccountTestSuite) TestDereferenceLocalAccountAsRemoteURLNoSharedInb
context.Background(),
fetchingAccount.Username,
testrig.URLMustParse(targetAccount.URI),
false,
)
suite.NoError(err)
suite.NotNil(fetchedAccount)
@ -143,6 +147,7 @@ func (suite *AccountTestSuite) TestDereferenceLocalAccountAsUsername() {
context.Background(),
fetchingAccount.Username,
testrig.URLMustParse(targetAccount.URI),
false,
)
suite.NoError(err)
suite.NotNil(fetchedAccount)
@ -157,6 +162,7 @@ func (suite *AccountTestSuite) TestDereferenceLocalAccountAsUsernameDomain() {
context.Background(),
fetchingAccount.Username,
testrig.URLMustParse(targetAccount.URI),
false,
)
suite.NoError(err)
suite.NotNil(fetchedAccount)
@ -213,6 +219,7 @@ func (suite *AccountTestSuite) TestDereferenceLocalAccountWithUnknownUserURI() {
context.Background(),
fetchingAccount.Username,
testrig.URLMustParse("http://localhost:8080/users/thisaccountdoesnotexist"),
false,
)
suite.True(gtserror.IsUnretrievable(err))
suite.EqualError(err, db.ErrNoEntries.Error())
@ -265,7 +272,7 @@ func (suite *AccountTestSuite) TestDereferenceLocalAccountByRedirect() {
uri := testrig.URLMustParse("https://this-will-be-redirected.butts/")
// Try dereference the test URI, since it correctly redirects to us it should return our account.
account, accountable, err := suite.dereferencer.GetAccountByURI(ctx, fetchingAccount.Username, uri)
account, accountable, err := suite.dereferencer.GetAccountByURI(ctx, fetchingAccount.Username, uri, false)
suite.NoError(err)
suite.Nil(accountable)
suite.NotNil(account)
@ -318,7 +325,7 @@ func (suite *AccountTestSuite) TestDereferenceMasqueradingLocalAccount() {
)
// Try dereference the test URI, since it correctly redirects to us it should return our account.
account, accountable, err := suite.dereferencer.GetAccountByURI(ctx, fetchingAccount.Username, uri)
account, accountable, err := suite.dereferencer.GetAccountByURI(ctx, fetchingAccount.Username, uri, false)
suite.NotNil(err)
suite.Nil(account)
suite.Nil(accountable)
@ -341,6 +348,7 @@ func (suite *AccountTestSuite) TestDereferenceRemoteAccountWithNonMatchingURI()
context.Background(),
fetchingAccount.Username,
testrig.URLMustParse(remoteAltURI),
false,
)
suite.Equal(err.Error(), fmt.Sprintf("enrichAccount: account uri %s does not match %s", remoteURI, remoteAltURI))
suite.Nil(fetchedAccount)
@ -357,6 +365,7 @@ func (suite *AccountTestSuite) TestDereferenceRemoteAccountWithUnexpectedKeyChan
remoteAcc, _, err := suite.dereferencer.GetAccountByURI(ctx,
fetchingAcc.Username,
testrig.URLMustParse(remoteURI),
false,
)
suite.NoError(err)
suite.NotNil(remoteAcc)
@ -395,6 +404,7 @@ func (suite *AccountTestSuite) TestDereferenceRemoteAccountWithExpectedKeyChange
remoteAcc, _, err := suite.dereferencer.GetAccountByURI(ctx,
fetchingAcc.Username,
testrig.URLMustParse(remoteURI),
false,
)
suite.NoError(err)
suite.NotNil(remoteAcc)
@ -436,6 +446,7 @@ func (suite *AccountTestSuite) TestRefreshFederatedRemoteAccountWithKeyChange()
remoteAcc, _, err := suite.dereferencer.GetAccountByURI(ctx,
fetchingAcc.Username,
testrig.URLMustParse(remoteURI),
false,
)
suite.NoError(err)
suite.NotNil(remoteAcc)

View file

@ -454,7 +454,8 @@ func (d *Dereferencer) enrichStatus(
// Ensure we have the author account of the status dereferenced (+ up-to-date). If this is a new status
// (i.e. status.AccountID == "") then any error here is irrecoverable. status.AccountID must ALWAYS be set.
if _, _, err := d.getAccountByURI(ctx, requestUser, attributedTo); err != nil && status.AccountID == "" {
// We want the exact URI match here as well, not the imprecise URL match.
if _, _, err := d.getAccountByURI(ctx, requestUser, attributedTo, false); err != nil && status.AccountID == "" {
// Note that we specifically DO NOT wrap the error, instead collapsing it as string.
// Errors fetching an account do not necessarily relate to dereferencing the status.
@ -1312,35 +1313,17 @@ func (d *Dereferencer) getPopulatedMention(
bool, // True if mention already exists in the DB.
error,
) {
// Mentions can be created using Name or Href.
// Prefer Href (TargetAccountURI), fall back to Name.
if mention.TargetAccountURI != "" {
// Mentions can be created using `name` or `href`,
// and when mention was extracted, we ensured that
// one of either `name` or `href` was set.
//
// Prefer to deref the mention target using `name`,
// which should always be exact. and fall back to `href`,
// which *should* be the target's URI, but may be the URL
// depending on implementation.
if mention.NameString != "" {
// Look for existing mention with target account's URI, if so use this.
existingMention, ok := existing.GetMentionByTargetURI(mention.TargetAccountURI)
if ok && existingMention.ID != "" {
return existingMention, true, nil
}
// Ensure that mention account URI is parseable.
accountURI, err := url.Parse(mention.TargetAccountURI)
if err != nil {
err := gtserror.Newf("invalid account uri %q: %w", mention.TargetAccountURI, err)
return nil, false, err
}
// Ensure we have account of the mention target dereferenced.
mention.TargetAccount, _, err = d.getAccountByURI(ctx,
requestUser,
accountURI,
)
if err != nil {
err := gtserror.Newf("failed to dereference account %s: %w", accountURI, err)
return nil, false, err
}
} else {
// Href wasn't set, extract the username and domain parts from namestring.
// Extract the username and domain parts from namestring.
username, domain, err := util.ExtractNamestringParts(mention.NameString)
if err != nil {
err := gtserror.Newf("failed to parse namestring %s: %w", mention.NameString, err)
@ -1369,6 +1352,34 @@ func (d *Dereferencer) getPopulatedMention(
if ok && existingMention.ID != "" {
return existingMention, true, nil
}
} else {
// Name wasn't set.
//
// Look for existing mention with target account's URI, if so use this.
existingMention, ok := existing.GetMentionByTargetURI(mention.TargetAccountURI)
if ok && existingMention.ID != "" {
return existingMention, true, nil
}
// Ensure that mention account URI is parseable.
accountURI, err := url.Parse(mention.TargetAccountURI)
if err != nil {
err := gtserror.Newf("invalid account uri %q: %w", mention.TargetAccountURI, err)
return nil, false, err
}
// Ensure we have account of the mention target dereferenced.
// Don't allow imprecise URL here, we want the exact acct.
mention.TargetAccount, _, err = d.getAccountByURI(ctx,
requestUser,
accountURI,
false,
)
if err != nil {
err := gtserror.Newf("failed to dereference account %s: %w", accountURI, err)
return nil, false, err
}
}
// At this point, mention.TargetAccountURI