aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorLoïc Dachary2021-10-26 15:59:14 -1000
committerAnthony Wang2022-03-18 17:33:29 -0500
commit4951af4d994d134b63d58a61ace9a71b7fa770e0 (patch)
tree7fab03ef225f0fd3591a98c905b2e2cf30b96aed
parentf2db473b0d682facfd285aab646b7a5599d3f6fe (diff)
activitypub: implement /api/v1/activitypub/user/{username} (#14186)
Return informations regarding a Person (as defined in ActivityStreams https://www.w3.org/TR/activitystreams-vocabulary/#dfn-person). Refs: https://github.com/go-gitea/gitea/issues/14186 Signed-off-by: Loïc Dachary <loic@dachary.org>
-rw-r--r--integrations/api_activitypub_person_test.go63
-rw-r--r--modules/structs/activitypub.go9
-rw-r--r--routers/api/v1/activitypub/person.go62
-rw-r--r--routers/api/v1/api.go6
-rw-r--r--routers/api/v1/swagger/activitypub.go16
-rw-r--r--templates/swagger/v1_json.tmpl39
6 files changed, 195 insertions, 0 deletions
diff --git a/integrations/api_activitypub_person_test.go b/integrations/api_activitypub_person_test.go
new file mode 100644
index 000000000..2efa82f02
--- /dev/null
+++ b/integrations/api_activitypub_person_test.go
@@ -0,0 +1,63 @@
+// Copyright 2021 The Gitea Authors. All rights reserved.
+// Use of this source code is governed by a MIT-style
+// license that can be found in the LICENSE file.
+
+package integrations
+
+import (
+ "context"
+ "encoding/json"
+ "fmt"
+ "net/http"
+ "net/url"
+ "testing"
+
+ "code.gitea.io/gitea/modules/setting"
+ "github.com/go-fed/activity/streams"
+ "github.com/go-fed/activity/streams/vocab"
+
+ "github.com/stretchr/testify/assert"
+)
+
+func TestActivityPubPerson(t *testing.T) {
+ onGiteaRun(t, func(*testing.T, *url.URL) {
+ setting.Federation.Enabled = true
+ defer func() {
+ setting.Federation.Enabled = false
+ }()
+
+ username := "user2"
+ req := NewRequestf(t, "GET", fmt.Sprintf("/api/v1/activitypub/user/%s", username))
+ resp := MakeRequest(t, req, http.StatusOK)
+ assert.Contains(t, string(resp.Body.Bytes()), "@context")
+ var m map[string]interface{}
+ _ = json.Unmarshal(resp.Body.Bytes(), &m)
+
+ var person vocab.ActivityStreamsPerson
+ resolver, _ := streams.NewJSONResolver(func(c context.Context, p vocab.ActivityStreamsPerson) error {
+ person = p
+ return nil
+ })
+ 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.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())
+ })
+}
+
+func TestActivityPubMissingPerson(t *testing.T) {
+ onGiteaRun(t, func(*testing.T, *url.URL) {
+ setting.Federation.Enabled = true
+ defer func() {
+ setting.Federation.Enabled = false
+ }()
+
+ req := NewRequestf(t, "GET", "/api/v1/activitypub/user/nonexistentuser")
+ resp := MakeRequest(t, req, http.StatusNotFound)
+ assert.Contains(t, string(resp.Body.Bytes()), "GetUserByName")
+ })
+}
diff --git a/modules/structs/activitypub.go b/modules/structs/activitypub.go
new file mode 100644
index 000000000..e1e2ec46a
--- /dev/null
+++ b/modules/structs/activitypub.go
@@ -0,0 +1,9 @@
+// Copyright 2021 The Gitea Authors. All rights reserved.
+// Use of this source code is governed by a MIT-style
+// license that can be found in the LICENSE file.
+
+package structs
+
+type ActivityPub struct {
+ Context string `json:"@context"`
+}
diff --git a/routers/api/v1/activitypub/person.go b/routers/api/v1/activitypub/person.go
new file mode 100644
index 000000000..ae1a8a7bb
--- /dev/null
+++ b/routers/api/v1/activitypub/person.go
@@ -0,0 +1,62 @@
+// Copyright 2021 The Gitea Authors. All rights reserved.
+// Use of this source code is governed by a MIT-style
+// license that can be found in the LICENSE file.
+
+package activitypub
+
+import (
+ "net/http"
+ "net/url"
+ "strings"
+
+ "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"
+)
+
+func Person(ctx *context.APIContext) {
+ // swagger:operation GET /activitypub/user/{username} information
+ // ---
+ // summary: Returns the person
+ // produces:
+ // - application/json
+ // parameters:
+ // - name: username
+ // in: path
+ // description: username of the user
+ // type: string
+ // required: true
+ // responses:
+ // "200":
+ // "$ref": "#/responses/ActivityPub"
+
+ 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)
+ person.SetJSONLDId(id)
+
+ name := streams.NewActivityStreamsNameProperty()
+ name.AppendXMLSchemaString(username)
+ person.SetActivityStreamsName(name)
+
+ ibox := streams.NewActivityStreamsInboxProperty()
+ url_object, _ = url.Parse(link + "/inbox")
+ ibox.SetIRI(url_object)
+ person.SetActivityStreamsInbox(ibox)
+
+ obox := streams.NewActivityStreamsOutboxProperty()
+ url_object, _ = url.Parse(link + "/outbox")
+ obox.SetIRI(url_object)
+ person.SetActivityStreamsOutbox(obox)
+
+ var jsonmap map[string]interface{}
+ jsonmap, _ = streams.Serialize(person)
+ ctx.JSON(http.StatusOK, jsonmap)
+}
diff --git a/routers/api/v1/api.go b/routers/api/v1/api.go
index d4891daef..3fde7c34e 100644
--- a/routers/api/v1/api.go
+++ b/routers/api/v1/api.go
@@ -79,6 +79,7 @@ import (
"code.gitea.io/gitea/modules/setting"
api "code.gitea.io/gitea/modules/structs"
"code.gitea.io/gitea/modules/web"
+ "code.gitea.io/gitea/routers/api/v1/activitypub"
"code.gitea.io/gitea/routers/api/v1/admin"
"code.gitea.io/gitea/routers/api/v1/misc"
"code.gitea.io/gitea/routers/api/v1/notify"
@@ -597,6 +598,11 @@ func Routes(sessioner func(http.Handler) http.Handler) *web.Route {
m.Get("/version", misc.Version)
if setting.Federation.Enabled {
m.Get("/nodeinfo", misc.NodeInfo)
+ m.Group("/activitypub", func() {
+ m.Group("/user/{username}", func() {
+ m.Get("", activitypub.Person)
+ })
+ })
}
m.Get("/signing-key.gpg", misc.SigningKey)
m.Post("/markdown", bind(api.MarkdownOption{}), misc.Markdown)
diff --git a/routers/api/v1/swagger/activitypub.go b/routers/api/v1/swagger/activitypub.go
new file mode 100644
index 000000000..3576439f4
--- /dev/null
+++ b/routers/api/v1/swagger/activitypub.go
@@ -0,0 +1,16 @@
+// Copyright 2021 The Gitea Authors. All rights reserved.
+// Use of this source code is governed by a MIT-style
+// license that can be found in the LICENSE file.
+
+package swagger
+
+import (
+ api "code.gitea.io/gitea/modules/structs"
+)
+
+// ActivityPub
+// swagger:response ActivityPub
+type swaggerResponseActivityPub struct {
+ // in:body
+ Body api.ActivityPub `json:"body"`
+}
diff --git a/templates/swagger/v1_json.tmpl b/templates/swagger/v1_json.tmpl
index 3bc615818..c4ecf0f2e 100644
--- a/templates/swagger/v1_json.tmpl
+++ b/templates/swagger/v1_json.tmpl
@@ -23,6 +23,29 @@
},
"basePath": "{{AppSubUrl | JSEscape | Safe}}/api/v1",
"paths": {
+ "/activitypub/user/{username}": {
+ "get": {
+ "produces": [
+ "application/json"
+ ],
+ "summary": "Returns the person",
+ "operationId": "information",
+ "parameters": [
+ {
+ "type": "string",
+ "description": "username of the user",
+ "name": "username",
+ "in": "path",
+ "required": true
+ }
+ ],
+ "responses": {
+ "200": {
+ "$ref": "#/responses/ActivityPub"
+ }
+ }
+ }
+ },
"/admin/cron": {
"get": {
"produces": [
@@ -12700,6 +12723,16 @@
},
"x-go-package": "code.gitea.io/gitea/modules/structs"
},
+ "ActivityPub": {
+ "type": "object",
+ "properties": {
+ "@context": {
+ "type": "string",
+ "x-go-name": "Context"
+ }
+ },
+ "x-go-package": "code.gitea.io/gitea/modules/structs"
+ },
"AddCollaboratorOption": {
"description": "AddCollaboratorOption options when adding a user as a collaborator of a repository",
"type": "object",
@@ -18235,6 +18268,12 @@
}
}
},
+ "ActivityPub": {
+ "description": "ActivityPub",
+ "schema": {
+ "$ref": "#/definitions/ActivityPub"
+ }
+ },
"AnnotatedTag": {
"description": "AnnotatedTag",
"schema": {