Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

User keypairs and HTTP signatures for ActivityPub federation using go-ap #19133

Merged
merged 116 commits into from
Jun 19, 2022
Merged
Show file tree
Hide file tree
Changes from 112 commits
Commits
Show all changes
116 commits
Select commit Hold shift + click to select a range
f2db473
go.mod: add go-fed/{httpsig,activity/pub,activity/streams} dependency
Mar 13, 2022
4951af4
activitypub: implement /api/v1/activitypub/user/{username} (#14186)
Oct 27, 2021
678a56f
activitypub: add the public key to Person (#14186)
Oct 29, 2021
e8907c3
activitypub: go-fed conformant Clock instance
Nov 9, 2021
15c1f62
activitypub: signing http client
Nov 9, 2021
97fedf2
activitypub: implement the ReqSignature middleware
Nov 10, 2021
b342241
activitypub: hack_16834
Dec 24, 2021
2a8864f
Fix CI checks-backend errors with go mod tidy
Mar 19, 2022
b480c52
Change 2021 to 2022, properly format package imports
Mar 19, 2022
f9e33d9
Run make fmt and make generate-swagger
Mar 19, 2022
ea4129e
Use Gitea JSON library, add assert for pkp
Mar 19, 2022
456ed42
Run make fmt again, fix err var redeclaration
Mar 20, 2022
d75809a
Remove LogSQL from ActivityPub person test
Mar 20, 2022
ebef769
Assert if json.Unmarshal succeeds
Mar 20, 2022
46973f9
Cleanup, handle invalid usernames for ActivityPub person GET request
Mar 24, 2022
3ed4a71
Rename hack_16834 to user_settings
Mar 25, 2022
21c56f8
Use the httplib module instead of http for GET requests
Mar 27, 2022
373a84a
Clean up whitespace with make fmt
Mar 27, 2022
d1a53f7
Use time.RFC1123 and make the http.Client proxy-aware
Mar 28, 2022
65016b2
Check if digest algo is supported in setting module
Mar 29, 2022
1da0d49
Clean up some variable declarations
Mar 30, 2022
fdae736
Remove unneeded copy
Mar 30, 2022
7ea5e10
Use system timezone instead of setting.DefaultUILocation
Mar 30, 2022
5139b3d
Use named constant for httpsigExpirationTime
Apr 13, 2022
7931e21
Make pubKey IRI #main-key instead of /#main-key
Apr 13, 2022
702a963
Move /#main-key to #main-key in tests
Apr 14, 2022
523ca3d
Implemented Webfinger endpoint.
KN4CK3R Apr 20, 2022
0d120f8
Add visible check.
KN4CK3R Apr 22, 2022
5d61e59
Add user profile as alias.
KN4CK3R Apr 22, 2022
aa962c6
Add actor IRI and remote interaction URL to WebFinger response
Apr 22, 2022
07150b3
Merge branch 'master' into feature-activitypub
6543 May 9, 2022
55c5e93
fmt
6543 May 9, 2022
609fcc7
Merge branch 'master' into feature-activitypub
6543 May 9, 2022
364838c
Fix lint errors
May 10, 2022
501a39f
Merge remote-tracking branch 'github/feature-activitypub' into featur…
May 10, 2022
becdf5e
Use go-ap instead of go-fed
May 23, 2022
1e57f01
Merge remote-tracking branch 'github/main' into feature-activitypub
Jun 9, 2022
67e0fcd
Run go mod tidy to fix missing modules in go.mod and go.sum
Jun 9, 2022
57e6b67
make fmt
Jun 9, 2022
a8cb4a8
Convert remaining code to go-ap
Jun 9, 2022
94fbd80
Clean up go.sum
Jun 9, 2022
2f0a0b1
Merge branch 'main' into feature-activitypub
6543 Jun 10, 2022
46cab80
Fix JSON unmarshall error
Jun 10, 2022
86a3221
Fix CI errors by adding @context to Person() and making sure types match
Jun 10, 2022
d487a76
Correctly decode JSON in api_activitypub_person_test.go
Jun 10, 2022
fc58ab6
Force CI rerun
Jun 10, 2022
7428ff0
Fix TestActivityPubPersonInbox segfault
Jun 10, 2022
66b1761
Fix lint error
Jun 10, 2022
f7da251
Merge branch 'main' into feature-activitypub
6543 Jun 11, 2022
cf6aed3
Use @mariusor's suggestions for idiomatic go-ap usage
Jun 11, 2022
528c282
Correctly add inbox/outbox IRIs to person
Jun 11, 2022
7658649
Merge remote-tracking branch 'upstream/main' into feature-activitypub
Jun 11, 2022
6074222
Code cleanup
Jun 12, 2022
76f06ce
Remove another LogSQL from ActivityPub person test
Jun 12, 2022
d1f14ff
Move httpsig algos slice to an init() function
Jun 12, 2022
191919e
Merge remote-tracking branch 'upstream/main' into feature-activitypub
Jun 12, 2022
5823d81
Add actor IRI and remote interaction URL to WebFinger response
Apr 22, 2022
d91c61f
Update TestWebFinger to check for ActivityPub IRI in aliases
Jun 13, 2022
d7b81f5
make fmt
Jun 13, 2022
a5b00ec
Force CI rerun
Jun 13, 2022
b6b7fe2
WebFinger: Add CORS header and fix Href -> Template for remote intera…
Jun 13, 2022
718f35a
Merge remote-tracking branch 'upstream/main' into feature-activitypub
Jun 13, 2022
ed2a6f5
make lint-backend
Jun 13, 2022
f889793
Make sure Person endpoint has Content-Type application/activity+json …
Jun 13, 2022
3e9a69c
Use UTC instead of GMT
Jun 14, 2022
d749f8f
Rename pkey to pubKey
Jun 14, 2022
08eebff
Make sure HTTP request Date in GMT
Jun 14, 2022
2706e89
Merge branch 'main' into feature-activitypub
6543 Jun 14, 2022
900ceb2
make fmt
6543 Jun 14, 2022
3f2d8b0
dont drop err
6543 Jun 14, 2022
add8469
Merge branch 'feature-activitypub' of github.com:Ta180m/Gitea into fe…
Jun 14, 2022
e60158c
Make sure API responses always refer to username in original case
Jun 14, 2022
a4403e4
Move httpsig algs constant slice to modules/setting/federation.go
Jun 14, 2022
faf2855
Add new federation settings to app.example.ini and config-cheat-sheet
Jun 14, 2022
d06772b
Return if marshalling error
Jun 14, 2022
a312007
Make sure Person IRIs are generated correctly
Jun 14, 2022
f53e46c
If httpsig verification fails, fix Host header and try again
Jun 14, 2022
f8ad1a8
Apply suggestions from code review
6543 Jun 15, 2022
c05bad8
Merge branch 'main' into feature-activitypub
6543 Jun 15, 2022
14cfd8d
Revert "If httpsig verification fails, fix Host header and try again"
Jun 15, 2022
1da4849
Merge remote-tracking branch 'github/feature-activitypub' into featur…
Jun 15, 2022
f48115f
Go back to using ap.IRI to generate inbox and outbox IRIs
Jun 15, 2022
172c39f
use const for key values
6543 Jun 15, 2022
5840163
Update routers/web/webfinger.go
6543 Jun 15, 2022
46b344c
Merge branch 'main' into feature-activitypub
6543 Jun 15, 2022
e5ed91d
Merge remote-tracking branch 'github/feature-activitypub' into featur…
Jun 15, 2022
95aad98
Use ctx.JSON in Person response to make code cleaner
Jun 16, 2022
3fe4459
Revert "Use ctx.JSON in Person response to make code cleaner"
Jun 16, 2022
e9e8a03
Use activitypub.ActivityStreamsContentType for Person response Conten…
Jun 16, 2022
a2d5202
Limit maximum ActivityPub request and response sizes to a configurabl…
Jun 16, 2022
fb1f551
Move setting key constants to models/user/setting_keys.go
Jun 16, 2022
ad62049
Fix failing ActivityPubPerson integration test by checking the correc…
Jun 16, 2022
db13e1d
Add a warning about changing settings that can break federation
Jun 16, 2022
6336ba2
Add better comments
Jun 16, 2022
0c49fea
Don't multiply Federation.MaxSize by 1<<20 twice
Jun 16, 2022
6602fd1
Add more better comments
Jun 16, 2022
3a8b840
Fix failing ActivityPubMissingPerson test
Jun 16, 2022
2a013b8
make generate-swagger
Jun 16, 2022
c118dac
Move getting the RFC 2616 time to a separate function
Jun 16, 2022
b35490c
Merge remote-tracking branch 'upstream/main' into feature-activitypub
Jun 16, 2022
7a214dd
More code cleanup
Jun 16, 2022
7e1784f
Merge branch 'main' into feature-activitypub
6543 Jun 16, 2022
8e6f3fb
Update go-ap to fix empty liked collection and removed unneeded HTTP …
Jun 17, 2022
7446583
go mod tidy
Jun 17, 2022
47011db
Add ed25519 to httpsig algorithms
Jun 17, 2022
7bfadb4
Merge branch 'main' into feature-activitypub
6543 Jun 18, 2022
fe18cf7
Merge branch 'main' into feature-activitypub
6543 Jun 18, 2022
37d2d01
Use go-ap/jsonld to add @context and marshal JSON
Jun 18, 2022
7c10ab9
Change Gitea user agent from the default to Gitea/Version
Jun 18, 2022
0231dad
go mod tidy
Jun 18, 2022
adfb213
Merge branch 'main' into feature-activitypub
6543 Jun 18, 2022
1bd8eb6
Merge branch 'main' into feature-activitypub
6543 Jun 18, 2022
4ffb6b6
Use ctx.ServerError and remove all remote interaction code from webfi…
Jun 18, 2022
28fd3e7
Remove accidently added files
Jun 18, 2022
f602958
Use ctx.ServerError in reqsignature.go
Jun 18, 2022
a3ff170
Merge branch 'main' into feature-activitypub
6543 Jun 18, 2022
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
17 changes: 17 additions & 0 deletions custom/conf/app.example.ini
Original file line number Diff line number Diff line change
Expand Up @@ -2253,6 +2253,23 @@ PATH =
;;
;; Enable/Disable user statistics for nodeinfo if federation is enabled
; SHARE_USER_STATISTICS = true
;;
;; Maximum federation request and response size (MB)
; MAX_SIZE = 4
;;
;; WARNING: Changing the settings below can break federation.
;;
;; HTTP signature algorithms
; ALGORITHMS = rsa-sha256, rsa-sha512, ed25519
;;
;; HTTP signature digest algorithm
; DIGEST_ALGORITHM = SHA-256
;;
;; GET headers for federation requests
; GET_HEADERS = (request-target), Date
;;
;; POST headers for federation requests
; POST_HEADERS = (request-target), Date, Digest

;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
Expand Down
8 changes: 8 additions & 0 deletions docs/content/doc/advanced/config-cheat-sheet.en-us.md
Original file line number Diff line number Diff line change
Expand Up @@ -1090,6 +1090,14 @@ Task queue configuration has been moved to `queue.task`. However, the below conf

- `ENABLED`: **true**: Enable/Disable federation capabilities
- `SHARE_USER_STATISTICS`: **true**: Enable/Disable user statistics for nodeinfo if federation is enabled
- `MAX_SIZE`: **4**: Maximum federation request and response size (MB)

WARNING: Changing the settings below can break federation.
This conversation was marked as resolved.
Show resolved Hide resolved

- `ALGORITHMS`: **rsa-sha256, rsa-sha512, ed25519**: HTTP signature algorithms
- `DIGEST_ALGORITHM`: **SHA-256**: HTTP signature digest algorithm
- `GET_HEADERS`: **(request-target), Date**: GET headers for federation requests
This conversation was marked as resolved.
Show resolved Hide resolved
- `POST_HEADERS`: **(request-target), Date, Digest**: POST headers for federation requests

## Packages (`packages`)

Expand Down
7 changes: 6 additions & 1 deletion go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -28,10 +28,12 @@ require (
github.com/ethantkoenig/rupture v1.0.1
github.com/felixge/fgprof v0.9.2
github.com/gliderlabs/ssh v0.3.4
github.com/go-ap/activitypub v0.0.0-20220615144428-48208c70483b
github.com/go-ap/jsonld v0.0.0-20220615144122-1d862b15410d
github.com/go-chi/chi/v5 v5.0.7
github.com/go-chi/cors v1.2.1
github.com/go-enry/go-enry/v2 v2.8.2
github.com/go-fed/httpsig v1.1.0
github.com/go-fed/httpsig v1.1.1-0.20201223112313-55836744818e
github.com/go-git/go-billy/v5 v5.3.1
github.com/go-git/go-git/v5 v5.4.3-0.20210630082519-b4368b2a2ca4
github.com/go-ldap/ldap/v3 v3.4.3
Expand Down Expand Up @@ -107,6 +109,7 @@ require (

require (
cloud.google.com/go v0.99.0 // indirect
git.sr.ht/~mariusor/go-xsd-duration v0.0.0-20200411073322-f0bcc40f0bf2 // indirect
github.com/Azure/go-ntlmssp v0.0.0-20211209120228-48547f28849e // indirect
github.com/Microsoft/go-winio v0.5.2 // indirect
github.com/ProtonMail/go-crypto v0.0.0-20220407094043-a94812496cf5 // indirect
Expand Down Expand Up @@ -160,6 +163,7 @@ require (
github.com/fsnotify/fsnotify v1.5.4 // indirect
github.com/fullstorydev/grpcurl v1.8.1 // indirect
github.com/fxamacker/cbor/v2 v2.4.0 // indirect
github.com/go-ap/errors v0.0.0-20220615144307-e8bc4a40ae9f // indirect
github.com/go-asn1-ber/asn1-ber v1.5.4 // indirect
github.com/go-enry/go-oniguruma v1.2.1 // indirect
github.com/go-git/gcfg v1.5.0 // indirect
Expand Down Expand Up @@ -252,6 +256,7 @@ require (
github.com/toqueteos/webbrowser v1.2.0 // indirect
github.com/ulikunitz/xz v0.5.10 // indirect
github.com/unknwon/com v1.0.1 // indirect
github.com/valyala/fastjson v1.6.3 // indirect
github.com/x448/float16 v0.8.4 // indirect
github.com/xanzy/ssh-agent v0.3.1 // indirect
github.com/xi2/xz v0.0.0-20171230120015-48954b6210f8 // indirect
Expand Down
14 changes: 12 additions & 2 deletions go.sum
Original file line number Diff line number Diff line change
Expand Up @@ -69,6 +69,8 @@ contrib.go.opencensus.io/exporter/stackdriver v0.13.5/go.mod h1:aXENhDJ1Y4lIg4EU
contrib.go.opencensus.io/integrations/ocsql v0.1.4/go.mod h1:8DsSdjz3F+APR+0z0WkU1aRorQCFfRxvqjUUPMbF3fE=
contrib.go.opencensus.io/resource v0.1.1/go.mod h1:F361eGI91LCmW1I/Saf+rX0+OFcigGlFvXwEGEnkRLA=
dmitri.shuralyov.com/gpu/mtl v0.0.0-20190408044501-666a987793e9/go.mod h1:H6x//7gZCb22OMCxBHrMx7a5I7Hp++hsVxbQ4BYO7hU=
git.sr.ht/~mariusor/go-xsd-duration v0.0.0-20200411073322-f0bcc40f0bf2 h1:2OrsyJYZp7J6nyAsKi2q1SELYRaIc0aQmcQ/EQqPfk8=
git.sr.ht/~mariusor/go-xsd-duration v0.0.0-20200411073322-f0bcc40f0bf2/go.mod h1:g/V2Hjas6Z1UHUp4yIx6bATpNzJ7DYtD0FG3+xARWxs=
gitea.com/go-chi/binding v0.0.0-20220309004920-114340dabecb h1:Yy0Bxzc8R2wxiwXoG/rECGplJUSpXqCsog9PuJFgiHs=
gitea.com/go-chi/binding v0.0.0-20220309004920-114340dabecb/go.mod h1:77TZu701zMXWJFvB8gvTbQ92zQ3DQq/H7l5wAEjQRKc=
gitea.com/go-chi/cache v0.0.0-20210110083709-82c4c9ce2d5e/go.mod h1:k2V/gPDEtXGjjMGuBJiapffAXTv76H4snSmlJRLUhH0=
Expand Down Expand Up @@ -460,6 +462,12 @@ github.com/globalsign/mgo v0.0.0-20180905125535-1ca0a4f7cbcb/go.mod h1:xkRDCp4j0
github.com/globalsign/mgo v0.0.0-20181015135952-eeefdecb41b8/go.mod h1:xkRDCp4j0OGD1HRkm4kmhM+pmpv3AKq5SU7GMg4oO/Q=
github.com/glycerine/go-unsnap-stream v0.0.0-20181221182339-f9677308dec2/go.mod h1:/20jfyN9Y5QPEAprSgKAUr+glWDY39ZiUEAYOEv5dsE=
github.com/glycerine/goconvey v0.0.0-20190410193231-58a59202ab31/go.mod h1:Ogl1Tioa0aV7gstGFO7KhffUsb9M4ydbEbbxpcEDc24=
github.com/go-ap/activitypub v0.0.0-20220615144428-48208c70483b h1:+RjYfEfoZdM3wHFs752dlOpGaoRhwRRyQxjajg08LcQ=
github.com/go-ap/activitypub v0.0.0-20220615144428-48208c70483b/go.mod h1:DE3vvc6Didgfd3k7M1Mos6qMDFNmMrxJmYVMHG9h9Io=
github.com/go-ap/errors v0.0.0-20220615144307-e8bc4a40ae9f h1:kJhGo4NApJP0Lt9lkJnfmuTnRWVFbCynY0kiTxpPUR4=
github.com/go-ap/errors v0.0.0-20220615144307-e8bc4a40ae9f/go.mod h1:KHkKFKZvc05lr79+RGoq/zG8YjWi3+FK60Bxd+mpCew=
github.com/go-ap/jsonld v0.0.0-20220615144122-1d862b15410d h1:Z/oRXMlZHjvjIqDma1FrIGL3iE5YL7MUI0bwYEZ6qbA=
github.com/go-ap/jsonld v0.0.0-20220615144122-1d862b15410d/go.mod h1:jyveZeGw5LaADntW+UEsMjl3IlIwk+DxlYNsbofQkGA=
github.com/go-asn1-ber/asn1-ber v1.5.4 h1:vXT6d/FNDiELJnLb6hGNa309LMsrCoYFvpwHDF0+Y1A=
github.com/go-asn1-ber/asn1-ber v1.5.4/go.mod h1:hEBeB/ic+5LoWskz+yKT7vGhhPYkProFKoKdwZRWMe0=
github.com/go-chi/chi/v5 v5.0.1/go.mod h1:DslCQbL2OYiznFReuXYUmQ2hGd1aDpCnlMNITLSKoi8=
Expand All @@ -472,8 +480,8 @@ github.com/go-enry/go-enry/v2 v2.8.2 h1:uiGmC+3K8sVd/6DOe2AOJEOihJdqda83nPyJNtMR
github.com/go-enry/go-enry/v2 v2.8.2/go.mod h1:GVzIiAytiS5uT/QiuakK7TF1u4xDab87Y8V5EJRpsIQ=
github.com/go-enry/go-oniguruma v1.2.1 h1:k8aAMuJfMrqm/56SG2lV9Cfti6tC4x8673aHCcBk+eo=
github.com/go-enry/go-oniguruma v1.2.1/go.mod h1:bWDhYP+S6xZQgiRL7wlTScFYBe023B6ilRZbCAD5Hf4=
github.com/go-fed/httpsig v1.1.0 h1:9M+hb0jkEICD8/cAiNqEB66R87tTINszBRTjwjQzWcI=
github.com/go-fed/httpsig v1.1.0/go.mod h1:RCMrTZvN1bJYtofsG4rd5NaO5obxQ5xBkdiS7xsT7bM=
github.com/go-fed/httpsig v1.1.1-0.20201223112313-55836744818e h1:oRq/fiirun5HqlEWMLIcDmLpIELlG4iGbd0s8iqgPi8=
github.com/go-fed/httpsig v1.1.1-0.20201223112313-55836744818e/go.mod h1:RCMrTZvN1bJYtofsG4rd5NaO5obxQ5xBkdiS7xsT7bM=
github.com/go-git/gcfg v1.5.0 h1:Q5ViNfGF8zFgyJWPqYwA7qGFoMTEiBmdlkcfRmpIMa4=
github.com/go-git/gcfg v1.5.0/go.mod h1:5m20vg6GwYabIxaOonVkTdrILxQMpEShl1xiMF4ua+E=
github.com/go-git/go-billy/v5 v5.2.0/go.mod h1:pmpqyWchKfYfrkb/UVH4otLvyi/5gJlGI4Hb3ZqZ3W0=
Expand Down Expand Up @@ -1507,6 +1515,8 @@ github.com/urfave/cli v1.22.9 h1:cv3/KhXGBGjEXLC4bH0sLuJ9BewaAbpk5oyMOveu4pw=
github.com/urfave/cli v1.22.9/go.mod h1:Gos4lmkARVdJ6EkW0WaNv/tZAAMe9V7XWyB60NtXRu0=
github.com/urfave/cli/v2 v2.2.0/go.mod h1:SE9GqnLQmjVa0iPEY0f1w3ygNIYcIJ0OKPMoW2caLfQ=
github.com/valyala/bytebufferpool v1.0.0/go.mod h1:6bBcMArwyJ5K/AmCkWv1jt77kVWyCJ6HpOuEn7z0Csc=
github.com/valyala/fastjson v1.6.3 h1:tAKFnnwmeMGPbwJ7IwxcTPCNr3uIzoIj3/Fh90ra4xc=
github.com/valyala/fastjson v1.6.3/go.mod h1:CLCAqky6SMuOcxStkYQvblddUtoRxhYMGLrsQns1aXY=
github.com/valyala/fasttemplate v1.0.1/go.mod h1:UQGH1tvbgY+Nz5t2n7tXsz52dQxojPUpymEIMZ47gx8=
github.com/vektah/gqlparser v1.1.2/go.mod h1:1ycwN7Ij5njmMkPPAOaRFY4rET2Enx7IkVv3vaXspKw=
github.com/weppos/publicsuffix-go v0.13.1-0.20210123135404-5fd73613514e/go.mod h1:HYux0V0Zi04bHNwOHy4cXJVz/TQjYonnF6aoYhj+3QE=
Expand Down
103 changes: 103 additions & 0 deletions integrations/api_activitypub_person_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,103 @@
// Copyright 2022 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"
"fmt"
"net/http"
"net/http/httptest"
"net/url"
"testing"

user_model "code.gitea.io/gitea/models/user"
"code.gitea.io/gitea/modules/activitypub"
"code.gitea.io/gitea/modules/setting"

This conversation was marked as resolved.
Show resolved Hide resolved
ap "github.com/go-ap/activitypub"
"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)
body := resp.Body.Bytes()
assert.Contains(t, string(body), "@context")

var person ap.Person
err := person.UnmarshalJSON(body)
assert.NoError(t, err)

assert.Equal(t, ap.PersonType, person.Type)
assert.Equal(t, username, person.PreferredUsername.String())
keyID := person.GetID().String()
assert.Regexp(t, fmt.Sprintf("activitypub/user/%s$", username), keyID)
This conversation was marked as resolved.
Show resolved Hide resolved
assert.Regexp(t, fmt.Sprintf("activitypub/user/%s/outbox$", username), person.Outbox.GetID().String())
assert.Regexp(t, fmt.Sprintf("activitypub/user/%s/inbox$", username), person.Inbox.GetID().String())

pubKey := person.PublicKey
assert.NotNil(t, pubKey)
publicKeyID := keyID + "#main-key"
assert.Equal(t, pubKey.ID.String(), publicKeyID)

pubKeyPem := pubKey.PublicKeyPem
assert.NotNil(t, pubKeyPem)
assert.Regexp(t, "^-----BEGIN PUBLIC KEY-----", pubKeyPem)
})
}

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, resp.Body.String(), "user redirect does not exist")
})
}

