log instrumentation & test package (#10371)

This PR is part of #4767.

It contains

* add log to federation services
* separat test package for test (fix dependency cycles)

Reviewed-on: https://codeberg.org/forgejo/forgejo/pulls/10371
Reviewed-by: Mathieu Fenniak <mfenniak@noreply.codeberg.org>
Co-authored-by: Michael Jerger <michael.jerger@meissa-gmbh.de>
Co-committed-by: Michael Jerger <michael.jerger@meissa-gmbh.de>
This commit is contained in:
Michael Jerger 2025-12-09 15:37:50 +01:00 committed by Mathieu Fenniak
commit 9cff7ebde5
19 changed files with 132 additions and 102 deletions

View file

@ -8,6 +8,7 @@ import (
"fmt"
"forgejo.org/models/db"
"forgejo.org/modules/log"
"forgejo.org/modules/validation"
)
@ -16,6 +17,7 @@ func init() {
}
func GetFederationHost(ctx context.Context, ID int64) (*FederationHost, error) {
log.Trace("GetFederationHost: %v", ID)
host := new(FederationHost)
has, err := db.GetEngine(ctx).Where("id=?", ID).Get(host)
if err != nil {
@ -26,6 +28,7 @@ func GetFederationHost(ctx context.Context, ID int64) (*FederationHost, error) {
if res, err := validation.IsValid(host); !res {
return nil, err
}
log.Trace("GetFederationHost: %v, got host %v", ID, host)
return host, nil
}

View file

@ -118,6 +118,7 @@ func GetFederatedUserByUserID(ctx context.Context, userID int64) (*User, *Federa
}
func FindFederatedUserByKeyID(ctx context.Context, keyID string) (*User, *FederatedUser, error) {
log.Trace("FindFederatedUserByKeyID: %v", keyID)
federatedUser := new(FederatedUser)
user := new(User)
has, err := db.GetEngine(ctx).Where("key_id=?", keyID).Get(federatedUser)
@ -140,6 +141,7 @@ func FindFederatedUserByKeyID(ctx context.Context, keyID string) (*User, *Federa
return nil, nil, err
}
log.Trace("FindFederatedUserByKeyID: %v found user.ID %v, federated_user %v", keyID, user.ID, federatedUser)
return user, federatedUser, nil
}

View file

@ -2,7 +2,7 @@
// Copyright 2023 The Forgejo Authors. All rights reserved.
// SPDX-License-Identifier: MIT
package activitypub
package activitypub_test
import (
"fmt"
@ -15,6 +15,7 @@ import (
"forgejo.org/models/db"
"forgejo.org/models/unittest"
user_model "forgejo.org/models/user"
"forgejo.org/modules/activitypub"
"forgejo.org/modules/log"
"forgejo.org/modules/setting"
@ -23,7 +24,7 @@ import (
)
func TestCurrentTime(t *testing.T) {
date := CurrentTime()
date := activitypub.CurrentTime()
_, err := time.Parse(http.TimeFormat, date)
require.NoError(t, err)
assert.Equal(t, "GMT", date[len(date)-3:])
@ -65,7 +66,7 @@ func TestClientCtx(t *testing.T) {
require.NoError(t, unittest.PrepareTestDatabase())
user := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: 1})
pubID := "myGpgId"
cf, err := NewClientFactory()
cf, err := activitypub.NewClientFactory()
log.Debug("ClientFactory: %v\nError: %v", cf, err)
require.NoError(t, err)
@ -73,7 +74,7 @@ func TestClientCtx(t *testing.T) {
log.Debug("Client: %v\nError: %v", c, err)
require.NoError(t, err)
_ = NewContext(db.DefaultContext, cf)
_ = activitypub.NewContext(db.DefaultContext, cf)
}
/* TODO: bring this test to work or delete
@ -111,7 +112,7 @@ func TestActivityPubSignedPost(t *testing.T) {
require.NoError(t, unittest.PrepareTestDatabase())
user := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: 1})
pubID := "https://example.com/pubID"
cf, err := NewClientFactory()
cf, err := activitypub.NewClientFactory()
require.NoError(t, err)
c, err := cf.WithKeys(db.DefaultContext, user, pubID)
require.NoError(t, err)
@ -120,7 +121,7 @@ func TestActivityPubSignedPost(t *testing.T) {
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"))
assert.Equal(t, activitypub.ActivityStreamsContentType, r.Header.Get("Content-Type"))
body, err := io.ReadAll(r.Body)
require.NoError(t, err)
assert.Equal(t, expected, string(body))

View file

@ -1,7 +1,7 @@
// Copyright 2022 The Gitea Authors. All rights reserved.
// SPDX-License-Identifier: MIT
package activitypub
package activitypub_test
import (
"testing"

View file

@ -1,7 +1,7 @@
// Copyright 2022 The Gitea Authors. All rights reserved.
// SPDX-License-Identifier: MIT
package activitypub
package activitypub_test
import (
"testing"
@ -9,6 +9,7 @@ import (
"forgejo.org/models/db"
"forgejo.org/models/unittest"
user_model "forgejo.org/models/user"
"forgejo.org/modules/activitypub"
_ "forgejo.org/models" // https://forum.gitea.com/t/testfixtures-could-not-clean-table-access-no-such-table-access/4137/4
@ -19,12 +20,12 @@ import (
func TestUserSettings(t *testing.T) {
require.NoError(t, unittest.PrepareTestDatabase())
user1 := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: 1})
pub, priv, err := GetKeyPair(db.DefaultContext, user1)
pub, priv, err := activitypub.GetKeyPair(db.DefaultContext, user1)
require.NoError(t, err)
pub1, err := GetPublicKey(db.DefaultContext, user1)
pub1, err := activitypub.GetPublicKey(db.DefaultContext, user1)
require.NoError(t, err)
assert.Equal(t, pub, pub1)
priv1, err := GetPrivateKey(db.DefaultContext, user1)
priv1, err := activitypub.GetPrivateKey(db.DefaultContext, user1)
require.NoError(t, err)
assert.Equal(t, priv, priv1)
}

View file

@ -1,11 +1,12 @@
// Copyright 2025 The Forgejo Authors. All rights reserved.
// SPDX-License-Identifier: MIT
package forgefed
package forgefed_test
import (
"testing"
"forgejo.org/modules/forgefed"
"forgejo.org/modules/validation"
ap "github.com/go-ap/activitypub"
@ -13,7 +14,7 @@ import (
)
func Test_NewForgeFollowValidation(t *testing.T) {
sut := ForgeFollow{}
sut := forgefed.ForgeFollow{}
sut.Type = "Follow"
sut.Actor = ap.IRI("example.org/alice")
sut.Object = ap.IRI("example.org/bob")
@ -21,7 +22,7 @@ func Test_NewForgeFollowValidation(t *testing.T) {
valid, err := validation.IsValid(sut)
assert.True(t, valid, "sut is invalid: %v\n", err)
sut = ForgeFollow{}
sut = forgefed.ForgeFollow{}
sut.Actor = ap.IRI("example.org/alice")
sut.Object = ap.IRI("example.org/bob")

View file

@ -1,7 +1,7 @@
// Copyright 2023, 2024 The Forgejo Authors. All rights reserved.
// SPDX-License-Identifier: MIT
package forgefed
package forgefed_test
import (
"errors"
@ -10,6 +10,7 @@ import (
"testing"
"time"
"forgejo.org/modules/forgefed"
"forgejo.org/modules/validation"
ap "github.com/go-ap/activitypub"
@ -23,7 +24,7 @@ func Test_NewForgeLike(t *testing.T) {
actorIRI := "https://repo.prod.meissa.de/api/v1/activitypub/user-id/1"
objectIRI := "https://codeberg.org/api/v1/activitypub/repository-id/1"
startTime, _ := time.Parse("2006-Jan-02", "2024-Mar-07")
sut, err := NewForgeLike(actorIRI, objectIRI, startTime)
sut, err := forgefed.NewForgeLike(actorIRI, objectIRI, startTime)
require.NoError(t, err, "unexpected error: %v\n", err)
valid, _ := validation.IsValid(sut)
@ -36,18 +37,18 @@ func Test_NewForgeLike(t *testing.T) {
func Test_LikeMarshalJSON(t *testing.T) {
type testPair struct {
item ForgeLike
item forgefed.ForgeLike
want []byte
wantErr error
}
tests := map[string]testPair{
"empty": {
item: ForgeLike{},
item: forgefed.ForgeLike{},
want: nil,
},
"with ID": {
item: ForgeLike{
item: forgefed.ForgeLike{
Activity: ap.Activity{
Actor: ap.IRI("https://repo.prod.meissa.de/api/v1/activitypub/user-id/1"),
Type: "Like",
@ -70,14 +71,14 @@ func Test_LikeMarshalJSON(t *testing.T) {
func Test_LikeUnmarshalJSON(t *testing.T) {
type testPair struct {
item []byte
want *ForgeLike
want *forgefed.ForgeLike
wantErr error
}
tests := map[string]testPair{
"with ID": {
item: []byte(`{"type":"Like","actor":"https://repo.prod.meissa.de/api/activitypub/user-id/1","object":"https://codeberg.org/api/activitypub/repository-id/1"}`),
want: &ForgeLike{
want: &forgefed.ForgeLike{
Activity: ap.Activity{
Type: "Like",
Actor: ap.IRI("https://repo.prod.meissa.de/api/activitypub/user-id/1"),
@ -88,14 +89,14 @@ func Test_LikeUnmarshalJSON(t *testing.T) {
},
"invalid": {
item: []byte(`{"type":"Invalid","actor":"https://repo.prod.meissa.de/api/activitypub/user-id/1","object":"https://codeberg.org/api/activitypub/repository-id/1"`),
want: &ForgeLike{},
want: &forgefed.ForgeLike{},
wantErr: errors.New("cannot parse JSON"),
},
}
for name, test := range tests {
t.Run(name, func(t *testing.T) {
got := new(ForgeLike)
got := new(forgefed.ForgeLike)
err := got.UnmarshalJSON(test.item)
assert.False(t, (err != nil || test.wantErr != nil) && !strings.Contains(err.Error(), test.wantErr.Error()), "UnmarshalJSON()\n error: %v\n wantErr: %v", err, test.wantErr)
@ -108,7 +109,7 @@ func Test_LikeUnmarshalJSON(t *testing.T) {
func Test_ForgeLikeValidation(t *testing.T) {
// Successful
sut := new(ForgeLike)
sut := new(forgefed.ForgeLike)
sut.UnmarshalJSON([]byte(`{"type":"Like",
"actor":"https://repo.prod.meissa.de/api/activitypub/user-id/1",
"object":"https://codeberg.org/api/activitypub/repository-id/1",
@ -148,7 +149,7 @@ func Test_ForgeLikeValidation(t *testing.T) {
}
func TestActivityValidation_Attack(t *testing.T) {
sut := new(ForgeLike)
sut := new(forgefed.ForgeLike)
sut.UnmarshalJSON([]byte(`{rubbish}`))
assert.Len(t, sut.Validate(), 5)
}

View file

@ -1,7 +1,7 @@
// Copyright 2023, 2024 The Forgejo Authors. All rights reserved.
// SPDX-License-Identifier: MIT
package forgefed
package forgefed_test
import (
"errors"
@ -10,6 +10,7 @@ import (
"testing"
"time"
"forgejo.org/modules/forgefed"
"forgejo.org/modules/validation"
ap "github.com/go-ap/activitypub"
@ -26,7 +27,7 @@ func Test_NewForgeUndoLike(t *testing.T) {
`"object":"https://codeberg.org/api/v1/activitypub/repository-id/1"}}`)
startTime, _ := time.Parse("2006-Jan-02", "2024-Mar-27")
sut, err := NewForgeUndoLike(actorIRI, objectIRI, startTime)
sut, err := forgefed.NewForgeUndoLike(actorIRI, objectIRI, startTime)
if err != nil {
t.Errorf("unexpected error: %v\n", err)
}
@ -46,20 +47,20 @@ func Test_NewForgeUndoLike(t *testing.T) {
func Test_UndoLikeMarshalJSON(t *testing.T) {
type testPair struct {
item ForgeUndoLike
item forgefed.ForgeUndoLike
want []byte
wantErr error
}
startTime, _ := time.Parse("2006-Jan-02", "2024-Mar-27")
like, _ := NewForgeLike("https://repo.prod.meissa.de/api/v1/activitypub/user-id/1", "https://codeberg.org/api/v1/activitypub/repository-id/1", startTime)
like, _ := forgefed.NewForgeLike("https://repo.prod.meissa.de/api/v1/activitypub/user-id/1", "https://codeberg.org/api/v1/activitypub/repository-id/1", startTime)
tests := map[string]testPair{
"empty": {
item: ForgeUndoLike{},
item: forgefed.ForgeUndoLike{},
want: nil,
},
"valid": {
item: ForgeUndoLike{
item: forgefed.ForgeUndoLike{
Activity: ap.Activity{
StartTime: startTime,
Actor: ap.IRI("https://repo.prod.meissa.de/api/v1/activitypub/user-id/1"),
@ -95,12 +96,12 @@ func Test_UndoLikeMarshalJSON(t *testing.T) {
func Test_UndoLikeUnmarshalJSON(t *testing.T) {
type testPair struct {
item []byte
want *ForgeUndoLike
want *forgefed.ForgeUndoLike
wantErr error
}
startTime, _ := time.Parse("2006-Jan-02", "2024-Mar-27")
like, _ := NewForgeLike("https://repo.prod.meissa.de/api/v1/activitypub/user-id/1", "https://codeberg.org/api/v1/activitypub/repository-id/1", startTime)
like, _ := forgefed.NewForgeLike("https://repo.prod.meissa.de/api/v1/activitypub/user-id/1", "https://codeberg.org/api/v1/activitypub/repository-id/1", startTime)
tests := map[string]testPair{
"valid": {
@ -112,7 +113,7 @@ func Test_UndoLikeUnmarshalJSON(t *testing.T) {
`"startTime":"2024-03-27T00:00:00Z",` +
`"actor":"https://repo.prod.meissa.de/api/v1/activitypub/user-id/1",` +
`"object":"https://codeberg.org/api/v1/activitypub/repository-id/1"}}`),
want: &ForgeUndoLike{
want: &forgefed.ForgeUndoLike{
Activity: ap.Activity{
StartTime: startTime,
Actor: ap.IRI("https://repo.prod.meissa.de/api/v1/activitypub/user-id/1"),
@ -131,7 +132,7 @@ func Test_UndoLikeUnmarshalJSON(t *testing.T) {
for name, test := range tests {
t.Run(name, func(t *testing.T) {
got := new(ForgeUndoLike)
got := new(forgefed.ForgeUndoLike)
err := got.UnmarshalJSON(test.item)
if test.wantErr != nil {
if err == nil {
@ -151,7 +152,7 @@ func Test_UndoLikeUnmarshalJSON(t *testing.T) {
}
func TestActivityValidationUndo(t *testing.T) {
sut := new(ForgeUndoLike)
sut := new(forgefed.ForgeUndoLike)
_ = sut.UnmarshalJSON([]byte(`
{"type":"Undo",

View file

@ -67,9 +67,6 @@ func (userActivity ForgeUserActivity) Validate() []string {
if len(userActivity.To) == 0 {
result = append(result, "Missing to")
}
if len(userActivity.CC) == 0 {
result = append(result, "Missing cc")
}
result = append(result, userActivity.Note.Validate()...)

View file

@ -1,11 +1,12 @@
// Copyright 2025 The Forgejo Authors. All rights reserved.
// SPDX-License-Identifier: MIT
package forgefed
package forgefed_test
import (
"testing"
"forgejo.org/modules/forgefed"
"forgejo.org/modules/validation"
ap "github.com/go-ap/activitypub"
@ -13,7 +14,7 @@ import (
)
func Test_ForgeUserActivityValidation(t *testing.T) {
note := ForgeUserActivityNote{}
note := forgefed.ForgeUserActivityNote{}
note.Type = "Note"
note.Content = ap.NaturalLanguageValues{
{
@ -23,7 +24,7 @@ func Test_ForgeUserActivityValidation(t *testing.T) {
}
note.URL = ap.IRI("example.org/user-id/57")
sut := ForgeUserActivity{}
sut := forgefed.ForgeUserActivity{}
sut.Type = "Create"
sut.Actor = ap.IRI("example.org/user-id/23")
sut.CC = ap.ItemCollection{

View file

@ -1,7 +1,7 @@
// Copyright 2023, 2024 The Forgejo Authors. All rights reserved.
// SPDX-License-Identifier: MIT
package forgefed
package forgefed_test
import (
"fmt"

View file

@ -1,13 +1,14 @@
// Copyright 2023, 2024, 2025 The Forgejo Authors. All rights reserved.
// SPDX-License-Identifier: MIT
package forgefed
package forgefed_test
import (
"reflect"
"strings"
"testing"
"forgejo.org/modules/forgefed"
"forgejo.org/modules/validation"
ap "github.com/go-ap/activitypub"
@ -16,7 +17,7 @@ import (
)
func TestNewPersonIdFromModel(t *testing.T) {
expected := PersonID{}
expected := forgefed.PersonID{}
expected.ID = "1"
expected.Source = "forgejo"
expected.HostSchema = "https"
@ -26,15 +27,15 @@ func TestNewPersonIdFromModel(t *testing.T) {
expected.IsPortSupplemented = false
expected.UnvalidatedInput = "https://an.other.host:443/api/v1/activitypub/user-id/1"
sut, _ := NewPersonIDFromModel("an.other.host", "https", 443, "forgejo", "1")
sut, _ := forgefed.NewPersonIDFromModel("an.other.host", "https", 443, "forgejo", "1")
assert.Equal(t, expected, sut)
}
func TestNewPersonId(t *testing.T) {
var sut, expected PersonID
var sut, expected forgefed.PersonID
var err error
expected = PersonID{}
expected = forgefed.PersonID{}
expected.ID = "1"
expected.Source = "forgejo"
expected.HostSchema = "https"
@ -44,11 +45,11 @@ func TestNewPersonId(t *testing.T) {
expected.IsPortSupplemented = true
expected.UnvalidatedInput = "https://an.other.host/api/v1/activitypub/user-id/1"
sut, err = NewPersonID("https://an.other.host/api/v1/activitypub/user-id/1", "forgejo")
sut, err = forgefed.NewPersonID("https://an.other.host/api/v1/activitypub/user-id/1", "forgejo")
require.NoError(t, err)
assert.Equal(t, expected, sut)
expected = PersonID{}
expected = forgefed.PersonID{}
expected.ID = "1"
expected.Source = "forgejo"
expected.HostSchema = "https"
@ -58,10 +59,10 @@ func TestNewPersonId(t *testing.T) {
expected.IsPortSupplemented = false
expected.UnvalidatedInput = "https://an.other.host:443/api/v1/activitypub/user-id/1"
sut, _ = NewPersonID("https://an.other.host:443/api/v1/activitypub/user-id/1", "forgejo")
sut, _ = forgefed.NewPersonID("https://an.other.host:443/api/v1/activitypub/user-id/1", "forgejo")
assert.Equal(t, expected, sut)
expected = PersonID{}
expected = forgefed.PersonID{}
expected.ID = "1"
expected.Source = "forgejo"
expected.HostSchema = "http"
@ -71,10 +72,10 @@ func TestNewPersonId(t *testing.T) {
expected.IsPortSupplemented = false
expected.UnvalidatedInput = "http://an.other.host:80/api/v1/activitypub/user-id/1"
sut, _ = NewPersonID("http://an.other.host:80/api/v1/activitypub/user-id/1", "forgejo")
sut, _ = forgefed.NewPersonID("http://an.other.host:80/api/v1/activitypub/user-id/1", "forgejo")
assert.Equal(t, expected, sut)
expected = PersonID{}
expected = forgefed.PersonID{}
expected.ID = "1"
expected.Source = "forgejo"
expected.HostSchema = "https"
@ -84,10 +85,10 @@ func TestNewPersonId(t *testing.T) {
expected.IsPortSupplemented = false
expected.UnvalidatedInput = "https://an.other.host:443/api/v1/activitypub/user-id/1"
sut, _ = NewPersonID("HTTPS://an.other.host:443/api/v1/activitypub/user-id/1", "forgejo")
sut, _ = forgefed.NewPersonID("HTTPS://an.other.host:443/api/v1/activitypub/user-id/1", "forgejo")
assert.Equal(t, expected, sut)
expected = PersonID{}
expected = forgefed.PersonID{}
expected.ID = "@me"
expected.Source = "gotosocial"
expected.HostSchema = "https"
@ -97,13 +98,13 @@ func TestNewPersonId(t *testing.T) {
expected.IsPortSupplemented = true
expected.UnvalidatedInput = "https://an.other.host/@me"
sut, err = NewPersonID("https://an.other.host/@me", "gotosocial")
sut, err = forgefed.NewPersonID("https://an.other.host/@me", "gotosocial")
require.NoError(t, err)
assert.Equal(t, expected, sut)
}
func TestPersonIdValidation(t *testing.T) {
sut := PersonID{}
sut := forgefed.PersonID{}
sut.ID = "1"
sut.Source = "forgejo"
sut.HostSchema = "https"
@ -117,7 +118,7 @@ func TestPersonIdValidation(t *testing.T) {
assert.False(t, result)
require.EqualError(t, err, "Validation Error: forgefed.PersonID: Value path should not be empty\npath: \"\" has to be a person specific api path")
sut = PersonID{}
sut = forgefed.PersonID{}
sut.ID = "1"
sut.Source = "mastodon"
sut.HostSchema = "https"
@ -131,7 +132,7 @@ func TestPersonIdValidation(t *testing.T) {
assert.True(t, result)
require.NoError(t, err)
sut = PersonID{}
sut = forgefed.PersonID{}
sut.ID = "1"
sut.Source = "forgejo"
sut.HostSchema = "https"
@ -145,7 +146,7 @@ func TestPersonIdValidation(t *testing.T) {
assert.False(t, result)
require.EqualError(t, err, "Validation Error: forgefed.PersonID: path: \"path\" has to be a person specific api path")
sut = PersonID{}
sut = forgefed.PersonID{}
sut.ID = "1"
sut.Source = "forgejox"
sut.HostSchema = "https"
@ -161,7 +162,7 @@ func TestPersonIdValidation(t *testing.T) {
}
func TestWebfingerId(t *testing.T) {
sut, _ := NewPersonID("https://codeberg.org/api/v1/activitypub/user-id/12345", "forgejo")
sut, _ := forgefed.NewPersonID("https://codeberg.org/api/v1/activitypub/user-id/12345", "forgejo")
assert.Equal(t, "@12345@codeberg.org", sut.AsWebfinger())
}
@ -182,7 +183,7 @@ func TestShouldThrowErrorOnInvalidInput(t *testing.T) {
}
for _, tt := range tests {
_, err := NewPersonID(tt.input, tt.username)
_, err := forgefed.NewPersonID(tt.input, tt.username)
if tt.expectErr {
assert.Error(t, err, "Expected an error for input: %s", tt.input)
} else {
@ -192,7 +193,7 @@ func TestShouldThrowErrorOnInvalidInput(t *testing.T) {
}
func Test_PersonMarshalJSON(t *testing.T) {
sut := ForgePerson{}
sut := forgefed.ForgePerson{}
sut.Type = "Person"
sut.PreferredUsername = ap.NaturalLanguageValuesNew()
sut.PreferredUsername.Set("en", ap.Content("MaxMuster"))
@ -201,7 +202,7 @@ func Test_PersonMarshalJSON(t *testing.T) {
}
func Test_PersonUnmarshalJSON(t *testing.T) {
expected := &ForgePerson{
expected := &forgefed.ForgePerson{
Actor: ap.Actor{
Type: "Person",
PreferredUsername: ap.NaturalLanguageValues{
@ -209,7 +210,7 @@ func Test_PersonUnmarshalJSON(t *testing.T) {
},
},
}
sut := new(ForgePerson)
sut := new(forgefed.ForgePerson)
err := sut.UnmarshalJSON([]byte(`{"type":"Person","preferredUsername":"MaxMuster"}`))
require.NoError(t, err, "UnmarshalJSON() unexpected error: %q", err)
@ -237,16 +238,16 @@ func Test_PersonUnmarshalJSON(t *testing.T) {
}
func TestForgePersonValidation(t *testing.T) {
sut := new(ForgePerson)
sut := new(forgefed.ForgePerson)
sut.UnmarshalJSON([]byte(`{"type":"Person","preferredUsername":"MaxMuster"}`))
valid, _ := validation.IsValid(sut)
assert.True(t, valid, "sut expected to be valid: %v\n", sut.Validate())
}
func TestAsloginName(t *testing.T) {
sut, _ := NewPersonID("https://codeberg.org/api/v1/activitypub/user-id/12345", "forgejo")
sut, _ := forgefed.NewPersonID("https://codeberg.org/api/v1/activitypub/user-id/12345", "forgejo")
assert.Equal(t, "12345-codeberg.org", sut.AsLoginName())
sut, _ = NewPersonID("https://codeberg.org:443/api/v1/activitypub/user-id/12345", "forgejo")
sut, _ = forgefed.NewPersonID("https://codeberg.org:443/api/v1/activitypub/user-id/12345", "forgejo")
assert.Equal(t, "12345-codeberg.org-443", sut.AsLoginName())
}

View file

@ -1,11 +1,12 @@
// Copyright 2023, 2024, 2025 The Forgejo Authors. All rights reserved.
// SPDX-License-Identifier: MIT
package forgefed
package forgefed_test
import (
"testing"
"forgejo.org/modules/forgefed"
"forgejo.org/modules/setting"
"github.com/stretchr/testify/assert"
@ -13,11 +14,11 @@ import (
)
func TestNewRepositoryId(t *testing.T) {
var sut, expected RepositoryID
var sut, expected forgefed.RepositoryID
var err error
setting.AppURL = "http://localhost:3000/"
expected = RepositoryID{}
expected = forgefed.RepositoryID{}
expected.ID = "1"
expected.Source = "forgejo"
expected.HostSchema = "http"
@ -27,10 +28,10 @@ func TestNewRepositoryId(t *testing.T) {
expected.IsPortSupplemented = false
expected.UnvalidatedInput = "http://localhost:3000/1"
_, err = NewRepositoryID("https://an.other.host/api/v1/activitypub/user-id/1", "forgejo")
_, err = forgefed.NewRepositoryID("https://an.other.host/api/v1/activitypub/user-id/1", "forgejo")
require.EqualError(t, err, "Validation Error: forgefed.RepositoryID: path: \"api/v1/activitypub/user-id\" has to be a repo specific api path")
expected = RepositoryID{}
expected = forgefed.RepositoryID{}
expected.ID = "1"
expected.Source = "forgejo"
expected.HostSchema = "http"
@ -39,7 +40,7 @@ func TestNewRepositoryId(t *testing.T) {
expected.HostPort = 3000
expected.IsPortSupplemented = false
expected.UnvalidatedInput = "http://localhost:3000/api/activitypub/repository-id/1"
sut, err = NewRepositoryID("http://localhost:3000/api/activitypub/repository-id/1", "forgejo")
sut, err = forgefed.NewRepositoryID("http://localhost:3000/api/activitypub/repository-id/1", "forgejo")
require.NoError(t, err)
assert.Equal(t, expected, sut)
}

View file

@ -1,19 +1,21 @@
// Copyright 2023, 2024, 2025 The Forgejo Authors. All rights reserved.
// SPDX-License-Identifier: MIT
package forgefed
package forgefed_test
import (
"testing"
"forgejo.org/modules/forgefed"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
)
func TestActorNew(t *testing.T) {
sut, err := NewActorID("https://an.other.forgejo.host/api/v1/activitypub/user-id/5")
sut, err := forgefed.NewActorID("https://an.other.forgejo.host/api/v1/activitypub/user-id/5")
require.NoError(t, err)
assert.Equal(t, ActorID{
assert.Equal(t, forgefed.ActorID{
ID: "5",
HostSchema: "https",
Path: "api/v1/activitypub/user-id",
@ -23,9 +25,9 @@ func TestActorNew(t *testing.T) {
IsPortSupplemented: true,
}, sut)
sut, err = NewActorID("https://an.other.forgejo.host/api/v1/activitypub/actor")
sut, err = forgefed.NewActorID("https://an.other.forgejo.host/api/v1/activitypub/actor")
require.NoError(t, err)
assert.Equal(t, ActorID{
assert.Equal(t, forgefed.ActorID{
ID: "actor",
HostSchema: "https",
Path: "api/v1/activitypub",
@ -35,9 +37,9 @@ func TestActorNew(t *testing.T) {
IsPortSupplemented: true,
}, sut)
sut, err = NewActorID("https://an.other.gts.host/users/me")
sut, err = forgefed.NewActorID("https://an.other.gts.host/users/me")
require.NoError(t, err)
assert.Equal(t, ActorID{
assert.Equal(t, forgefed.ActorID{
ID: "me",
HostSchema: "https",
Path: "users",
@ -49,7 +51,7 @@ func TestActorNew(t *testing.T) {
}
func TestActorIdValidation(t *testing.T) {
sut := ActorID{}
sut := forgefed.ActorID{}
sut.HostSchema = "https"
sut.Path = "api/v1/activitypub/user-id"
sut.Host = "an.other.host"
@ -60,7 +62,7 @@ func TestActorIdValidation(t *testing.T) {
assert.Len(t, result, 1)
assert.Equal(t, "Value ID should not be empty", result[0])
sut = ActorID{}
sut = forgefed.ActorID{}
sut.ID = "1"
sut.HostSchema = "https"
sut.Path = "api/v1/activitypub/user-id"

View file

@ -1,11 +1,12 @@
// Copyright 2025 The Forgejo Authors. All rights reserved.
// SPDX-License-Identifier: MIT
package forgefed
package forgefed_test
import (
"testing"
"forgejo.org/modules/forgefed"
"forgejo.org/modules/validation"
ap "github.com/go-ap/activitypub"
@ -13,7 +14,7 @@ import (
)
func Test_UserActivityNoteValidation(t *testing.T) {
sut := ForgeUserActivityNote{}
sut := forgefed.ForgeUserActivityNote{}
sut.Type = "Note"
sut.Content = ap.NaturalLanguageValues{
{

View file

@ -1,13 +1,14 @@
// Copyright 2023 The Forgejo Authors. All rights reserved.
// SPDX-License-Identifier: MIT
package forgefed
package forgefed_test
import (
"fmt"
"reflect"
"testing"
"forgejo.org/modules/forgefed"
"forgejo.org/modules/json"
ap "github.com/go-ap/activitypub"
@ -15,18 +16,18 @@ import (
func Test_RepositoryMarshalJSON(t *testing.T) {
type testPair struct {
item Repository
item forgefed.Repository
want []byte
wantErr error
}
tests := map[string]testPair{
"empty": {
item: Repository{},
item: forgefed.Repository{},
want: nil,
},
"with ID": {
item: Repository{
item: forgefed.Repository{
Actor: ap.Actor{
ID: "https://example.com/1",
},
@ -35,7 +36,7 @@ func Test_RepositoryMarshalJSON(t *testing.T) {
want: []byte(`{"id":"https://example.com/1"}`),
},
"with Team as IRI": {
item: Repository{
item: forgefed.Repository{
Team: ap.IRI("https://example.com/1"),
Actor: ap.Actor{
ID: "https://example.com/1",
@ -44,7 +45,7 @@ func Test_RepositoryMarshalJSON(t *testing.T) {
want: []byte(`{"id":"https://example.com/1","team":"https://example.com/1"}`),
},
"with Team as IRIs": {
item: Repository{
item: forgefed.Repository{
Team: ap.ItemCollection{
ap.IRI("https://example.com/1"),
ap.IRI("https://example.com/2"),
@ -56,7 +57,7 @@ func Test_RepositoryMarshalJSON(t *testing.T) {
want: []byte(`{"id":"https://example.com/1","team":["https://example.com/1","https://example.com/2"]}`),
},
"with Team as Object": {
item: Repository{
item: forgefed.Repository{
Team: ap.Object{ID: "https://example.com/1"},
Actor: ap.Actor{
ID: "https://example.com/1",
@ -65,7 +66,7 @@ func Test_RepositoryMarshalJSON(t *testing.T) {
want: []byte(`{"id":"https://example.com/1","team":{"id":"https://example.com/1"}}`),
},
"with Team as slice of Objects": {
item: Repository{
item: forgefed.Repository{
Team: ap.ItemCollection{
ap.Object{ID: "https://example.com/1"},
ap.Object{ID: "https://example.com/2"},
@ -95,7 +96,7 @@ func Test_RepositoryMarshalJSON(t *testing.T) {
func Test_RepositoryUnmarshalJSON(t *testing.T) {
type testPair struct {
data []byte
want *Repository
want *forgefed.Repository
wantErr error
}
@ -110,18 +111,18 @@ func Test_RepositoryUnmarshalJSON(t *testing.T) {
},
"with Type": {
data: []byte(`{"type":"Repository"}`),
want: &Repository{
want: &forgefed.Repository{
Actor: ap.Actor{
Type: RepositoryType,
Type: forgefed.RepositoryType,
},
},
},
"with Type and ID": {
data: []byte(`{"id":"https://example.com/1","type":"Repository"}`),
want: &Repository{
want: &forgefed.Repository{
Actor: ap.Actor{
ID: "https://example.com/1",
Type: RepositoryType,
Type: forgefed.RepositoryType,
},
},
},
@ -129,7 +130,7 @@ func Test_RepositoryUnmarshalJSON(t *testing.T) {
for name, tt := range tests {
t.Run(name, func(t *testing.T) {
got := new(Repository)
got := new(forgefed.Repository)
err := got.UnmarshalJSON(tt.data)
if (err != nil || tt.wantErr != nil) && tt.wantErr.Error() != err.Error() {
t.Errorf("UnmarshalJSON() error = \"%v\", wantErr \"%v\"", err, tt.wantErr)

View file

@ -24,20 +24,24 @@ func verifyHTTPSignature(ctx app_context.APIContext) (authenticated bool, err er
// 1. Figure out what key we need to verify
v, err := httpsig.NewVerifier(r)
if err != nil {
log.Debug("For %q verification failed: %v", r.URL.Path, err)
return false, err
}
log.Debug("Verify %q, signed by KeyId: %v", r.URL.Path, v.KeyId())
signatureAlgorithm := httpsig.Algorithm(setting.Federation.SignatureAlgorithms[0])
pubKey, err := federation.FindOrCreateFederatedUserKey(ctx, v.KeyId())
if err != nil || pubKey == nil {
pubKey, err = federation.FindOrCreateFederationHostKey(ctx, v.KeyId())
if err != nil {
log.Debug("For %q verification failed: %v", r.URL.Path, err)
return false, err
}
}
err = v.Verify(pubKey, signatureAlgorithm)
if err != nil {
log.Debug("For %q verification failed: %v", r.URL.Path, err)
return false, err
}
return true, nil

View file

@ -59,16 +59,17 @@ func deliverToInbox(item deliveryQueueItem) error {
return err
}
log.Debug("Delivering %s to %s", item.Payload, item.InboxURL)
log.Trace("Delivering to: %s, signedBy: %s", item.InboxURL, item.Doer.ID)
res, err := apclient.Post(item.Payload, item.InboxURL)
if err != nil {
log.Info("Delivering to: %s failed: %s, times: %v", item.InboxURL, err, item.DeliveryCount)
return err
}
if res.StatusCode >= 400 {
defer res.Body.Close()
body, _ := io.ReadAll(io.LimitReader(res.Body, 16*1024))
log.Warn("Delivering to %s failed: %d %s, %v times", item.InboxURL, res.StatusCode, string(body), item.DeliveryCount)
log.Warn("Delivering to: %s failed. Status: %d, responseBody: %s, times: %v", item.InboxURL, res.StatusCode, string(body), item.DeliveryCount)
return fmt.Errorf("delivery failed")
}

View file

@ -16,6 +16,7 @@ import (
"forgejo.org/models/user"
"forgejo.org/modules/activitypub"
fm "forgejo.org/modules/forgefed"
"forgejo.org/modules/log"
ap "github.com/go-ap/activitypub"
)
@ -56,6 +57,7 @@ func NewActorIDFromKeyID(ctx context.Context, uri string) (fm.ActorID, error) {
}
func FindOrCreateFederatedUserKey(ctx context.Context, keyID string) (pubKey any, err error) {
log.Trace("KeyID: %v", keyID)
var federatedUser *user.FederatedUser
var keyURL *url.URL
@ -92,6 +94,7 @@ func FindOrCreateFederatedUserKey(ctx context.Context, keyID string) (pubKey any
if err != nil {
return nil, err
}
log.Trace("For KeyID %v found pubKey %v", keyID, pubKey)
return pubKey, nil
}
@ -118,12 +121,15 @@ func FindOrCreateFederatedUserKey(ctx context.Context, keyID string) (pubKey any
if err != nil {
return nil, err
}
log.Trace("For %v found pubKey %v", keyID, pubKey)
return pubKey, nil
}
log.Trace("For %v found no pubKey", keyID)
return nil, nil
}
func FindOrCreateFederationHostKey(ctx context.Context, keyID string) (pubKey any, err error) {
log.Trace("KeyID: %v", keyID)
keyURL, err := url.Parse(keyID)
if err != nil {
return nil, err
@ -152,6 +158,7 @@ func FindOrCreateFederationHostKey(ctx context.Context, keyID string) (pubKey an
if err != nil {
return nil, err
}
log.Trace("For %v found pubKey: %v", keyID, pubKey)
return pubKey, nil
}
@ -179,12 +186,15 @@ func FindOrCreateFederationHostKey(ctx context.Context, keyID string) (pubKey an
if err != nil {
return nil, err
}
log.Trace("For %v found pubKey: %v", keyID, pubKey)
return pubKey, nil
}
log.Trace("For %v found no pubKey.", keyID)
return nil, nil
}
func fetchKeyFromAp(ctx context.Context, keyURL url.URL) (pubKey any, pubKeyBytes []byte, apPerson *ap.Person, err error) {
log.Trace("keyURL %v", keyURL)
actionsUser := user.NewAPServerActor()
clientFactory, err := activitypub.GetClientFactory(ctx)
@ -223,6 +233,7 @@ func fetchKeyFromAp(ctx context.Context, keyURL url.URL) (pubKey any, pubKeyByte
return nil, nil, nil, err
}
log.Trace("For %v fetched pubKey %v", keyURL, pubKey)
return pubKey, pubKeyBytes, person, err
}