start refactoring return codes from fedi endpoints, remove some cruft

This commit is contained in:
tobi 2025-10-08 13:14:06 +02:00
commit 47051a26d6
28 changed files with 346 additions and 291 deletions

View file

@ -54,24 +54,24 @@ func (p *Processor) OutboxGet(
ctx context.Context,
requestedUser string,
page *paging.Page,
) (interface{}, gtserror.WithCode) {
) (any, gtserror.WithCode) {
// Authenticate incoming request, getting related accounts.
auth, errWithCode := p.authenticate(ctx, requestedUser)
if errWithCode != nil {
return nil, errWithCode
}
receivingAcct := auth.receivingAcct
receiver := auth.receiver
// Parse the collection ID object from account's followers URI.
collectionID, err := url.Parse(receivingAcct.OutboxURI)
collectionID, err := url.Parse(receiver.OutboxURI)
if err != nil {
err := gtserror.Newf("error parsing account outbox uri %s: %w", receivingAcct.OutboxURI, err)
err := gtserror.Newf("error parsing account outbox uri %s: %w", receiver.OutboxURI, err)
return nil, gtserror.NewErrorInternalError(err)
}
// Ensure we have stats for this account.
if err := p.state.DB.PopulateAccountStats(ctx, receivingAcct); err != nil {
err := gtserror.Newf("error getting stats for account %s: %w", receivingAcct.ID, err)
if err := p.state.DB.PopulateAccountStats(ctx, receiver); err != nil {
err := gtserror.Newf("error getting stats for account %s: %w", receiver.ID, err)
return nil, gtserror.NewErrorInternalError(err)
}
@ -83,8 +83,8 @@ func (p *Processor) OutboxGet(
switch {
case receivingAcct.IsInstance() ||
*receivingAcct.Settings.HideCollections:
case receiver.IsInstance() ||
*receiver.Settings.HideCollections:
// If account that hides collections, or instance
// account (ie., can't post / have relationships),
// just return barest stub of collection.
@ -94,7 +94,7 @@ func (p *Processor) OutboxGet(
// If paging disabled, or we're currently handshaking
// the requester, just return collection that links
// to first page (i.e. path below), with no items.
params.Total = util.Ptr(*receivingAcct.Stats.StatusesCount)
params.Total = util.Ptr(*receiver.Stats.StatusesCount)
params.First = new(paging.Page)
params.Query = make(url.Values, 1)
params.Query.Set("limit", "40") // enables paging
@ -105,7 +105,7 @@ func (p *Processor) OutboxGet(
// Get page of full public statuses.
statuses, err := p.state.DB.GetAccountStatuses(
ctx,
receivingAcct.ID,
receiver.ID,
page.GetLimit(), // limit
true, // excludeReplies
true, // excludeReblogs
@ -133,7 +133,7 @@ func (p *Processor) OutboxGet(
// (eg., local-only statuses, if the requester is remote).
statuses, err = p.visFilter.StatusesVisible(
ctx,
auth.requestingAcct,
auth.requester,
statuses,
)
if err != nil {
@ -142,7 +142,7 @@ func (p *Processor) OutboxGet(
}
// Start building AS collection page params.
params.Total = util.Ptr(*receivingAcct.Stats.StatusesCount)
params.Total = util.Ptr(*receiver.Stats.StatusesCount)
var pageParams ap.CollectionPageParams
pageParams.CollectionParams = params
@ -194,24 +194,24 @@ func (p *Processor) FollowersGet(
ctx context.Context,
requestedUser string,
page *paging.Page,
) (interface{}, gtserror.WithCode) {
) (any, gtserror.WithCode) {
// Authenticate incoming request, getting related accounts.
auth, errWithCode := p.authenticate(ctx, requestedUser)
if errWithCode != nil {
return nil, errWithCode
}
receivingAcct := auth.receivingAcct
receiver := auth.receiver
// Parse the collection ID object from account's followers URI.
collectionID, err := url.Parse(receivingAcct.FollowersURI)
collectionID, err := url.Parse(receiver.FollowersURI)
if err != nil {
err := gtserror.Newf("error parsing account followers uri %s: %w", receivingAcct.FollowersURI, err)
err := gtserror.Newf("error parsing account followers uri %s: %w", receiver.FollowersURI, err)
return nil, gtserror.NewErrorInternalError(err)
}
// Ensure we have stats for this account.
if err := p.state.DB.PopulateAccountStats(ctx, receivingAcct); err != nil {
err := gtserror.Newf("error getting stats for account %s: %w", receivingAcct.ID, err)
if err := p.state.DB.PopulateAccountStats(ctx, receiver); err != nil {
err := gtserror.Newf("error getting stats for account %s: %w", receiver.ID, err)
return nil, gtserror.NewErrorInternalError(err)
}
@ -223,8 +223,8 @@ func (p *Processor) FollowersGet(
switch {
case receivingAcct.IsInstance() ||
*receivingAcct.Settings.HideCollections:
case receiver.IsInstance() ||
*receiver.Settings.HideCollections:
// If account that hides collections, or instance
// account (ie., can't post / have relationships),
// just return barest stub of collection.
@ -234,7 +234,7 @@ func (p *Processor) FollowersGet(
// If paging disabled, or we're currently handshaking
// the requester, just return collection that links
// to first page (i.e. path below), with no items.
params.Total = util.Ptr(*receivingAcct.Stats.FollowersCount)
params.Total = util.Ptr(*receiver.Stats.FollowersCount)
params.First = new(paging.Page)
params.Query = make(url.Values, 1)
params.Query.Set("limit", "40") // enables paging
@ -243,7 +243,7 @@ func (p *Processor) FollowersGet(
default:
// Paging enabled.
// Get page of full follower objects with attached accounts.
followers, err := p.state.DB.GetAccountFollowers(ctx, receivingAcct.ID, page)
followers, err := p.state.DB.GetAccountFollowers(ctx, receiver.ID, page)
if err != nil {
err := gtserror.Newf("error getting followers: %w", err)
return nil, gtserror.NewErrorInternalError(err)
@ -260,7 +260,7 @@ func (p *Processor) FollowersGet(
}
// Start building AS collection page params.
params.Total = util.Ptr(*receivingAcct.Stats.FollowersCount)
params.Total = util.Ptr(*receiver.Stats.FollowersCount)
var pageParams ap.CollectionPageParams
pageParams.CollectionParams = params
@ -306,24 +306,24 @@ func (p *Processor) FollowersGet(
// FollowingGet returns the serialized ActivityPub
// collection of a local account's following collection,
// which contains links to accounts followed by this account.
func (p *Processor) FollowingGet(ctx context.Context, requestedUser string, page *paging.Page) (interface{}, gtserror.WithCode) {
func (p *Processor) FollowingGet(ctx context.Context, requestedUser string, page *paging.Page) (any, gtserror.WithCode) {
// Authenticate incoming request, getting related accounts.
auth, errWithCode := p.authenticate(ctx, requestedUser)
if errWithCode != nil {
return nil, errWithCode
}
receivingAcct := auth.receivingAcct
receiver := auth.receiver
// Parse collection ID from account's following URI.
collectionID, err := url.Parse(receivingAcct.FollowingURI)
collectionID, err := url.Parse(receiver.FollowingURI)
if err != nil {
err := gtserror.Newf("error parsing account following uri %s: %w", receivingAcct.FollowingURI, err)
err := gtserror.Newf("error parsing account following uri %s: %w", receiver.FollowingURI, err)
return nil, gtserror.NewErrorInternalError(err)
}
// Ensure we have stats for this account.
if err := p.state.DB.PopulateAccountStats(ctx, receivingAcct); err != nil {
err := gtserror.Newf("error getting stats for account %s: %w", receivingAcct.ID, err)
if err := p.state.DB.PopulateAccountStats(ctx, receiver); err != nil {
err := gtserror.Newf("error getting stats for account %s: %w", receiver.ID, err)
return nil, gtserror.NewErrorInternalError(err)
}
@ -334,8 +334,8 @@ func (p *Processor) FollowingGet(ctx context.Context, requestedUser string, page
params.ID = collectionID
switch {
case receivingAcct.IsInstance() ||
*receivingAcct.Settings.HideCollections:
case receiver.IsInstance() ||
*receiver.Settings.HideCollections:
// If account that hides collections, or instance
// account (ie., can't post / have relationships),
// just return barest stub of collection.
@ -345,7 +345,7 @@ func (p *Processor) FollowingGet(ctx context.Context, requestedUser string, page
// If paging disabled, or we're currently handshaking
// the requester, just return collection that links
// to first page (i.e. path below), with no items.
params.Total = util.Ptr(*receivingAcct.Stats.FollowingCount)
params.Total = util.Ptr(*receiver.Stats.FollowingCount)
params.First = new(paging.Page)
params.Query = make(url.Values, 1)
params.Query.Set("limit", "40") // enables paging
@ -354,7 +354,7 @@ func (p *Processor) FollowingGet(ctx context.Context, requestedUser string, page
default:
// Paging enabled.
// Get page of full follower objects with attached accounts.
follows, err := p.state.DB.GetAccountFollows(ctx, receivingAcct.ID, page)
follows, err := p.state.DB.GetAccountFollows(ctx, receiver.ID, page)
if err != nil {
err := gtserror.Newf("error getting follows: %w", err)
return nil, gtserror.NewErrorInternalError(err)
@ -371,7 +371,7 @@ func (p *Processor) FollowingGet(ctx context.Context, requestedUser string, page
}
// Start AS collection page params.
params.Total = util.Ptr(*receivingAcct.Stats.FollowingCount)
params.Total = util.Ptr(*receiver.Stats.FollowingCount)
var pageParams ap.CollectionPageParams
pageParams.CollectionParams = params
@ -416,28 +416,29 @@ func (p *Processor) FollowingGet(ctx context.Context, requestedUser string, page
// FeaturedCollectionGet returns an ordered collection of the requested username's Pinned posts.
// The returned collection have an `items` property which contains an ordered list of status URIs.
func (p *Processor) FeaturedCollectionGet(ctx context.Context, requestedUser string) (interface{}, gtserror.WithCode) {
func (p *Processor) FeaturedCollectionGet(ctx context.Context, requestedUser string) (any, gtserror.WithCode) {
// Authenticate incoming request, getting related accounts.
auth, errWithCode := p.authenticate(ctx, requestedUser)
if errWithCode != nil {
return nil, errWithCode
}
receivingAcct := auth.receivingAcct
receiver := auth.receiver
statuses, err := p.state.DB.GetAccountPinnedStatuses(ctx, receivingAcct.ID)
if err != nil {
if !errors.Is(err, db.ErrNoEntries) {
return nil, gtserror.NewErrorInternalError(err)
}
statuses, err := p.state.DB.GetAccountPinnedStatuses(ctx, receiver.ID)
if err != nil && !errors.Is(err, db.ErrNoEntries) {
err := gtserror.Newf("db error getting pinned statuses: %w", err)
return nil, gtserror.NewErrorInternalError(err)
}
collection, err := p.converter.StatusesToASFeaturedCollection(ctx, receivingAcct.FeaturedCollectionURI, statuses)
collection, err := p.converter.StatusesToASFeaturedCollection(ctx, receiver.FeaturedCollectionURI, statuses)
if err != nil {
err := gtserror.Newf("error converting pinned statuses: %w", err)
return nil, gtserror.NewErrorInternalError(err)
}
data, err := ap.Serialize(collection)
if err != nil {
err := gtserror.Newf("error serializing: %w", err)
return nil, gtserror.NewErrorInternalError(err)
}

View file

@ -30,24 +30,11 @@ import (
type commonAuth struct {
handshakingURI *url.URL // Set to requestingAcct's URI if we're currently handshaking them.
requestingAcct *gtsmodel.Account // Remote account making request to this instance.
receivingAcct *gtsmodel.Account // Local account receiving the request.
requester *gtsmodel.Account // Remote account making request to this instance.
receiver *gtsmodel.Account // Local account receiving the request.
}
func (p *Processor) authenticate(ctx context.Context, requestedUser string) (*commonAuth, gtserror.WithCode) {
// First get the requested (receiving) LOCAL account with username from database.
receiver, err := p.state.DB.GetAccountByUsernameDomain(ctx, requestedUser, "")
if err != nil {
if !errors.Is(err, db.ErrNoEntries) {
// Real db error.
err = gtserror.Newf("db error getting account %s: %w", requestedUser, err)
return nil, gtserror.NewErrorInternalError(err)
}
// Account just not found in the db.
return nil, gtserror.NewErrorNotFound(err)
}
// Ensure request signed, and use signature URI to
// get requesting account, dereferencing if necessary.
pubKeyAuth, errWithCode := p.federator.AuthenticateFederatedRequest(ctx, requestedUser)
@ -55,31 +42,46 @@ func (p *Processor) authenticate(ctx context.Context, requestedUser string) (*co
return nil, errWithCode
}
// Get the requested local account
// with given username from database.
receiver, err := p.state.DB.GetAccountByUsernameDomain(ctx, requestedUser, "")
if err != nil && !errors.Is(err, db.ErrNoEntries) {
err = gtserror.Newf("db error getting account %s: %w", requestedUser, err)
return nil, gtserror.NewErrorInternalError(err)
}
if receiver == nil {
err := gtserror.Newf("account %s not found in the db", requestedUser)
return nil, gtserror.NewErrorNotFound(err)
}
if pubKeyAuth.Handshaking {
// We're still handshaking so we
// don't know the requester yet.
return &commonAuth{
handshakingURI: pubKeyAuth.OwnerURI,
receivingAcct: receiver,
receiver: receiver,
}, nil
}
// Get requester from auth.
requester := pubKeyAuth.Owner
// Ensure block does not exist between receiver and requester.
blocked, err := p.state.DB.IsEitherBlocked(ctx, receiver.ID, requester.ID)
// Ensure receiver does not block requester.
blocked, err := p.state.DB.IsBlocked(ctx, receiver.ID, requester.ID)
if err != nil {
err := gtserror.Newf("error checking block: %w", err)
err := gtserror.Newf("db error checking block: %w", err)
return nil, gtserror.NewErrorInternalError(err)
} else if blocked {
const text = "block exists between accounts"
}
if blocked {
var text = requestedUser + " blocks " + requester.Username
return nil, gtserror.NewErrorForbidden(errors.New(text))
}
return &commonAuth{
requestingAcct: requester,
receivingAcct: receiver,
requester: requester,
receiver: receiver,
}, nil
}
@ -120,7 +122,7 @@ func (p *Processor) validateIntReqRequest(
// Ensure interaction request was accepted
// by the account in the request path.
if req.TargetAccountID != auth.receivingAcct.ID {
if req.TargetAccountID != auth.receiver.ID {
text := fmt.Sprintf(
"account %s is not targeted by interaction request %s and therefore can't accept it",
requestedUser, intReqID,

View file

@ -19,38 +19,69 @@ package fedi
import (
"context"
"fmt"
"errors"
"code.superseriousbusiness.org/gotosocial/internal/ap"
"code.superseriousbusiness.org/gotosocial/internal/config"
"code.superseriousbusiness.org/gotosocial/internal/db"
"code.superseriousbusiness.org/gotosocial/internal/gtserror"
)
// EmojiGet handles the GET for a federated emoji originating from this instance.
func (p *Processor) EmojiGet(ctx context.Context, requestedEmojiID string) (interface{}, gtserror.WithCode) {
if _, errWithCode := p.federator.AuthenticateFederatedRequest(ctx, ""); errWithCode != nil {
// EmojiGet handles the GET for an emoji originating from this instance.
func (p *Processor) EmojiGet(ctx context.Context, emojiID string) (any, gtserror.WithCode) {
// Authenticate incoming request.
//
// Pass hostname string to this function to indicate
// it's the instance account being requested, as
// emojis are always owned by the instance account.
auth, errWithCode := p.authenticate(ctx, config.GetHost())
if errWithCode != nil {
return nil, errWithCode
}
requestedEmoji, err := p.state.DB.GetEmojiByID(ctx, requestedEmojiID)
if auth.handshakingURI != nil {
// We're currently handshaking, which means
// we don't know this account yet. This should
// be a very rare race condition.
err := gtserror.Newf("network race handshaking %s", auth.handshakingURI)
return nil, gtserror.NewErrorInternalError(err)
}
// Get the requested emoji.
emoji, err := p.state.DB.GetEmojiByID(ctx, emojiID)
if err != nil && !errors.Is(err, db.ErrNoEntries) {
err := gtserror.Newf("db error getting emoji %s: %w", emojiID, err)
return nil, gtserror.NewErrorNotFound(err)
}
if emoji == nil {
err := gtserror.Newf("emoji %s not found in the db", emojiID)
return nil, gtserror.NewErrorNotFound(err)
}
// Only serve *our*
// emojis on this path.
if !emoji.IsLocal() {
err := gtserror.Newf("emoji %s doesn't belong to this instance (domain is %s)", emojiID, emoji.Domain)
return nil, gtserror.NewErrorNotFound(err)
}
// Don't serve emojis that have
// been disabled by an admin.
if *emoji.Disabled {
err := gtserror.Newf("emoji with id %s has been disabled by an admin", emojiID)
return nil, gtserror.NewErrorNotFound(err)
}
apEmoji, err := p.converter.EmojiToAS(ctx, emoji)
if err != nil {
return nil, gtserror.NewErrorNotFound(fmt.Errorf("database error getting emoji with id %s: %s", requestedEmojiID, err))
}
if !requestedEmoji.IsLocal() {
return nil, gtserror.NewErrorNotFound(fmt.Errorf("emoji with id %s doesn't belong to this instance (domain %s)", requestedEmojiID, requestedEmoji.Domain))
}
if *requestedEmoji.Disabled {
return nil, gtserror.NewErrorNotFound(fmt.Errorf("emoji with id %s has been disabled", requestedEmojiID))
}
apEmoji, err := p.converter.EmojiToAS(ctx, requestedEmoji)
if err != nil {
return nil, gtserror.NewErrorInternalError(fmt.Errorf("error converting gtsmodel emoji with id %s to ap emoji: %s", requestedEmojiID, err))
err := gtserror.Newf("error converting emoji %s to ap: %s", emojiID, err)
return nil, gtserror.NewErrorInternalError(err)
}
data, err := ap.Serialize(apEmoji)
if err != nil {
err := gtserror.Newf("error serializing emoji %s: %w", emojiID, err)
return nil, gtserror.NewErrorInternalError(err)
}

View file

@ -26,6 +26,7 @@ import (
"code.superseriousbusiness.org/activity/streams/vocab"
"code.superseriousbusiness.org/gotosocial/internal/ap"
"code.superseriousbusiness.org/gotosocial/internal/db"
"code.superseriousbusiness.org/gotosocial/internal/gtserror"
"code.superseriousbusiness.org/gotosocial/internal/gtsmodel"
"code.superseriousbusiness.org/gotosocial/internal/log"
@ -33,9 +34,13 @@ import (
"code.superseriousbusiness.org/gotosocial/internal/util"
)
// StatusGet handles the getting of a fedi/activitypub representation of a local status.
// StatusGet handles getting an AP representation of a local status.
// It performs appropriate authentication before returning a JSON serializable interface.
func (p *Processor) StatusGet(ctx context.Context, requestedUser string, statusID string) (interface{}, gtserror.WithCode) {
func (p *Processor) StatusGet(
ctx context.Context,
requestedUser string,
statusID string,
) (any, gtserror.WithCode) {
// Authenticate incoming request, getting related accounts.
auth, errWithCode := p.authenticate(ctx, requestedUser)
if errWithCode != nil {
@ -49,16 +54,23 @@ func (p *Processor) StatusGet(ctx context.Context, requestedUser string, statusI
err := gtserror.Newf("network race handshaking %s", auth.handshakingURI)
return nil, gtserror.NewErrorInternalError(err)
}
receivingAcct := auth.receivingAcct
requestingAcct := auth.requestingAcct
receiver := auth.receiver
requester := auth.requester
status, err := p.state.DB.GetStatusByID(ctx, statusID)
if err != nil {
if err != nil && !errors.Is(err, db.ErrNoEntries) {
err := gtserror.Newf("db error getting status: %w", err)
return nil, gtserror.NewErrorInternalError(err)
}
if status == nil {
// TODO: Update this to serve "gone"
// when a status has been deleted.
err := gtserror.Newf("status %s not found in the db", statusID)
return nil, gtserror.NewErrorNotFound(err)
}
if status.AccountID != receivingAcct.ID {
if status.AccountID != receiver.ID {
const text = "status does not belong to receiving account"
return nil, gtserror.NewErrorNotFound(errors.New(text))
}
@ -68,7 +80,7 @@ func (p *Processor) StatusGet(ctx context.Context, requestedUser string, statusI
return nil, gtserror.NewErrorNotFound(errors.New(text))
}
visible, err := p.visFilter.StatusVisible(ctx, requestingAcct, status)
visible, err := p.visFilter.StatusVisible(ctx, requester, status)
if err != nil {
return nil, gtserror.NewErrorInternalError(err)
}
@ -93,7 +105,7 @@ func (p *Processor) StatusGet(ctx context.Context, requestedUser string, statusI
return data, nil
}
// GetStatus handles the getting of a fedi/activitypub representation of replies to a status,
// GetStatus handles getting an AP representation of replies to a status,
// performing appropriate authentication before returning a JSON serializable interface to the caller.
func (p *Processor) StatusRepliesGet(
ctx context.Context,
@ -101,7 +113,7 @@ func (p *Processor) StatusRepliesGet(
statusID string,
page *paging.Page,
onlyOtherAccounts bool,
) (interface{}, gtserror.WithCode) {
) (any, gtserror.WithCode) {
// Authenticate incoming request, getting related accounts.
auth, errWithCode := p.authenticate(ctx, requestedUser)
if errWithCode != nil {
@ -116,8 +128,8 @@ func (p *Processor) StatusRepliesGet(
return nil, gtserror.NewErrorInternalError(err)
}
receivingAcct := auth.receivingAcct
requestingAcct := auth.requestingAcct
receivingAcct := auth.receiver
requestingAcct := auth.requester
// Get target status and ensure visible to requester.
status, errWithCode := p.c.GetVisibleTargetStatus(ctx,

View file

@ -20,96 +20,83 @@ package fedi
import (
"context"
"errors"
"fmt"
"net/url"
"code.superseriousbusiness.org/gotosocial/internal/ap"
"code.superseriousbusiness.org/gotosocial/internal/db"
"code.superseriousbusiness.org/gotosocial/internal/gtscontext"
"code.superseriousbusiness.org/gotosocial/internal/gtserror"
"code.superseriousbusiness.org/gotosocial/internal/uris"
)
// UserGet handles the getting of a fedi/activitypub representation of a user/account,
// performing authentication before returning a JSON serializable interface to the caller.
func (p *Processor) UserGet(ctx context.Context, requestedUsername string, requestURL *url.URL) (interface{}, gtserror.WithCode) {
// (Try to) get the requested local account from the db.
receiver, err := p.state.DB.GetAccountByUsernameDomain(ctx, requestedUsername, "")
if err != nil {
if errors.Is(err, db.ErrNoEntries) {
// Account just not found w/ this username.
err := fmt.Errorf("account with username %s not found in the db", requestedUsername)
return nil, gtserror.NewErrorNotFound(err)
}
// Real db error.
err := fmt.Errorf("db error getting account with username %s: %w", requestedUsername, err)
return nil, gtserror.NewErrorInternalError(err)
}
if uris.IsPublicKeyPath(requestURL) {
// If request is on a public key path, we don't need to
// authenticate this request. However, we'll only serve
// the bare minimum user profile needed for the pubkey.
//
// TODO: https://codeberg.org/superseriousbusiness/gotosocial/issues/1186
minimalPerson, err := p.converter.AccountToASMinimal(ctx, receiver)
if err != nil {
err := gtserror.Newf("error converting to minimal account: %w", err)
return nil, gtserror.NewErrorInternalError(err)
}
// Return early with bare minimum data.
return data(minimalPerson)
}
// If the request is not on a public key path, we want to
// try to authenticate it before we serve any data, so that
// we can serve a more complete profile.
pubKeyAuth, errWithCode := p.federator.AuthenticateFederatedRequest(ctx, requestedUsername)
// UserGet handles getting an AP representation of an account.
// It does auth before returning a JSON serializable interface to the caller.
func (p *Processor) UserGet(
ctx context.Context,
requestedUser string,
) (any, gtserror.WithCode) {
// Authenticate incoming request, getting related accounts.
//
// We may currently be handshaking with the remote account
// making the request. Unlike with other fedi endpoints,
// if we're still handshaking then don't be coy: just serve
// the AP representation of the requested account anyway.
//
// This ensures that we don't get stuck in a loop with another
// GtS instance, where each instance is trying repeatedly to
// dereference the other account that's making the request
// before it will reveal its own account.
//
// Instead, we end up in an 'I'll show you mine if you show me
// yours' situation, where we sort of agree to reveal each
// other's profiles at the same time.
auth, errWithCode := p.authenticate(ctx, requestedUser)
if errWithCode != nil {
return nil, errWithCode // likely 401
return nil, errWithCode
}
// Auth passed, generate the proper AP representation.
accountable, err := p.converter.AccountToAS(ctx, receiver)
// Generate the proper AP representation.
accountable, err := p.converter.AccountToAS(ctx, auth.receiver)
if err != nil {
err := gtserror.Newf("error converting account: %w", err)
err := gtserror.Newf("error converting to accountable: %w", err)
return nil, gtserror.NewErrorInternalError(err)
}
data, err := ap.Serialize(accountable)
if err != nil {
err := gtserror.Newf("error serializing accountable: %w", err)
return nil, gtserror.NewErrorInternalError(err)
}
return data, nil
}
// UserGetMinimal returns a minimal AP representation
// of the requested account, containing just the public
// key, without doing authentication.
func (p *Processor) UserGetMinimal(
ctx context.Context,
requestedUser string,
) (any, gtserror.WithCode) {
acct, err := p.state.DB.GetAccountByUsernameDomain(
gtscontext.SetBarebones(ctx),
requestedUser, "",
)
if err != nil && !errors.Is(err, db.ErrNoEntries) {
err := gtserror.Newf("db error getting account %s: %w", requestedUser, err)
return nil, gtserror.NewErrorInternalError(err)
}
if acct == nil {
err := gtserror.Newf("account %s not found in the db", requestedUser)
return nil, gtserror.NewErrorNotFound(err)
}
// Generate minimal AP representation.
accountable, err := p.converter.AccountToASMinimal(ctx, acct)
if err != nil {
err := gtserror.Newf("error converting to accountable: %w", err)
return nil, gtserror.NewErrorInternalError(err)
}
if pubKeyAuth.Handshaking {
// If we are currently handshaking with the remote account
// making the request, then don't be coy: just serve the AP
// representation of the target account.
//
// This handshake check ensures that we don't get stuck in
// a loop with another GtS instance, where each instance is
// trying repeatedly to dereference the other account that's
// making the request before it will reveal its own account.
//
// Instead, we end up in an 'I'll show you mine if you show me
// yours' situation, where we sort of agree to reveal each
// other's profiles at the same time.
return data(accountable)
}
// Get requester from auth.
requester := pubKeyAuth.Owner
// Check that block does not exist between receiver and requester.
blocked, err := p.state.DB.IsBlocked(ctx, receiver.ID, requester.ID)
if err != nil {
err := gtserror.Newf("error checking block: %w", err)
return nil, gtserror.NewErrorInternalError(err)
} else if blocked {
const text = "block exists between accounts"
return nil, gtserror.NewErrorForbidden(errors.New(text))
}
return data(accountable)
}
func data(accountable ap.Accountable) (interface{}, gtserror.WithCode) {
data, err := ap.Serialize(accountable)
if err != nil {
err := gtserror.Newf("error serializing accountable: %w", err)

View file

@ -47,7 +47,7 @@ var (
nodeInfoProtocols = []string{"activitypub"}
nodeInfoInbound = []string{}
nodeInfoOutbound = []string{}
nodeInfoMetadata = make(map[string]interface{})
nodeInfoMetadata = make(map[string]any)
)
// NodeInfoRelGet returns a well known response giving the path to node info.