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>
This commit is contained in:
Loïc Dachary 2021-10-26 15:59:14 -10:00 committed by Anthony Wang
parent f2db473b0d
commit 4951af4d99
No known key found for this signature in database
GPG Key ID: BC96B00AEC5F2D76
6 changed files with 195 additions and 0 deletions

View File

@ -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")
})
}

View File

@ -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"`
}

View File

@ -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)
}

View File

@ -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)

View File

@ -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"`
}

View File

@ -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": {