From 1244634d14c48cb4f643e34b7f270e76d89cb491 Mon Sep 17 00:00:00 2001 From: Andrei Kurilov <18027129+akurilov@users.noreply.github.com> Date: Sat, 13 Jul 2024 11:56:36 +0300 Subject: [PATCH] fixes [skip ci] --- api/http/handler/callback.go | 9 +++++--- api/http/handler/inbox.go | 18 +++++++++++---- main.go | 8 +++---- service/activitypub/logging.go | 12 +++++----- service/activitypub/mock.go | 4 ++-- service/activitypub/service.go | 17 ++++++++------- service/activitypub/service_test.go | 3 ++- service/logging.go | 4 ++-- service/mock.go | 2 +- service/service.go | 34 +++++++++++++++-------------- service/service_test.go | 2 +- 11 files changed, 65 insertions(+), 48 deletions(-) diff --git a/api/http/handler/callback.go b/api/http/handler/callback.go index 733b5a1..4455a33 100644 --- a/api/http/handler/callback.go +++ b/api/http/handler/callback.go @@ -25,6 +25,7 @@ type CallbackHandler interface { type callbackHandler struct { topicPrefixBase string + host string svcConv converter.Service svcAp activitypub.Service } @@ -34,9 +35,10 @@ const keyHubTopic = "hub.topic" const linkSelfSuffix = ">; rel=\"self\"" const keyAckCount = "X-Ack-Count" -func NewCallbackHandler(topicPrefixBase string, svcConv converter.Service, svcAp activitypub.Service) CallbackHandler { +func NewCallbackHandler(topicPrefixBase, host string, svcConv converter.Service, svcAp activitypub.Service) CallbackHandler { return callbackHandler{ topicPrefixBase: topicPrefixBase, + host: host, svcConv: svcConv, svcAp: svcAp, } @@ -80,6 +82,7 @@ func (ch callbackHandler) Deliver(ctx *gin.Context) { ctx.String(http.StatusBadRequest, fmt.Sprintf("invalid self link header value in the request: %s", topic)) return } + pubKeyId := fmt.Sprintf("https://%s/actor/%s#main-key", ch.host, interestId) followerUrl, err := url.QueryUnescape(ctx.Query(reader.QueryParamFollower)) if err != nil || followerUrl == "" { @@ -88,7 +91,7 @@ func (ch callbackHandler) Deliver(ctx *gin.Context) { } var follower vocab.Actor - follower, _, err = ch.svcAp.FetchActor(ctx, vocab.IRI(followerUrl)) + follower, _, err = ch.svcAp.FetchActor(ctx, vocab.IRI(followerUrl), pubKeyId) if err != nil { ctx.String(http.StatusInternalServerError, fmt.Sprintf("failed to resolve the follower %s: %s", follower, err)) return @@ -120,7 +123,7 @@ func (ch callbackHandler) Deliver(ctx *gin.Context) { a, err = ch.svcConv.ConvertEventToActivity(ctx, evtProto, interestId, &follower) } if err == nil { - err = ch.svcAp.SendActivity(ctx, a, follower.Inbox.GetLink()) + err = ch.svcAp.SendActivity(ctx, a, follower.Inbox.GetLink(), pubKeyId) } if err != nil { break diff --git a/api/http/handler/inbox.go b/api/http/handler/inbox.go index dac1eb0..71160fd 100644 --- a/api/http/handler/inbox.go +++ b/api/http/handler/inbox.go @@ -21,14 +21,16 @@ import ( type inboxHandler struct { svcActivityPub activitypub.Service svc service.Service + host string } const limitReqBodyLen = 262_144 -func NewInboxHandler(svcActivityPub activitypub.Service, svc service.Service) Handler { +func NewInboxHandler(svcActivityPub activitypub.Service, svc service.Service, host string) Handler { return inboxHandler{ svcActivityPub: svcActivityPub, svc: svc, + host: host, } } @@ -65,9 +67,18 @@ func (h inboxHandler) Handle(ctx *gin.Context) { return } + actorIdLocal := ctx.Param("id") + var pubKeyId string + switch actorIdLocal { + case "": + pubKeyId = fmt.Sprintf("https://%s/actor#main-key", h.host) + default: + pubKeyId = fmt.Sprintf("https://%s/actor/%s#main-key", h.host, actorIdLocal) + } + var actor vocab.Actor var actorTags util.ObjectTags - actor, actorTags, err = h.svcActivityPub.FetchActor(ctx, activity.Actor.GetLink()) + actor, actorTags, err = h.svcActivityPub.FetchActor(ctx, activity.Actor.GetLink(), pubKeyId) if err != nil { ctx.String(http.StatusInternalServerError, err.Error()) return @@ -80,9 +91,8 @@ func (h inboxHandler) Handle(ctx *gin.Context) { return } - actorIdLocal := ctx.Param("id") var post func() - post, err = h.svc.HandleActivity(ctx, actorIdLocal, actor, actorTags, activity, tags) + post, err = h.svc.HandleActivity(ctx, actorIdLocal, pubKeyId, actor, actorTags, activity, tags) switch { case errors.Is(err, reader.ErrConflict): ctx.String(http.StatusConflict, err.Error()) diff --git a/main.go b/main.go index c410731..0033a0b 100644 --- a/main.go +++ b/main.go @@ -209,7 +209,7 @@ func main() { ha := handler.NewActorHandler(actor, actorExtraAttrs, clientAwk, "https://awakari.com/sub-details.html?id=", cfg.Api) // WebFinger - wf := apiHttp.WebFinger{ + wfDefault := apiHttp.WebFinger{ Subject: fmt.Sprintf("acct:%s@%s", cfg.Api.Actor.Name, cfg.Api.Http.Host), Links: []apiHttp.WebFingerLink{ { @@ -219,10 +219,10 @@ func main() { }, }, } - hwf := handler.NewWebFingerHandler(wf, cfg.Api.Http.Host, clientAwk) + hwf := handler.NewWebFingerHandler(wfDefault, cfg.Api.Http.Host, clientAwk) // handlers for inbox, outbox, following, followers - hi := handler.NewInboxHandler(svcActivityPub, svc) + hi := handler.NewInboxHandler(svcActivityPub, svc, cfg.Api.Http.Host) ho := handler.NewOutboxHandler(svcReader, svcConv, fmt.Sprintf("https://%s/outbox", cfg.Api.Http.Host)) hoDummy := handler.NewDummyCollectionHandler(vocab.OrderedCollectionPage{ ID: vocab.IRI(fmt.Sprintf("https://%s/outbox", cfg.Api.Http.Host)), @@ -266,7 +266,7 @@ func main() { } }() - hc := handler.NewCallbackHandler(cfg.Api.Reader.Uri, svcConv, svcActivityPub) + hc := handler.NewCallbackHandler(cfg.Api.Reader.Uri, cfg.Api.Http.Host, svcConv, svcActivityPub) log.Info(fmt.Sprintf("starting to listen the HTTP API @ port #%d...", cfg.Api.Reader.CallBack.Port)) internalCallbacks := gin.Default() diff --git a/service/activitypub/logging.go b/service/activitypub/logging.go index 1211f3b..05b0f41 100644 --- a/service/activitypub/logging.go +++ b/service/activitypub/logging.go @@ -27,15 +27,15 @@ func (l logging) ResolveActorLink(ctx context.Context, host, name string) (self return } -func (l logging) FetchActor(ctx context.Context, addr vocab.IRI) (actor vocab.Actor, tags util.ObjectTags, err error) { - actor, tags, err = l.svc.FetchActor(ctx, addr) - l.log.Log(ctx, logLevel(err), fmt.Sprintf("activitypub.FetchActor(addr=%s): {Id;%+v, Inbox:%+v, Tags:%+v}, %s", addr, actor.ID, actor.Inbox, tags, err)) +func (l logging) FetchActor(ctx context.Context, addr vocab.IRI, pubKeyId string) (actor vocab.Actor, tags util.ObjectTags, err error) { + actor, tags, err = l.svc.FetchActor(ctx, addr, pubKeyId) + l.log.Log(ctx, logLevel(err), fmt.Sprintf("activitypub.FetchActor(addr=%s, pubKeyId=%s): {Id;%+v, Inbox:%+v, Tags:%+v}, %s", addr, pubKeyId, actor.ID, actor.Inbox, tags, err)) return } -func (l logging) SendActivity(ctx context.Context, a vocab.Activity, inbox vocab.IRI) (err error) { - err = l.svc.SendActivity(ctx, a, inbox) - l.log.Log(ctx, logLevel(err), fmt.Sprintf("activitypub.SendActivity(a=%+v, inbox=%s): %s", a, inbox, err)) +func (l logging) SendActivity(ctx context.Context, a vocab.Activity, inbox vocab.IRI, pubKeyId string) (err error) { + err = l.svc.SendActivity(ctx, a, inbox, pubKeyId) + l.log.Log(ctx, logLevel(err), fmt.Sprintf("activitypub.SendActivity(a=%v, inbox=%s, pubKeyId=%s): %s", a, inbox, pubKeyId, err)) return } diff --git a/service/activitypub/mock.go b/service/activitypub/mock.go index e9a169a..1cb2cf4 100644 --- a/service/activitypub/mock.go +++ b/service/activitypub/mock.go @@ -25,7 +25,7 @@ func (m mock) ResolveActorLink(ctx context.Context, host, name string) (self voc return } -func (m mock) FetchActor(ctx context.Context, self vocab.IRI) (a vocab.Actor, tags util.ObjectTags, err error) { +func (m mock) FetchActor(ctx context.Context, self vocab.IRI, pubKeyId string) (a vocab.Actor, tags util.ObjectTags, err error) { switch self { case "https://fail.social/users/johndoe": err = ErrActorFetch @@ -53,7 +53,7 @@ func (m mock) FetchActor(ctx context.Context, self vocab.IRI) (a vocab.Actor, ta return } -func (m mock) SendActivity(ctx context.Context, a vocab.Activity, inbox vocab.IRI) (err error) { +func (m mock) SendActivity(ctx context.Context, a vocab.Activity, inbox vocab.IRI, pubKeyId string) (err error) { switch inbox { case "https://host.fail/users/johndoe/inbox": err = ErrActivitySend diff --git a/service/activitypub/service.go b/service/activitypub/service.go index 03c961d..7c1e59b 100644 --- a/service/activitypub/service.go +++ b/service/activitypub/service.go @@ -23,8 +23,8 @@ import ( type Service interface { ResolveActorLink(ctx context.Context, host, name string) (self vocab.IRI, err error) - FetchActor(ctx context.Context, addr vocab.IRI) (a vocab.Actor, tags util.ObjectTags, err error) - SendActivity(ctx context.Context, a vocab.Activity, inbox vocab.IRI) (err error) + FetchActor(ctx context.Context, addr vocab.IRI, pubKeyId string) (a vocab.Actor, tags util.ObjectTags, err error) + SendActivity(ctx context.Context, a vocab.Activity, inbox vocab.IRI, pubKeyId string) (err error) nodeinfo.Resolver } @@ -94,7 +94,7 @@ func (svc service) ResolveActorLink(ctx context.Context, host, name string) (sel return } -func (svc service) FetchActor(ctx context.Context, addr vocab.IRI) (actor vocab.Actor, tags util.ObjectTags, err error) { +func (svc service) FetchActor(ctx context.Context, addr vocab.IRI, pubKeyId string) (actor vocab.Actor, tags util.ObjectTags, err error) { // var req *http.Request req, err = http.NewRequestWithContext(ctx, http.MethodGet, string(addr), nil) @@ -112,7 +112,7 @@ func (svc service) FetchActor(ctx context.Context, addr vocab.IRI) (actor vocab. req.Header.Set("Date", now.Format(http.TimeFormat)) } // - err = svc.signRequest(req, []byte{}) + err = svc.signRequest(req, []byte{}, pubKeyId) // if err == nil { resp, err = svc.clientHttp.Do(req) @@ -137,7 +137,7 @@ func (svc service) FetchActor(ctx context.Context, addr vocab.IRI) (actor vocab. return } -func (svc service) SendActivity(ctx context.Context, a vocab.Activity, inbox vocab.IRI) (err error) { +func (svc service) SendActivity(ctx context.Context, a vocab.Activity, inbox vocab.IRI, pubKeyId string) (err error) { // aFixed, _ := apiHttp.FixContext(a) var d []byte @@ -160,7 +160,7 @@ func (svc service) SendActivity(ctx context.Context, a vocab.Activity, inbox voc req.Header.Set("Date", now.Format(http.TimeFormat)) } // - err = svc.signRequest(req, d) + err = svc.signRequest(req, d, pubKeyId) // var resp *http.Response if err == nil { @@ -182,7 +182,7 @@ func (svc service) SendActivity(ctx context.Context, a vocab.Activity, inbox voc return } -func (svc service) signRequest(req *http.Request, data []byte) (err error) { +func (svc service) signRequest(req *http.Request, data []byte, pubKeyId string) (err error) { var signer httpsig.Signer signer, _, err = httpsig.NewSigner(prefs, digestAlgorithm, headersToSign, httpsig.Signature, 120) var privKey any @@ -193,7 +193,8 @@ func (svc service) signRequest(req *http.Request, data []byte) (err error) { } } if err == nil { - err = signer.SignRequest(privKey, fmt.Sprintf("https://%s/actor#main-key", svc.hostname), req, data) + //err = signer.SignRequest(privKey, fmt.Sprintf("https://%s/actor#main-key", svc.hostname), req, data) + err = signer.SignRequest(privKey, pubKeyId, req, data) if err != nil { err = fmt.Errorf("failed to sign the follow request: %w", err) } diff --git a/service/activitypub/service_test.go b/service/activitypub/service_test.go index 98ff358..e16738b 100644 --- a/service/activitypub/service_test.go +++ b/service/activitypub/service_test.go @@ -25,7 +25,7 @@ func TestService_FetchActor(t *testing.T) { t.Skip() } svc := NewService(http.DefaultClient, "activitypub.awakari.com", []byte{}, nil) - actor, _, err := svc.FetchActor(context.TODO(), "https://mastodon.social/users/akurilov") + actor, _, err := svc.FetchActor(context.TODO(), "https://mastodon.social/users/akurilov", "https://activitypub.awakari.com/actor#main-key") assert.Equal(t, "https://mastodon.social/users/akurilov/inbox", actor.Inbox.GetLink().String()) assert.Nil(t, err) } @@ -45,6 +45,7 @@ func TestService_RequestFollow(t *testing.T) { Object: vocab.IRI("https://mastodon.social/users/akurilov"), }, "https://mastodon.social/users/akurilov/inbox", + "foo.bar#main.key", ) assert.Nil(t, err) } diff --git a/service/logging.go b/service/logging.go index 1e11386..5fea556 100644 --- a/service/logging.go +++ b/service/logging.go @@ -27,8 +27,8 @@ func (l logging) RequestFollow(ctx context.Context, addr, groupId, userId, subId return } -func (l logging) HandleActivity(ctx context.Context, actorIdLocal string, actor vocab.Actor, actorTags util.ObjectTags, activity vocab.Activity, activityTags util.ActivityTags) (post func(), err error) { - post, err = l.svc.HandleActivity(ctx, actorIdLocal, actor, actorTags, activity, activityTags) +func (l logging) HandleActivity(ctx context.Context, actorIdLocal, pubKeyId string, actor vocab.Actor, actorTags util.ObjectTags, activity vocab.Activity, activityTags util.ActivityTags) (post func(), err error) { + post, err = l.svc.HandleActivity(ctx, actorIdLocal, pubKeyId, actor, actorTags, activity, activityTags) l.log.Log(ctx, logLevel(err), fmt.Sprintf("service.HandleActivity(actorIdLocal=%s, actor.Id=%s, actor.Tags=%d, activity.Type=%s, activity.Tags=%d): err=%s", actorIdLocal, actor.ID, len(actorTags.Tag), activity.Type, len(activityTags.Tag), err)) return } diff --git a/service/mock.go b/service/mock.go index af4d877..1ec66a7 100644 --- a/service/mock.go +++ b/service/mock.go @@ -32,7 +32,7 @@ func (m mock) RequestFollow(ctx context.Context, addr, groupId, userId, subId, t return } -func (m mock) HandleActivity(ctx context.Context, actorIdLocal string, actor vocab.Actor, actorTags util.ObjectTags, activity vocab.Activity, tags util.ActivityTags) (post func(), err error) { +func (m mock) HandleActivity(ctx context.Context, actorIdLocal, pubKeyId string, actor vocab.Actor, actorTags util.ObjectTags, activity vocab.Activity, tags util.ActivityTags) (post func(), err error) { switch actor.ID { case "fail": err = storage.ErrInternal diff --git a/service/service.go b/service/service.go index 68c0ca4..ebd4641 100644 --- a/service/service.go +++ b/service/service.go @@ -26,7 +26,7 @@ type Service interface { HandleActivity( ctx context.Context, - actorIdLocal string, + actorIdLocal, pubKeyId string, actor vocab.Actor, actorTags util.ObjectTags, activity vocab.Activity, @@ -111,10 +111,12 @@ func (svc service) RequestFollow(ctx context.Context, addr, groupId, userId, sub } } + pubKeyId := fmt.Sprintf("https://%s/actor#main-key", svc.hostSelf) + var actor vocab.Actor var actorTags util.ObjectTags if err == nil { - actor, actorTags, err = svc.ap.FetchActor(ctx, vocab.IRI(addrResolved)) + actor, actorTags, err = svc.ap.FetchActor(ctx, vocab.IRI(addrResolved), pubKeyId) if err != nil { err = fmt.Errorf("%w: failed to fetch actor: %s, cause: %s", ErrInvalid, addrResolved, err) } @@ -148,7 +150,7 @@ func (svc service) RequestFollow(ctx context.Context, addr, groupId, userId, sub Actor: vocab.IRI(fmt.Sprintf("https://%s/actor", svc.hostSelf)), Object: vocab.IRI(addrResolved), } - err = svc.ap.SendActivity(ctx, activity, actor.Inbox.GetLink()) + err = svc.ap.SendActivity(ctx, activity, actor.Inbox.GetLink(), pubKeyId) if err != nil { src.Err = err.Error() _ = svc.stor.Update(ctx, src) @@ -160,7 +162,7 @@ func (svc service) RequestFollow(ctx context.Context, addr, groupId, userId, sub func (svc service) HandleActivity( ctx context.Context, - actorIdLocal string, + actorIdLocal, pubKeyId string, actor vocab.Actor, actorTags util.ObjectTags, activity vocab.Activity, @@ -172,32 +174,32 @@ func (svc service) HandleActivity( actorId := actor.ID.String() switch activity.Type { case vocab.FollowType: - post, err = svc.handleFollowActivity(ctx, actorIdLocal, actorId, activity) + post, err = svc.handleFollowActivity(ctx, actorIdLocal, pubKeyId, actorId, activity) case vocab.UndoType: err = svc.handleUndoActivity(ctx, actorIdLocal, actorId, activity) default: - err = svc.handleSourceActivity(ctx, actorId, actor, actorTags, activity, activityTags) + err = svc.handleSourceActivity(ctx, actorId, pubKeyId, actor, actorTags, activity, activityTags) } return } -func (svc service) handleFollowActivity(ctx context.Context, actorIdLocal, actorId string, activity vocab.Activity) (post func(), err error) { +func (svc service) handleFollowActivity(ctx context.Context, actorIdLocal, pubKeyId, actorId string, activity vocab.Activity) (post func(), err error) { d, _ := json.Marshal(activity) fmt.Printf("Follow activity payload: %s\n", d) cbUrl := svc.makeCallbackUrl(actorId) err = svc.r.CreateCallback(ctx, actorIdLocal, cbUrl) var actor vocab.Actor if err == nil { - actor, _, err = svc.ap.FetchActor(ctx, vocab.IRI(actorId)) + actor, _, err = svc.ap.FetchActor(ctx, vocab.IRI(actorId), pubKeyId) } if err == nil { post = func() { - time.Sleep(1 * time.Minute) // TODO tmp test code + time.Sleep(10 * time.Second) accept := vocab.AcceptNew(vocab.IRI(fmt.Sprintf("https://%s/%s", svc.hostSelf, uuid.NewString())), activity.Object) accept.Context = vocab.IRI(model.NsAs) accept.Actor = vocab.ID(fmt.Sprintf("https://%s/actor/%s", svc.hostSelf, actorIdLocal)) accept.Object = activity - _ = svc.ap.SendActivity(ctx, *accept, actor.Inbox.GetLink()) + _ = svc.ap.SendActivity(ctx, *accept, actor.Inbox.GetLink(), pubKeyId) } } return @@ -219,7 +221,7 @@ func (svc service) makeCallbackUrl(actorId string) (cbUrl string) { func (svc service) handleSourceActivity( ctx context.Context, - srcId string, + srcId, pubKeyId string, actor vocab.Actor, actorTags util.ObjectTags, activity vocab.Activity, @@ -258,7 +260,7 @@ func (svc service) handleSourceActivity( err = fmt.Errorf("%w: actor=%+v, activity.Type=%s", ErrNoAccept, actor, activity.Type) } case errors.Is(err, storage.ErrNotFound): - err = svc.unfollow(ctx, actor.ID) + err = svc.unfollow(ctx, actor.ID, pubKeyId) } return } @@ -274,16 +276,16 @@ func (svc service) List(ctx context.Context, filter model.Filter, limit uint32, } func (svc service) Unfollow(ctx context.Context, url vocab.IRI, groupId, userId string) (err error) { - err = svc.unfollow(ctx, url) + err = svc.unfollow(ctx, url, fmt.Sprintf("https://%s/actor#main-key", svc.hostSelf)) if err == nil { err = svc.stor.Delete(ctx, url.String(), groupId, userId) } return } -func (svc service) unfollow(ctx context.Context, url vocab.IRI) (err error) { +func (svc service) unfollow(ctx context.Context, url vocab.IRI, pubKeyId string) (err error) { var actor vocab.Actor - actor, _, err = svc.ap.FetchActor(ctx, url) + actor, _, err = svc.ap.FetchActor(ctx, url, pubKeyId) if err != nil { err = fmt.Errorf("%w: failed to fetch actor: %s, cause: %s", ErrInvalid, url, err) } @@ -299,7 +301,7 @@ func (svc service) unfollow(ctx context.Context, url vocab.IRI) (err error) { Object: url, }, } - err = svc.ap.SendActivity(ctx, activity, actor.Inbox.GetLink()) + err = svc.ap.SendActivity(ctx, activity, actor.Inbox.GetLink(), pubKeyId) } return } diff --git a/service/service_test.go b/service/service_test.go index 11adcb9..eab3386 100644 --- a/service/service_test.go +++ b/service/service_test.go @@ -100,7 +100,7 @@ func TestService_HandleActivity(t *testing.T) { } for k, c := range cases { t.Run(k, func(t *testing.T) { - post, err := svc.HandleActivity(context.TODO(), "", vocab.Actor{ID: c.url}, util.ObjectTags{}, c.activity, util.ActivityTags{}) + post, err := svc.HandleActivity(context.TODO(), "", "foo.bar#main.key", vocab.Actor{ID: c.url}, util.ObjectTags{}, c.activity, util.ActivityTags{}) assert.Nil(t, post) assert.ErrorIs(t, err, c.err) })