func TestActivityPubPersonInbox(t *testing.T) {
srv := httptest.NewServer(c)
defer srv.Close()

onGiteaRun(t, func(*testing.T, *url.URL) {
appURL := setting.AppURL
setting.Federation.Enabled = true
setting.AppURL = srv.URL
defer func() {
setting.Federation.Enabled = false
setting.Database.LogSQL = false
setting.AppURL = appURL
}()
username1 := "user1"
ctx := context.Background()
user1, err := user_model.GetUserByName(ctx, username1)
assert.NoError(t, err)
user1url := fmt.Sprintf("%s/api/v1/activitypub/user/%s#main-key", srv.URL, username1)
c, err := activitypub.NewClient(user1, user1url)
assert.NoError(t, err)
username2 := "user2"
user2inboxurl := fmt.Sprintf("%s/api/v1/activitypub/user/%s/inbox", srv.URL, username2)

// Signed request succeeds
resp, err := c.Post([]byte{}, user2inboxurl)
assert.NoError(t, err)
assert.Equal(t, http.StatusNoContent, resp.StatusCode)

// Unsigned request fails
req := NewRequest(t, "POST", user2inboxurl)
MakeRequest(t, req, http.StatusInternalServerError)
})
}
2 changes: 1 addition & 1 deletion integrations/webfinger_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -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()}, 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)
Expand Down
4 changes: 4 additions & 0 deletions models/user/setting_keys.go
Original file line number Diff line number Diff line change
Expand Up @@ -9,4 +9,8 @@ const (
SettingsKeyHiddenCommentTypes = "issue.hidden_comment_types"
// SettingsKeyDiffWhitespaceBehavior is the setting key for whitespace behavior of diff
SettingsKeyDiffWhitespaceBehavior = "diff.whitespace_behaviour"
// UserActivityPubPrivPem is user's private key
UserActivityPubPrivPem = "activitypub.priv_pem"
// UserActivityPubPubPem is user's public key
UserActivityPubPubPem = "activitypub.pub_pem"
)
124 changes: 124 additions & 0 deletions modules/activitypub/client.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,124 @@
// Copyright 2022 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 (
"bytes"
"crypto/rsa"
"crypto/x509"
"encoding/pem"
"fmt"
"net/http"
"strings"
"time"

