From 9cff7ebde50087d94584bb05e1ce67b86234a40a Mon Sep 17 00:00:00 2001 From: Michael Jerger Date: Tue, 9 Dec 2025 15:37:50 +0100 Subject: [PATCH] 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 Co-authored-by: Michael Jerger Co-committed-by: Michael Jerger --- models/forgefed/federationhost_repository.go | 3 ++ models/user/user_repository.go | 2 + modules/activitypub/client_test.go | 13 ++--- modules/activitypub/main_test.go | 2 +- modules/activitypub/user_settings_test.go | 9 ++-- modules/forgefed/activity_follow_test.go | 7 +-- modules/forgefed/activity_like_test.go | 23 ++++---- modules/forgefed/activity_undo_like_test.go | 23 ++++---- modules/forgefed/activity_user_activity.go | 3 -- .../forgefed/activity_user_activity_test.go | 7 +-- .../activity_validateandcheckerror_test.go | 2 +- modules/forgefed/actor_person_test.go | 53 ++++++++++--------- modules/forgefed/actor_repository_test.go | 13 ++--- modules/forgefed/actor_test.go | 20 +++---- .../object_user_activity_note_test.go | 5 +- modules/forgefed/repository_test.go | 29 +++++----- routers/api/v1/activitypub/reqsignature.go | 4 ++ services/federation/delivery_queue.go | 5 +- services/federation/signature_service.go | 11 ++++ 19 files changed, 132 insertions(+), 102 deletions(-) diff --git a/models/forgefed/federationhost_repository.go b/models/forgefed/federationhost_repository.go index 687966605f..a44b502ba1 100644 --- a/models/forgefed/federationhost_repository.go +++ b/models/forgefed/federationhost_repository.go @@ -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 } diff --git a/models/user/user_repository.go b/models/user/user_repository.go index df864746e8..f1d06abe17 100644 --- a/models/user/user_repository.go +++ b/models/user/user_repository.go @@ -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 } diff --git a/modules/activitypub/client_test.go b/modules/activitypub/client_test.go index e63d4859be..b0de25758d 100644 --- a/modules/activitypub/client_test.go +++ b/modules/activitypub/client_test.go @@ -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)) diff --git a/modules/activitypub/main_test.go b/modules/activitypub/main_test.go index 4895c85d6b..c46c1759c1 100644 --- a/modules/activitypub/main_test.go +++ b/modules/activitypub/main_test.go @@ -1,7 +1,7 @@ // Copyright 2022 The Gitea Authors. All rights reserved. // SPDX-License-Identifier: MIT -package activitypub +package activitypub_test import ( "testing" diff --git a/modules/activitypub/user_settings_test.go b/modules/activitypub/user_settings_test.go index f1a779855c..475e761e69 100644 --- a/modules/activitypub/user_settings_test.go +++ b/modules/activitypub/user_settings_test.go @@ -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) } diff --git a/modules/forgefed/activity_follow_test.go b/modules/forgefed/activity_follow_test.go index 8ba31d5f6f..18fbef33aa 100644 --- a/modules/forgefed/activity_follow_test.go +++ b/modules/forgefed/activity_follow_test.go @@ -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") diff --git a/modules/forgefed/activity_like_test.go b/modules/forgefed/activity_like_test.go index eef5563d8b..c0b565f4db 100644 --- a/modules/forgefed/activity_like_test.go +++ b/modules/forgefed/activity_like_test.go @@ -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) } diff --git a/modules/forgefed/activity_undo_like_test.go b/modules/forgefed/activity_undo_like_test.go index 76358b1669..18db688c48 100644 --- a/modules/forgefed/activity_undo_like_test.go +++ b/modules/forgefed/activity_undo_like_test.go @@ -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", diff --git a/modules/forgefed/activity_user_activity.go b/modules/forgefed/activity_user_activity.go index 0eb05cd6ec..82353245c9 100644 --- a/modules/forgefed/activity_user_activity.go +++ b/modules/forgefed/activity_user_activity.go @@ -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()...) diff --git a/modules/forgefed/activity_user_activity_test.go b/modules/forgefed/activity_user_activity_test.go index 107ae51204..49137c7ab4 100644 --- a/modules/forgefed/activity_user_activity_test.go +++ b/modules/forgefed/activity_user_activity_test.go @@ -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{ diff --git a/modules/forgefed/activity_validateandcheckerror_test.go b/modules/forgefed/activity_validateandcheckerror_test.go index c1c9164fd2..68ac4c8ffe 100644 --- a/modules/forgefed/activity_validateandcheckerror_test.go +++ b/modules/forgefed/activity_validateandcheckerror_test.go @@ -1,7 +1,7 @@ // Copyright 2023, 2024 The Forgejo Authors. All rights reserved. // SPDX-License-Identifier: MIT -package forgefed +package forgefed_test import ( "fmt" diff --git a/modules/forgefed/actor_person_test.go b/modules/forgefed/actor_person_test.go index e4f1734a9d..a5f3ee47b1 100644 --- a/modules/forgefed/actor_person_test.go +++ b/modules/forgefed/actor_person_test.go @@ -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()) } diff --git a/modules/forgefed/actor_repository_test.go b/modules/forgefed/actor_repository_test.go index 382706f387..f44c6f0b29 100644 --- a/modules/forgefed/actor_repository_test.go +++ b/modules/forgefed/actor_repository_test.go @@ -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) } diff --git a/modules/forgefed/actor_test.go b/modules/forgefed/actor_test.go index a32114616c..e190b6071c 100644 --- a/modules/forgefed/actor_test.go +++ b/modules/forgefed/actor_test.go @@ -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" diff --git a/modules/forgefed/object_user_activity_note_test.go b/modules/forgefed/object_user_activity_note_test.go index 02aebd58d3..4f790033bc 100644 --- a/modules/forgefed/object_user_activity_note_test.go +++ b/modules/forgefed/object_user_activity_note_test.go @@ -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{ { diff --git a/modules/forgefed/repository_test.go b/modules/forgefed/repository_test.go index 5aebbbc08f..05c1bc5796 100644 --- a/modules/forgefed/repository_test.go +++ b/modules/forgefed/repository_test.go @@ -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) diff --git a/routers/api/v1/activitypub/reqsignature.go b/routers/api/v1/activitypub/reqsignature.go index fd6d6520c3..2c17d953d0 100644 --- a/routers/api/v1/activitypub/reqsignature.go +++ b/routers/api/v1/activitypub/reqsignature.go @@ -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 diff --git a/services/federation/delivery_queue.go b/services/federation/delivery_queue.go index f71467e9f0..3d65a216f0 100644 --- a/services/federation/delivery_queue.go +++ b/services/federation/delivery_queue.go @@ -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") } diff --git a/services/federation/signature_service.go b/services/federation/signature_service.go index 8912db789a..25e4e270bc 100644 --- a/services/federation/signature_service.go +++ b/services/federation/signature_service.go @@ -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 }