From e60158c70b831c0c45aa147c3571d8a6a870e27c Mon Sep 17 00:00:00 2001 From: Anthony Wang Date: Tue, 14 Jun 2022 12:01:41 -0500 Subject: [PATCH] Make sure API responses always refer to username in original case Copied from what I wrote on #19133 discussion: Handling username case is a very tricky issue and I've already encountered a Mastodon <-> Gitea federation bug due to Gitea considering Ta180m and ta180m to be the same user while Mastodon thinks they are two different users. I think the best way forward is for Gitea to only use the original case version of the username for federation so other AP software don't get confused. --- integrations/webfinger_test.go | 2 +- routers/api/v1/activitypub/person.go | 24 ++++++++-------------- routers/api/v1/activitypub/reqsignature.go | 7 +++---- routers/api/v1/api.go | 2 +- routers/web/webfinger.go | 4 ++-- 5 files changed, 16 insertions(+), 23 deletions(-) diff --git a/integrations/webfinger_test.go b/integrations/webfinger_test.go index 98f12bee11..07bf58b509 100644 --- a/integrations/webfinger_test.go +++ b/integrations/webfinger_test.go @@ -52,7 +52,7 @@ func TestWebfinger(t *testing.T) { var jrd webfingerJRD DecodeJSON(t, resp, &jrd) assert.Equal(t, "acct:user2@"+appURL.Host, jrd.Subject) - assert.ElementsMatch(t, []string{user.HTMLURL(), appURL.String() + "api/v1/activitypub/user/" + user.Name}, jrd.Aliases) + assert.ElementsMatch(t, []string{user.HTMLURL(), appURL.String() + "api/v1/activitypub/user/" + url.PathEscape(user.Name)}, jrd.Aliases) req = NewRequest(t, "GET", fmt.Sprintf("/.well-known/webfinger?resource=acct:%s@%s", user.LowerName, "unknown.host")) MakeRequest(t, req, http.StatusBadRequest) diff --git a/routers/api/v1/activitypub/person.go b/routers/api/v1/activitypub/person.go index ce7f0eecdf..9b18dc118b 100644 --- a/routers/api/v1/activitypub/person.go +++ b/routers/api/v1/activitypub/person.go @@ -6,14 +6,12 @@ package activitypub import ( "net/http" - "strings" "code.gitea.io/gitea/modules/activitypub" "code.gitea.io/gitea/modules/context" "code.gitea.io/gitea/modules/json" "code.gitea.io/gitea/modules/log" "code.gitea.io/gitea/modules/setting" - "code.gitea.io/gitea/routers/api/v1/user" ap "github.com/go-ap/activitypub" ) @@ -35,35 +33,29 @@ func Person(ctx *context.APIContext) { // "200": // "$ref": "#/responses/ActivityPub" - user := user.GetUserByParamsName(ctx, "username") - if user == nil { - return - } - username := ctx.Params("username") - - link := strings.TrimSuffix(setting.AppURL, "/") + strings.TrimSuffix(ctx.Req.URL.EscapedPath(), "/") + link := setting.AppURL + "api/v1/activitypub/user/" + ctx.ContextUser.Name person := ap.PersonNew(ap.IRI(link)) person.Name = ap.NaturalLanguageValuesNew() - err := person.Name.Set("en", ap.Content(user.FullName)) + err := person.Name.Set("en", ap.Content(ctx.ContextUser.FullName)) if err != nil { ctx.Error(http.StatusInternalServerError, "Set Name", err) return } person.PreferredUsername = ap.NaturalLanguageValuesNew() - err = person.PreferredUsername.Set("en", ap.Content(username)) + err = person.PreferredUsername.Set("en", ap.Content(ctx.ContextUser.Name)) if err != nil { ctx.Error(http.StatusInternalServerError, "Set PreferredUsername", err) return } - person.URL = ap.IRI(setting.AppURL + username) + person.URL = ap.IRI(ctx.ContextUser.HTMLURL()) person.Icon = ap.Image{ Type: ap.ImageType, MediaType: "image/png", - URL: ap.IRI(user.AvatarLink()), + URL: ap.IRI(ctx.ContextUser.AvatarLink()), } person.Inbox = nil @@ -74,7 +66,7 @@ func Person(ctx *context.APIContext) { person.PublicKey.ID = ap.IRI(link + "#main-key") person.PublicKey.Owner = ap.IRI(link) - publicKeyPem, err := activitypub.GetPublicKey(user) + publicKeyPem, err := activitypub.GetPublicKey(ctx.ContextUser) if err != nil { ctx.Error(http.StatusInternalServerError, "GetPublicKey", err) return @@ -84,12 +76,14 @@ func Person(ctx *context.APIContext) { binary, err := person.MarshalJSON() if err != nil { ctx.Error(http.StatusInternalServerError, "Serialize", err) + return } var jsonmap map[string]interface{} err = json.Unmarshal(binary, &jsonmap) if err != nil { - ctx.Error(http.StatusInternalServerError, "Unmarshall", err) + ctx.Error(http.StatusInternalServerError, "Unmarshal", err) + return } jsonmap["@context"] = []string{"https://www.w3.org/ns/activitystreams", "https://w3id.org/security/v1"} diff --git a/routers/api/v1/activitypub/reqsignature.go b/routers/api/v1/activitypub/reqsignature.go index ffc69aa8de..f080f4e201 100644 --- a/routers/api/v1/activitypub/reqsignature.go +++ b/routers/api/v1/activitypub/reqsignature.go @@ -5,7 +5,6 @@ package activitypub import ( - "context" "crypto" "crypto/x509" "encoding/pem" @@ -25,7 +24,7 @@ import ( "github.com/go-fed/httpsig" ) -func getPublicKeyFromResponse(ctx context.Context, b []byte, keyID *url.URL) (p crypto.PublicKey, err error) { +func getPublicKeyFromResponse(b []byte, keyID *url.URL) (p crypto.PublicKey, err error) { person := ap.PersonNew(ap.IRI(keyID.String())) err = person.UnmarshalJSON(b) if err != nil { @@ -34,7 +33,7 @@ func getPublicKeyFromResponse(ctx context.Context, b []byte, keyID *url.URL) (p } pubKey := person.PublicKey if pubKey.ID.String() != keyID.String() { - err = fmt.Errorf("cannot find publicKey with id: %s in %s", keyID, b) + err = fmt.Errorf("cannot find publicKey with id: %s in %s", keyID, string(b)) return } pubKeyPem := pubKey.PublicKeyPem @@ -84,7 +83,7 @@ func verifyHTTPSignatures(ctx *gitea_context.APIContext) (authenticated bool, er if err != nil { return } - pubKey, err := getPublicKeyFromResponse(*ctx, b, idIRI) + pubKey, err := getPublicKeyFromResponse(b, idIRI) if err != nil { return } diff --git a/routers/api/v1/api.go b/routers/api/v1/api.go index fb1b0453e2..cecf6513cb 100644 --- a/routers/api/v1/api.go +++ b/routers/api/v1/api.go @@ -648,7 +648,7 @@ func Routes() *web.Route { m.Group("/user/{username}", func() { m.Get("", activitypub.Person) m.Post("/inbox", activitypub.ReqSignature(), activitypub.PersonInbox) - }) + }, context_service.UserAssignmentAPI()) }) } m.Get("/signing-key.gpg", misc.SigningKey) diff --git a/routers/web/webfinger.go b/routers/web/webfinger.go index 92f6f3106a..b52c1879a9 100644 --- a/routers/web/webfinger.go +++ b/routers/web/webfinger.go @@ -87,7 +87,7 @@ func WebfingerQuery(ctx *context.Context) { aliases := []string{ u.HTMLURL(), - appURL.String() + "api/v1/activitypub/user/" + strings.ToLower(u.Name), + appURL.String() + "api/v1/activitypub/user/" + url.PathEscape(u.Name), } if !u.KeepEmailPrivate { aliases = append(aliases, fmt.Sprintf("mailto:%s", u.Email)) @@ -106,7 +106,7 @@ func WebfingerQuery(ctx *context.Context) { { Rel: "self", Type: "application/activity+json", - Href: appURL.String() + "api/v1/activitypub/user/" + strings.ToLower(u.Name), + Href: appURL.String() + "api/v1/activitypub/user/" + url.PathEscape(u.Name), }, { Rel: "http://ostatus.org/schema/1.0/subscribe",