diff --git a/internal/ap/activitystreams.go b/internal/ap/activitystreams.go index a78b0b61d..d9a5780e9 100644 --- a/internal/ap/activitystreams.go +++ b/internal/ap/activitystreams.go @@ -77,6 +77,9 @@ // See https://www.w3.org/TR/activitystreams-vocabulary/#microsyntaxes // and https://www.w3.org/TR/activitystreams-vocabulary/#dfn-tag TagHashtag = "Hashtag" + + // PublicKey is the internal type name of a Public Key. + PublicKey = "PublicKey" ) // isActivity returns whether AS type name is of an Activity (NOT IntransitiveActivity). diff --git a/internal/ap/serialize.go b/internal/ap/serialize.go index b13ebb340..c3a873896 100644 --- a/internal/ap/serialize.go +++ b/internal/ap/serialize.go @@ -50,12 +50,31 @@ func Serialize(t vocab.Type) (m map[string]interface{}, e error) { return serializeStatusable(t, true) case IsActivityable(tn): return serializeActivityable(t, true) + case tn == PublicKey: + return serializePubkey(t) default: // No custom serializer necessary. return streams.Serialize(t) } } +// serializePubkey is a custom serializer for a bare PublicKey type. +// Unlike the standard streams.Serialize function, it includes the +// ActivityStreams JSON-LD namespace in the @context entry. +func serializePubkey(t vocab.Type) (map[string]interface{}, error) { + data, err := streams.Serialize(t) + if err != nil { + return nil, err + } + + data["@context"] = []string{ + "https://www.w3.org/ns/activitystreams", + "https://w3id.org/security/v1", + } + + return data, nil +} + // serializeWithOrderedItems is a custom serializer // for any type that has an `orderedItems` property. // Unlike the standard streams.Serialize function, diff --git a/internal/processing/fedi/user.go b/internal/processing/fedi/user.go index bf14554cf..afe5aeec5 100644 --- a/internal/processing/fedi/user.go +++ b/internal/processing/fedi/user.go @@ -48,22 +48,19 @@ func (p *Processor) UserGet(ctx context.Context, requestedUsername string, reque } 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://github.com/superseriousbusiness/gotosocial/issues/1186 - minimalPerson, err := p.converter.AccountToASMinimal(ctx, receiver) + // If request is on the public key path, we don't need to + // authenticate this request. Just serve the public key. + pubKey, err := p.converter.AccountToASPubKey(ctx, receiver) if err != nil { - err := gtserror.Newf("error converting to minimal account: %w", err) + err := gtserror.Newf("error converting to pubKey: %w", err) return nil, gtserror.NewErrorInternalError(err) } // Return early with bare minimum data. - return data(minimalPerson) + return data(pubKey) } - // If the request is not on a public key path, we want to + // If the request is not on the 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) @@ -110,8 +107,8 @@ func (p *Processor) UserGet(ctx context.Context, requestedUsername string, reque return data(person) } -func data(requestedPerson vocab.ActivityStreamsPerson) (interface{}, gtserror.WithCode) { - data, err := ap.Serialize(requestedPerson) +func data(t vocab.Type) (interface{}, gtserror.WithCode) { + data, err := ap.Serialize(t) if err != nil { err := gtserror.Newf("error serializing person: %w", err) return nil, gtserror.NewErrorInternalError(err) diff --git a/internal/typeutils/internaltoas.go b/internal/typeutils/internaltoas.go index a795541d0..627b7e270 100644 --- a/internal/typeutils/internaltoas.go +++ b/internal/typeutils/internaltoas.go @@ -360,34 +360,8 @@ func (c *Converter) AccountToAS(ctx context.Context, a *gtsmodel.Account) (vocab return person, nil } -// AccountToASMinimal converts a gts model account into an activity streams person, suitable for federation. -// -// The returned account will just have the Type, Username, PublicKey, and ID properties set. This is -// suitable for serving to requesters to whom we want to give as little information as possible because -// we don't trust them (yet). -func (c *Converter) AccountToASMinimal(ctx context.Context, a *gtsmodel.Account) (vocab.ActivityStreamsPerson, error) { - person := streams.NewActivityStreamsPerson() - - // id should be the activitypub URI of this user - // something like https://example.org/users/example_user - profileIDURI, err := url.Parse(a.URI) - if err != nil { - return nil, err - } - idProp := streams.NewJSONLDIdProperty() - idProp.SetIRI(profileIDURI) - person.SetJSONLDId(idProp) - - // preferredUsername - // Used for Webfinger lookup. Must be unique on the domain, and must correspond to a Webfinger acct: URI. - preferredUsernameProp := streams.NewActivityStreamsPreferredUsernameProperty() - preferredUsernameProp.SetXMLSchemaString(a.Username) - person.SetActivityStreamsPreferredUsername(preferredUsernameProp) - - // publicKey - // Required for signatures. - publicKeyProp := streams.NewW3IDSecurityV1PublicKeyProperty() - +// AccountToASPubKey returns the PublicKey representation for the given Account. +func (c *Converter) AccountToASPubKey(ctx context.Context, a *gtsmodel.Account) (vocab.W3IDSecurityV1PublicKey, error) { // create the public key publicKey := streams.NewW3IDSecurityV1PublicKey() @@ -401,8 +375,14 @@ func (c *Converter) AccountToASMinimal(ctx context.Context, a *gtsmodel.Account) publicKey.SetJSONLDId(publicKeyIDProp) // set owner for the public key + // owner id should be the activitypub URI of this user + // something like https://example.org/users/example_user + ownerURI, err := url.Parse(a.URI) + if err != nil { + return nil, err + } publicKeyOwnerProp := streams.NewW3IDSecurityV1OwnerProperty() - publicKeyOwnerProp.SetIRI(profileIDURI) + publicKeyOwnerProp.SetIRI(ownerURI) publicKey.SetW3IDSecurityV1Owner(publicKeyOwnerProp) // set the pem key itself @@ -418,13 +398,7 @@ func (c *Converter) AccountToASMinimal(ctx context.Context, a *gtsmodel.Account) publicKeyPEMProp.Set(string(publicKeyBytes)) publicKey.SetW3IDSecurityV1PublicKeyPem(publicKeyPEMProp) - // append the public key to the public key property - publicKeyProp.AppendW3IDSecurityV1PublicKey(publicKey) - - // set the public key property on the Person - person.SetW3IDSecurityV1PublicKey(publicKeyProp) - - return person, nil + return publicKey, nil } // StatusToAS converts a gts model status into an ActivityStreams Statusable implementation, suitable for federation