Skip to content
GitLab
Menu
Projects
Groups
Snippets
Loading...
Help
Help
Support
Community forum
Keyboard shortcuts
?
Submit feedback
Contribute to GitLab
Sign in / Register
Toggle navigation
Menu
Open sidebar
forgefriends
Forgefriends
Commits
8a3c8657
Verified
Commit
8a3c8657
authored
Nov 10, 2021
by
Loïc Dachary
Browse files
activitypub: implement the ReqSignature middleware
Signed-off-by:
Loïc Dachary
<
loic@dachary.org
>
parent
323cdf84
Changes
8
Hide whitespace changes
Inline
Side-by-side
integrations/api_activitypub_person_test.go
View file @
8a3c8657
...
...
@@ -9,9 +9,12 @@ import (
"encoding/json"
"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"
"github.com/go-fed/activity/pub"
"github.com/go-fed/activity/streams"
...
...
@@ -32,7 +35,7 @@ func TestActivityPubPerson(t *testing.T) {
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"
)
assert
.
Contains
(
t
,
resp
.
Body
.
String
(
),
"@context"
)
var
m
map
[
string
]
interface
{}
_
=
json
.
Unmarshal
(
resp
.
Body
.
Bytes
(),
&
m
)
...
...
@@ -46,26 +49,26 @@ func TestActivityPubPerson(t *testing.T) {
assert
.
Equal
(
t
,
err
,
nil
)
assert
.
Equal
(
t
,
"Person"
,
person
.
GetTypeName
())
assert
.
Equal
(
t
,
username
,
person
.
GetActivityStreamsName
()
.
Begin
()
.
GetXMLSchemaString
())
keyI
d
:=
person
.
GetJSONLDId
()
.
GetIRI
()
.
String
()
assert
.
Regexp
(
t
,
fmt
.
Sprintf
(
"activitypub/user/%s$"
,
username
),
keyI
d
)
keyI
D
:=
person
.
GetJSONLDId
()
.
GetIRI
()
.
String
()
assert
.
Regexp
(
t
,
fmt
.
Sprintf
(
"activitypub/user/%s$"
,
username
),
keyI
D
)
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
()
publicKeyI
d
:=
keyI
d
+
"/#main-key"
publicKeyI
D
:=
keyI
D
+
"/#main-key"
var
pkpFound
vocab
.
W3IDSecurityV1PublicKey
for
pkpIter
:=
pkp
.
Begin
();
pkpIter
!=
pkp
.
End
();
pkpIter
=
pkpIter
.
Next
()
{
if
!
pkpIter
.
IsW3IDSecurityV1PublicKey
()
{
continue
}
pkValue
:=
pkpIter
.
Get
()
var
pkI
d
*
url
.
URL
pkI
d
,
err
=
pub
.
GetId
(
pkValue
)
var
pkI
D
*
url
.
URL
pkI
D
,
err
=
pub
.
GetId
(
pkValue
)
if
err
!=
nil
{
return
}
assert
.
Equal
(
t
,
pkI
d
.
String
(),
publicKeyI
d
)
if
pkI
d
.
String
()
!=
publicKeyI
d
{
assert
.
Equal
(
t
,
pkI
D
.
String
(),
publicKeyI
D
)
if
pkI
D
.
String
()
!=
publicKeyI
D
{
continue
}
pkpFound
=
pkValue
...
...
@@ -91,6 +94,40 @@ func TestActivityPubMissingPerson(t *testing.T) {
req
:=
NewRequestf
(
t
,
"GET"
,
"/api/v1/activitypub/user/nonexistentuser"
)
resp
:=
MakeRequest
(
t
,
req
,
http
.
StatusNotFound
)
assert
.
Contains
(
t
,
string
(
resp
.
Body
.
Bytes
()),
"GetUserByName"
)
assert
.
Contains
(
t
,
resp
.
Body
.
String
(),
"GetUserByName"
)
})
}
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
.
Database
.
LogSQL
=
true
setting
.
AppURL
=
srv
.
URL
defer
func
()
{
setting
.
Federation
.
Enabled
=
false
setting
.
Database
.
LogSQL
=
false
setting
.
AppURL
=
appURL
}()
username1
:=
"user1"
user1
,
err
:=
user_model
.
GetUserByName
(
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
,
204
,
resp
.
StatusCode
)
// Unsigned request fails
req
:=
NewRequest
(
t
,
"POST"
,
user2inboxurl
)
MakeRequest
(
t
,
req
,
500
)
})
}
modules/activitypub/client.go
View file @
8a3c8657
...
...
@@ -19,10 +19,11 @@ import (
)
const
(
activityStreamsContentType
=
"application/ld+json; profile=
\"
https://www.w3.org/ns/activitystreams
\"
"
// ActivityStreamsContentType const
ActivityStreamsContentType
=
"application/ld+json; profile=
\"
https://www.w3.org/ns/activitystreams
\"
"
)
func
containsRequiredH
ttp
Headers
(
method
string
,
headers
[]
string
)
error
{
func
containsRequiredH
TTP
Headers
(
method
string
,
headers
[]
string
)
error
{
var
hasRequestTarget
,
hasDate
,
hasDigest
bool
for
_
,
header
:=
range
headers
{
hasRequestTarget
=
hasRequestTarget
||
header
==
httpsig
.
RequestTarget
...
...
@@ -39,6 +40,7 @@ func containsRequiredHttpHeaders(method string, headers []string) error {
return
nil
}
// Client struct
type
Client
struct
{
clock
pub
.
Clock
client
*
http
.
Client
...
...
@@ -47,13 +49,14 @@ type Client struct {
getHeaders
[]
string
postHeaders
[]
string
priv
*
rsa
.
PrivateKey
pubI
d
string
pubI
D
string
}
func
NewClient
(
user
*
user_model
.
User
,
pubId
string
)
(
c
*
Client
,
err
error
)
{
if
err
=
containsRequiredHttpHeaders
(
http
.
MethodGet
,
setting
.
Federation
.
GetHeaders
);
err
!=
nil
{
// 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
}
else
if
err
=
containsRequiredH
ttp
Headers
(
http
.
MethodPost
,
setting
.
Federation
.
PostHeaders
);
err
!=
nil
{
}
else
if
err
=
containsRequiredH
TTP
Headers
(
http
.
MethodPost
,
setting
.
Federation
.
PostHeaders
);
err
!=
nil
{
return
}
else
if
!
httpsig
.
IsSupportedDigestAlgorithm
(
setting
.
Federation
.
DigestAlgorithm
)
{
err
=
fmt
.
Errorf
(
"unsupported digest algorithm: %s"
,
setting
.
Federation
.
DigestAlgorithm
)
...
...
@@ -86,21 +89,21 @@ func NewClient(user *user_model.User, pubId string) (c *Client, err error) {
getHeaders
:
setting
.
Federation
.
GetHeaders
,
postHeaders
:
setting
.
Federation
.
PostHeaders
,
priv
:
privParsed
,
pubI
d
:
pubI
d
,
pubI
D
:
pubI
D
,
}
return
}
func
(
c
*
Client
)
Post
(
b
[]
byte
,
to
string
)
(
resp
*
http
.
Response
,
err
error
)
{
// NewRequest function
func
(
c
*
Client
)
NewRequest
(
b
[]
byte
,
to
string
)
(
req
*
http
.
Request
,
err
error
)
{
byteCopy
:=
make
([]
byte
,
len
(
b
))
copy
(
byteCopy
,
b
)
buf
:=
bytes
.
NewBuffer
(
byteCopy
)
var
req
*
http
.
Request
req
,
err
=
http
.
NewRequest
(
http
.
MethodPost
,
to
,
buf
)
if
err
!=
nil
{
return
}
req
.
Header
.
Add
(
"Content-Type"
,
a
ctivityStreamsContentType
)
req
.
Header
.
Add
(
"Content-Type"
,
A
ctivityStreamsContentType
)
req
.
Header
.
Add
(
"Accept-Charset"
,
"utf-8"
)
req
.
Header
.
Add
(
"Date"
,
fmt
.
Sprintf
(
"%s GMT"
,
c
.
clock
.
Now
()
.
UTC
()
.
Format
(
"Mon, 02 Jan 2006 15:04:05"
)))
...
...
@@ -108,8 +111,14 @@ func (c *Client) Post(b []byte, to string) (resp *http.Response, err error) {
if
err
!=
nil
{
return
}
err
=
signer
.
SignRequest
(
c
.
priv
,
c
.
pubId
,
req
,
b
)
if
err
!=
nil
{
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
)
...
...
modules/activitypub/client_test.go
View file @
8a3c8657
...
...
@@ -24,15 +24,15 @@ import (
func
TestActivityPubSignedPost
(
t
*
testing
.
T
)
{
assert
.
NoError
(
t
,
unittest
.
PrepareTestDatabase
())
user
:=
unittest
.
AssertExistsAndLoadBean
(
t
,
&
user_model
.
User
{
ID
:
1
})
.
(
*
user_model
.
User
)
pubI
d
:=
"https://example.com/pubI
d
"
c
,
err
:=
NewClient
(
user
,
pubI
d
)
pubI
D
:=
"https://example.com/pubI
D
"
c
,
err
:=
NewClient
(
user
,
pubI
D
)
assert
.
NoError
(
t
,
err
)
expected
:=
"BODY"
srv
:=
httptest
.
NewServer
(
http
.
HandlerFunc
(
func
(
w
http
.
ResponseWriter
,
r
*
http
.
Request
)
{
assert
.
Regexp
(
t
,
regexp
.
MustCompile
(
"^"
+
setting
.
Federation
.
DigestAlgorithm
),
r
.
Header
.
Get
(
"Digest"
))
assert
.
Contains
(
t
,
r
.
Header
.
Get
(
"Signature"
),
pubI
d
)
assert
.
Equal
(
t
,
r
.
Header
.
Get
(
"Content-Type"
),
a
ctivityStreamsContentType
)
assert
.
Contains
(
t
,
r
.
Header
.
Get
(
"Signature"
),
pubI
D
)
assert
.
Equal
(
t
,
r
.
Header
.
Get
(
"Content-Type"
),
A
ctivityStreamsContentType
)
body
,
err
:=
ioutil
.
ReadAll
(
r
.
Body
)
assert
.
NoError
(
t
,
err
)
assert
.
Equal
(
t
,
expected
,
string
(
body
))
...
...
modules/structs/activitypub.go
View file @
8a3c8657
...
...
@@ -4,6 +4,7 @@
package
structs
// ActivityPub type
type
ActivityPub
struct
{
Context
string
`json:"@context"`
}
routers/api/v1/activitypub/person.go
View file @
8a3c8657
...
...
@@ -9,7 +9,6 @@ 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"
...
...
@@ -17,32 +16,9 @@ import (
"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
// Person function
func
Person
(
ctx
*
context
.
APIContext
)
{
// swagger:operation GET /activitypub/user/{username}
informati
on
// swagger:operation GET /activitypub/user/{username}
activitypub activitypubPers
on
// ---
// summary: Returns the person
// produces:
...
...
@@ -73,30 +49,30 @@ func Person(ctx *context.APIContext) {
person
.
SetActivityStreamsName
(
name
)
ibox
:=
streams
.
NewActivityStreamsInboxProperty
()
url
_o
bject
,
_
:=
url
.
Parse
(
link
+
"/inbox"
)
ibox
.
SetIRI
(
url
_o
bject
)
url
O
bject
,
_
:=
url
.
Parse
(
link
+
"/inbox"
)
ibox
.
SetIRI
(
url
O
bject
)
person
.
SetActivityStreamsInbox
(
ibox
)
obox
:=
streams
.
NewActivityStreamsOutboxProperty
()
url
_o
bject
,
_
=
url
.
Parse
(
link
+
"/outbox"
)
obox
.
SetIRI
(
url
_o
bject
)
url
O
bject
,
_
=
url
.
Parse
(
link
+
"/outbox"
)
obox
.
SetIRI
(
url
O
bject
)
person
.
SetActivityStreamsOutbox
(
obox
)
publicKeyProp
:=
streams
.
NewW3IDSecurityV1PublicKeyProperty
()
publicKeyType
:=
streams
.
NewW3IDSecurityV1PublicKey
()
pubKeyI
d
Prop
:=
streams
.
NewJSONLDIdProperty
()
pubKeyI
D
Prop
:=
streams
.
NewJSONLDIdProperty
()
pubKeyIRI
,
_
:=
url
.
Parse
(
link
+
"/#main-key"
)
pubKeyI
d
Prop
.
SetIRI
(
pubKeyIRI
)
publicKeyType
.
SetJSONLDId
(
pubKeyI
d
Prop
)
pubKeyI
D
Prop
.
SetIRI
(
pubKeyIRI
)
publicKeyType
.
SetJSONLDId
(
pubKeyI
D
Prop
)
ownerProp
:=
streams
.
NewW3IDSecurityV1OwnerProperty
()
ownerProp
.
SetIRI
(
idIRI
)
publicKeyType
.
SetW3IDSecurityV1Owner
(
ownerProp
)
publicKeyPemProp
:=
streams
.
NewW3IDSecurityV1PublicKeyPemProperty
()
if
publicKeyPem
,
err
:=
GetPublicKey
(
user
);
err
!=
nil
{
if
publicKeyPem
,
err
:=
activitypub
.
GetPublicKey
(
user
);
err
!=
nil
{
ctx
.
Error
(
http
.
StatusInternalServerError
,
"GetPublicKey"
,
err
)
}
else
{
publicKeyPemProp
.
Set
(
publicKeyPem
)
...
...
@@ -110,3 +86,24 @@ func Person(ctx *context.APIContext) {
jsonmap
,
_
=
streams
.
Serialize
(
person
)
ctx
.
JSON
(
http
.
StatusOK
,
jsonmap
)
}
// PersonInbox function
func
PersonInbox
(
ctx
*
context
.
APIContext
)
{
// swagger:operation POST /activitypub/user/{username}/inbox activitypub activitypubPersonInbox
// ---
// summary: Send to the inbox
// produces:
// - application/json
// parameters:
// - name: username
// in: path
// description: username of the user
// type: string
// required: true
// responses:
// responses:
// "204":
// "$ref": "#/responses/empty"
ctx
.
Status
(
http
.
StatusNoContent
)
}
routers/api/v1/activitypub/reqsignature.go
0 → 100644
View file @
8a3c8657
// 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
(
"context"
"crypto"
"crypto/x509"
"encoding/json"
"encoding/pem"
"fmt"
"io/ioutil"
"net/http"
"net/url"
"code.gitea.io/gitea/modules/activitypub"
gitea_context
"code.gitea.io/gitea/modules/context"
"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"
"github.com/go-fed/httpsig"
)
type
publicKeyer
interface
{
GetW3IDSecurityV1PublicKey
()
vocab
.
W3IDSecurityV1PublicKeyProperty
}
func
getPublicKeyFromResponse
(
ctx
context
.
Context
,
b
[]
byte
,
keyID
*
url
.
URL
)
(
p
crypto
.
PublicKey
,
err
error
)
{
m
:=
make
(
map
[
string
]
interface
{})
err
=
json
.
Unmarshal
(
b
,
&
m
)
if
err
!=
nil
{
return
}
var
t
vocab
.
Type
t
,
err
=
streams
.
ToType
(
ctx
,
m
)
if
err
!=
nil
{
return
}
pker
,
ok
:=
t
.
(
publicKeyer
)
if
!
ok
{
err
=
fmt
.
Errorf
(
"ActivityStreams type cannot be converted to one known to have publicKey property: %T"
,
t
)
return
}
pkp
:=
pker
.
GetW3IDSecurityV1PublicKey
()
if
pkp
==
nil
{
err
=
fmt
.
Errorf
(
"publicKey property is not provided"
)
return
}
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
}
if
pkID
.
String
()
!=
keyID
.
String
()
{
continue
}
pkpFound
=
pkValue
break
}
if
pkpFound
==
nil
{
err
=
fmt
.
Errorf
(
"cannot find publicKey with id: %s in %s"
,
keyID
,
b
)
return
}
pkPemProp
:=
pkpFound
.
GetW3IDSecurityV1PublicKeyPem
()
if
pkPemProp
==
nil
||
!
pkPemProp
.
IsXMLSchemaString
()
{
err
=
fmt
.
Errorf
(
"publicKeyPem property is not provided or it is not embedded as a value"
)
return
}
pubKeyPem
:=
pkPemProp
.
Get
()
var
block
*
pem
.
Block
block
,
_
=
pem
.
Decode
([]
byte
(
pubKeyPem
))
if
block
==
nil
||
block
.
Type
!=
"PUBLIC KEY"
{
err
=
fmt
.
Errorf
(
"could not decode publicKeyPem to PUBLIC KEY pem block type"
)
return
}
p
,
err
=
x509
.
ParsePKIXPublicKey
(
block
.
Bytes
)
return
}
func
fetch
(
iri
*
url
.
URL
)
(
b
[]
byte
,
err
error
)
{
var
req
*
http
.
Request
req
,
err
=
http
.
NewRequest
(
http
.
MethodGet
,
iri
.
String
(),
nil
)
if
err
!=
nil
{
return
}
req
.
Header
.
Add
(
"Accept"
,
activitypub
.
ActivityStreamsContentType
)
req
.
Header
.
Add
(
"Accept-Charset"
,
"utf-8"
)
clock
,
err
:=
activitypub
.
NewClock
()
if
err
!=
nil
{
return
}
req
.
Header
.
Add
(
"Date"
,
fmt
.
Sprintf
(
"%s GMT"
,
clock
.
Now
()
.
UTC
()
.
Format
(
"Mon, 02 Jan 2006 15:04:05"
)))
var
resp
*
http
.
Response
client
:=
&
http
.
Client
{}
resp
,
err
=
client
.
Do
(
req
)
if
err
!=
nil
{
return
}
defer
resp
.
Body
.
Close
()
if
resp
.
StatusCode
!=
http
.
StatusOK
{
err
=
fmt
.
Errorf
(
"url IRI fetch [%s] failed with status (%d): %s"
,
iri
,
resp
.
StatusCode
,
resp
.
Status
)
return
}
b
,
err
=
ioutil
.
ReadAll
(
resp
.
Body
)
return
}
func
verifyHTTPSignatures
(
ctx
*
gitea_context
.
APIContext
)
(
authenticated
bool
,
err
error
)
{
r
:=
ctx
.
Req
// 1. Figure out what key we need to verify
var
v
httpsig
.
Verifier
v
,
err
=
httpsig
.
NewVerifier
(
r
)
if
err
!=
nil
{
return
}
ID
:=
v
.
KeyId
()
var
idIRI
*
url
.
URL
idIRI
,
err
=
url
.
Parse
(
ID
)
if
err
!=
nil
{
return
}
// 2. Fetch the public key of the other actor
var
b
[]
byte
b
,
err
=
fetch
(
idIRI
)
if
err
!=
nil
{
return
}
pKey
,
err
:=
getPublicKeyFromResponse
(
*
ctx
,
b
,
idIRI
)
if
err
!=
nil
{
return
}
// 3. Verify the other actor's key
algo
:=
httpsig
.
Algorithm
(
setting
.
Federation
.
Algorithms
[
0
])
authenticated
=
nil
==
v
.
Verify
(
pKey
,
algo
)
return
}
// ReqSignature function
func
ReqSignature
()
func
(
ctx
*
gitea_context
.
APIContext
)
{
return
func
(
ctx
*
gitea_context
.
APIContext
)
{
if
authenticated
,
err
:=
verifyHTTPSignatures
(
ctx
);
err
!=
nil
{
ctx
.
Error
(
http
.
StatusInternalServerError
,
"verifyHttpSignatures"
,
err
)
}
else
if
!
authenticated
{
ctx
.
Error
(
http
.
StatusForbidden
,
"reqSignature"
,
"request signature verification failed"
)
}
}
}
routers/api/v1/api.go
View file @
8a3c8657
...
...
@@ -602,6 +602,7 @@ func Routes(sessioner func(http.Handler) http.Handler) *web.Route {
m
.
Group
(
"/user/{username}"
,
func
()
{
m
.
Get
(
""
,
activitypub
.
Person
)
})
m
.
Post
(
"/user/{username}/inbox"
,
activitypub
.
ReqSignature
(),
activitypub
.
PersonInbox
)
})
}
m
.
Get
(
"/signing-key.gpg"
,
misc
.
SigningKey
)
...
...
templates/swagger/v1_json.tmpl
View file @
8a3c8657
...
...
@@ -28,8 +28,11 @@
"produces": [
"application/json"
],
"tags": [
"activitypub"
],
"summary": "Returns the person",
"operationId": "
informati
on",
"operationId": "
activitypubPers
on",
"parameters": [
{
"type": "string",
...
...
@@ -46,6 +49,32 @@
}
}
},
"/activitypub/user/{username}/inbox": {
"post": {
"produces": [
"application/json"
],
"tags": [
"activitypub"
],
"summary": "Send to the inbox",
"operationId": "activitypubPersonInbox",
"parameters": [
{
"type": "string",
"description": "username of the user",
"name": "username",
"in": "path",
"required": true
}
],
"responses": {
"204": {
"$ref": "#/responses/empty"
}
}
}
},
"/admin/cron": {
"get": {
"produces": [
...
...
Write
Preview
Markdown
is supported
0%
Try again
or
attach a new file
.
Attach a file
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Cancel
Please
register
or
sign in
to comment