From 678a56fbf81f6568ba82b6236c534ce10ab18fea Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Lo=C3=AFc=20Dachary?= Date: Thu, 28 Oct 2021 17:13:24 -1000 Subject: [PATCH] activitypub: add the public key to Person (#14186) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Refs: https://github.com/go-gitea/gitea/issues/14186 Signed-off-by: Loïc Dachary --- integrations/api_activitypub_person_test.go | 39 ++++++++++++-- routers/api/v1/activitypub/person.go | 58 +++++++++++++++++++-- 2 files changed, 90 insertions(+), 7 deletions(-) diff --git a/integrations/api_activitypub_person_test.go b/integrations/api_activitypub_person_test.go index 2efa82f025..e031e886dc 100644 --- a/integrations/api_activitypub_person_test.go +++ b/integrations/api_activitypub_person_test.go @@ -13,6 +13,7 @@ import ( "testing" "code.gitea.io/gitea/modules/setting" + "github.com/go-fed/activity/pub" "github.com/go-fed/activity/streams" "github.com/go-fed/activity/streams/vocab" @@ -22,8 +23,10 @@ import ( func TestActivityPubPerson(t *testing.T) { onGiteaRun(t, func(*testing.T, *url.URL) { setting.Federation.Enabled = true + setting.Database.LogSQL = true defer func() { setting.Federation.Enabled = false + setting.Database.LogSQL = false }() username := "user2" @@ -41,11 +44,41 @@ func TestActivityPubPerson(t *testing.T) { ctx := context.Background() err := resolver.Resolve(ctx, m) assert.Equal(t, err, nil) - assert.Equal(t, person.GetTypeName(), "Person") - assert.Equal(t, person.GetActivityStreamsName().Begin().GetXMLSchemaString(), username) - assert.Regexp(t, fmt.Sprintf("activitypub/user/%s$", username), person.GetJSONLDId().GetIRI().String()) + assert.Equal(t, "Person", person.GetTypeName()) + assert.Equal(t, username, person.GetActivityStreamsName().Begin().GetXMLSchemaString()) + keyId := person.GetJSONLDId().GetIRI().String() + assert.Regexp(t, fmt.Sprintf("activitypub/user/%s$", username), keyId) assert.Regexp(t, fmt.Sprintf("activitypub/user/%s/outbox$", username), person.GetActivityStreamsOutbox().GetIRI().String()) assert.Regexp(t, fmt.Sprintf("activitypub/user/%s/inbox$", username), person.GetActivityStreamsInbox().GetIRI().String()) + + pkp := person.GetW3IDSecurityV1PublicKey() + publicKeyId := keyId + "/#main-key" + var pkpFound vocab.W3IDSecurityV1PublicKey + for pkpIter := pkp.Begin(); pkpIter != pkp.End(); pkpIter = pkpIter.Next() { + if !pkpIter.IsW3IDSecurityV1PublicKey() { + continue + } + pkValue := pkpIter.Get() + var pkId *url.URL + pkId, err = pub.GetId(pkValue) + if err != nil { + return + } + assert.Equal(t, pkId.String(), publicKeyId) + if pkId.String() != publicKeyId { + continue + } + pkpFound = pkValue + break + } + assert.NotNil(t, pkpFound) + + pkPemProp := pkpFound.GetW3IDSecurityV1PublicKeyPem() + assert.NotNil(t, pkPemProp) + assert.True(t, pkPemProp.IsXMLSchemaString()) + + pubKeyPem := pkPemProp.Get() + assert.Regexp(t, "^-----BEGIN PUBLIC KEY-----", pubKeyPem) }) } diff --git a/routers/api/v1/activitypub/person.go b/routers/api/v1/activitypub/person.go index ae1a8a7bbd..326629f8b2 100644 --- a/routers/api/v1/activitypub/person.go +++ b/routers/api/v1/activitypub/person.go @@ -9,12 +9,38 @@ import ( "net/url" "strings" + "code.gitea.io/gitea/models" + "code.gitea.io/gitea/modules/activitypub" "code.gitea.io/gitea/modules/context" "code.gitea.io/gitea/modules/setting" "code.gitea.io/gitea/routers/api/v1/user" "github.com/go-fed/activity/streams" ) +// hack waiting on https://github.com/go-gitea/gitea/pull/16834 +func GetPublicKey(user *models.User) (string, error) { + if settings, err := models.GetUserSetting(user.ID, []string{"activitypub_pubPem"}); err != nil { + return "", err + } else if len(settings) == 0 { + if priv, pub, err := activitypub.GenerateKeyPair(); err != nil { + return "", err + } else { + privPem := &models.UserSetting{UserID: user.ID, Name: "activitypub_privPem", Value: priv} + if err := models.SetUserSetting(privPem); err != nil { + return "", err + } + pubPem := &models.UserSetting{UserID: user.ID, Name: "activitypub_pubPem", Value: pub} + if err := models.SetUserSetting(pubPem); err != nil { + return "", err + } + return pubPem.Value, nil + } + } else { + return settings[0].Value, nil + } +} + +// NodeInfo returns the NodeInfo for the Gitea instance to allow for federation func Person(ctx *context.APIContext) { // swagger:operation GET /activitypub/user/{username} information // --- @@ -31,15 +57,15 @@ func Person(ctx *context.APIContext) { // "200": // "$ref": "#/responses/ActivityPub" - user.GetUserByParamsName(ctx, "username") + user := user.GetUserByParamsName(ctx, "username") username := ctx.Params("username") person := streams.NewActivityStreamsPerson() id := streams.NewJSONLDIdProperty() link := strings.TrimSuffix(setting.AppURL, "/") + strings.TrimSuffix(ctx.Req.URL.EscapedPath(), "/") - url_object, _ := url.Parse(link) - id.SetIRI(url_object) + idIRI, _ := url.Parse(link) + id.SetIRI(idIRI) person.SetJSONLDId(id) name := streams.NewActivityStreamsNameProperty() @@ -47,7 +73,7 @@ func Person(ctx *context.APIContext) { person.SetActivityStreamsName(name) ibox := streams.NewActivityStreamsInboxProperty() - url_object, _ = url.Parse(link + "/inbox") + url_object, _ := url.Parse(link + "/inbox") ibox.SetIRI(url_object) person.SetActivityStreamsInbox(ibox) @@ -56,6 +82,30 @@ func Person(ctx *context.APIContext) { obox.SetIRI(url_object) person.SetActivityStreamsOutbox(obox) + publicKeyProp := streams.NewW3IDSecurityV1PublicKeyProperty() + + publicKeyType := streams.NewW3IDSecurityV1PublicKey() + + pubKeyIdProp := streams.NewJSONLDIdProperty() + pubKeyIRI, _ := url.Parse(link + "/#main-key") + pubKeyIdProp.SetIRI(pubKeyIRI) + publicKeyType.SetJSONLDId(pubKeyIdProp) + + ownerProp := streams.NewW3IDSecurityV1OwnerProperty() + ownerProp.SetIRI(idIRI) + publicKeyType.SetW3IDSecurityV1Owner(ownerProp) + + publicKeyPemProp := streams.NewW3IDSecurityV1PublicKeyPemProperty() + if publicKeyPem, err := GetPublicKey(user); err != nil { + ctx.Error(http.StatusInternalServerError, "GetPublicKey", err) + } else { + publicKeyPemProp.Set(publicKeyPem) + } + publicKeyType.SetW3IDSecurityV1PublicKeyPem(publicKeyPemProp) + + publicKeyProp.AppendW3IDSecurityV1PublicKey(publicKeyType) + person.SetW3IDSecurityV1PublicKey(publicKeyProp) + var jsonmap map[string]interface{} jsonmap, _ = streams.Serialize(person) ctx.JSON(http.StatusOK, jsonmap)