user_model "code.gitea.io/gitea/models/user"
"code.gitea.io/gitea/modules/proxy"
"code.gitea.io/gitea/modules/setting"

"github.com/go-fed/httpsig"
)

const (
// ActivityStreamsContentType const
ActivityStreamsContentType = `application/ld+json; profile="https://www.w3.org/ns/activitystreams"`
httpsigExpirationTime = 60
)

// Gets the current time as an RFC 2616 formatted string
// RFC 2616 requires RFC 1123 dates but with GMT instead of UTC
func CurrentTime() string {
return strings.ReplaceAll(time.Now().UTC().Format(time.RFC1123), "UTC", "GMT")
}

func containsRequiredHTTPHeaders(method string, headers []string) error {
var hasRequestTarget, hasDate, hasDigest bool
for _, header := range headers {
hasRequestTarget = hasRequestTarget || header == httpsig.RequestTarget
hasDate = hasDate || header == "Date"
hasDigest = hasDigest || header == "Digest"
}
if !hasRequestTarget {
return fmt.Errorf("missing http header for %s: %s", method, httpsig.RequestTarget)
} else if !hasDate {
return fmt.Errorf("missing http header for %s: Date", method)
} else if !hasDigest && method != http.MethodGet {
return fmt.Errorf("missing http header for %s: Digest", method)
}
return nil
}

