Mark unused&immature activitypub as "not implemented" (#36789)

After many years, "activitypub" is still "in progress" and no real
progress for end users. So it is not mature.

Temporarily mark the endpoints as "501 not implemented",
and wait until the whole design is stable and usable.
This commit is contained in:
wxiaoguang
2026-03-01 20:59:49 +08:00
committed by GitHub
parent e3cf360154
commit 1592576fa5
21 changed files with 4 additions and 1083 deletions

View File

@@ -1,124 +0,0 @@
// Copyright 2022 The Gitea Authors. All rights reserved.
// SPDX-License-Identifier: MIT
package activitypub
import (
"bytes"
"context"
"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/42wim/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(ctx context.Context, user *user_model.User, pubID string) (c *Client, err error) {
if err = containsRequiredHTTPHeaders(http.MethodGet, setting.Federation.GetHeaders); err != nil {
return nil, err
} else if err = containsRequiredHTTPHeaders(http.MethodPost, setting.Federation.PostHeaders); err != nil {
return nil, err
}
priv, err := GetPrivateKey(ctx, user)
if err != nil {
return nil, err
}
privPem, _ := pem.Decode([]byte(priv))
privParsed, err := x509.ParsePKCS1PrivateKey(privPem.Bytes)
if err != nil {
return nil, err
}
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 c, err
}
// 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 nil, err
}
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)
if err != nil {
return nil, err
}
err = signer.SignRequest(c.priv, c.pubID, req, b)
return req, err
}
// 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 nil, err
}
resp, err = c.client.Do(req)
return resp, err
}

View File

@@ -1,45 +0,0 @@
// Copyright 2022 The Gitea Authors. All rights reserved.
// SPDX-License-Identifier: MIT
package activitypub
import (
"fmt"
"io"
"net/http"
"net/http/httptest"
"testing"
"code.gitea.io/gitea/models/unittest"
user_model "code.gitea.io/gitea/models/user"
"code.gitea.io/gitea/modules/setting"
"github.com/stretchr/testify/assert"
)
func TestActivityPubSignedPost(t *testing.T) {
assert.NoError(t, unittest.PrepareTestDatabase())
user := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: 1})
pubID := "https://example.com/pubID"
c, err := NewClient(t.Context(), user, pubID)
assert.NoError(t, err)
expected := "BODY"
srv := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
assert.Regexp(t, "^"+setting.Federation.DigestAlgorithm, r.Header.Get("Digest"))
assert.Contains(t, r.Header.Get("Signature"), pubID)
assert.Equal(t, ActivityStreamsContentType, r.Header.Get("Content-Type"))
body, err := io.ReadAll(r.Body)
assert.NoError(t, err)
assert.Equal(t, expected, string(body))
fmt.Fprint(w, expected)
}))
defer srv.Close()
r, err := c.Post([]byte(expected), srv.URL)
assert.NoError(t, err)
defer r.Body.Close()
body, err := io.ReadAll(r.Body)
assert.NoError(t, err)
assert.Equal(t, expected, string(body))
}

View File

@@ -1,18 +0,0 @@
// Copyright 2022 The Gitea Authors. All rights reserved.
// SPDX-License-Identifier: MIT
package activitypub
import (
"testing"
"code.gitea.io/gitea/models/unittest"
_ "code.gitea.io/gitea/models"
_ "code.gitea.io/gitea/models/actions"
_ "code.gitea.io/gitea/models/activities"
)
func TestMain(m *testing.M) {
unittest.MainTest(m)
}

View File

@@ -1,48 +0,0 @@
// Copyright 2022 The Gitea Authors. All rights reserved.
// SPDX-License-Identifier: MIT
package activitypub
import (
"context"
user_model "code.gitea.io/gitea/models/user"
"code.gitea.io/gitea/modules/util"
)
const rsaBits = 3072
// GetKeyPair function returns a user's private and public keys
func GetKeyPair(ctx context.Context, user *user_model.User) (pub, priv string, err error) {
var settings map[string]*user_model.Setting
settings, err = user_model.GetSettings(ctx, user.ID, []string{user_model.UserActivityPubPrivPem, user_model.UserActivityPubPubPem})
if err != nil {
return pub, priv, err
} else if len(settings) == 0 {
if priv, pub, err = util.GenerateKeyPair(rsaBits); err != nil {
return pub, priv, err
}
if err = user_model.SetUserSetting(ctx, user.ID, user_model.UserActivityPubPrivPem, priv); err != nil {
return pub, priv, err
}
if err = user_model.SetUserSetting(ctx, user.ID, user_model.UserActivityPubPubPem, pub); err != nil {
return pub, priv, err
}
return pub, priv, err
}
priv = settings[user_model.UserActivityPubPrivPem].SettingValue
pub = settings[user_model.UserActivityPubPubPem].SettingValue
return pub, priv, err
}
// GetPublicKey function returns a user's public key
func GetPublicKey(ctx context.Context, user *user_model.User) (pub string, err error) {
pub, _, err = GetKeyPair(ctx, user)
return pub, err
}
// GetPrivateKey function returns a user's private key
func GetPrivateKey(ctx context.Context, user *user_model.User) (priv string, err error) {
_, priv, err = GetKeyPair(ctx, user)
return priv, err
}

View File

@@ -1,28 +0,0 @@
// Copyright 2022 The Gitea Authors. All rights reserved.
// SPDX-License-Identifier: MIT
package activitypub
import (
"testing"
"code.gitea.io/gitea/models/unittest"
user_model "code.gitea.io/gitea/models/user"
_ "code.gitea.io/gitea/models" // https://forum.gitea.com/t/testfixtures-could-not-clean-table-access-no-such-table-access/4137/4
"github.com/stretchr/testify/assert"
)
func TestUserSettings(t *testing.T) {
assert.NoError(t, unittest.PrepareTestDatabase())
user1 := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: 1})
pub, priv, err := GetKeyPair(t.Context(), user1)
assert.NoError(t, err)
pub1, err := GetPublicKey(t.Context(), user1)
assert.NoError(t, err)
assert.Equal(t, pub, pub1)
priv1, err := GetPrivateKey(t.Context(), user1)
assert.NoError(t, err)
assert.Equal(t, priv, priv1)
}

View File

@@ -1,10 +0,0 @@
// Copyright 2022 The Gitea Authors. All rights reserved.
// SPDX-License-Identifier: MIT
package structs
// ActivityPub type
type ActivityPub struct {
// Context defines the JSON-LD context for ActivityPub
Context string `json:"@context"`
}