// Client struct
type Client struct {
client *http.Client
algs []httpsig.Algorithm
digestAlg httpsig.DigestAlgorithm
getHeaders []string
postHeaders []string
priv *rsa.PrivateKey
pubID string
}

// NewClient function
func NewClient(user *user_model.User, pubID string) (c *Client, err error) {
if err = containsRequiredHTTPHeaders(http.MethodGet, setting.Federation.GetHeaders); err != nil {
return
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Given that there is currently a PR that attempts to get rid of naked returns, I think you should convert them.
Everywhere you added them.

} else if err = containsRequiredHTTPHeaders(http.MethodPost, setting.Federation.PostHeaders); err != nil {
return
}

priv, err := GetPrivateKey(user)
if err != nil {
return
}
privPem, _ := pem.Decode([]byte(priv))
privParsed, err := x509.ParsePKCS1PrivateKey(privPem.Bytes)
if err != nil {
return
}

c = &Client{
client: &http.Client{
Transport: &http.Transport{
Proxy: proxy.Proxy(),
},
},
algs: setting.HttpsigAlgs,
digestAlg: httpsig.DigestAlgorithm(setting.Federation.DigestAlgorithm),
getHeaders: setting.Federation.GetHeaders,
postHeaders: setting.Federation.PostHeaders,
priv: privParsed,
pubID: pubID,
}
return
}

// NewRequest function
func (c *Client) NewRequest(b []byte, to string) (req *http.Request, err error) {
buf := bytes.NewBuffer(b)
req, err = http.NewRequest(http.MethodPost, to, buf)
if err != nil {
return
}
req.Header.Add("Content-Type", ActivityStreamsContentType)
req.Header.Add("Date", CurrentTime())
req.Header.Add("User-Agent", "Gitea/"+setting.AppVer)
signer, _, err := httpsig.NewSigner(c.algs, c.digestAlg, c.postHeaders, httpsig.Signature, httpsigExpirationTime)
This conversation was marked as resolved.
Show resolved Hide resolved
if err != nil {
return
}
err = signer.SignRequest(c.priv, c.pubID, req, b)
return
}

// Post function
func (c *Client) Post(b []byte, to string) (resp *http.Response, err error) {
var req *http.Request
if req, err = c.NewRequest(b, to); err != nil {
return
}
resp, err = c.client.Do(req)
return
}
Loading