diff options
Diffstat (limited to 'vendor/codeberg.org/superseriousbusiness/activity/pub')
19 files changed, 0 insertions, 5683 deletions
diff --git a/vendor/codeberg.org/superseriousbusiness/activity/pub/README.md b/vendor/codeberg.org/superseriousbusiness/activity/pub/README.md deleted file mode 100644 index bb9a49b05..000000000 --- a/vendor/codeberg.org/superseriousbusiness/activity/pub/README.md +++ /dev/null @@ -1,270 +0,0 @@ -# pub - -Implements the Social and Federating Protocols in the ActivityPub specification. - -## Reference & Tutorial - -The [go-fed website](https://go-fed.org/) contains tutorials and reference -materials, in addition to the rest of this README. - -## How To Use - -``` -go get github.com/go-fed/activity -``` - -The root of all ActivityPub behavior is the `Actor`, which requires you to -implement a few interfaces: - -```golang -import ( - "codeberg.org/superseriousbusiness/activity/pub" -) - -type myActivityPubApp struct { /* ... */ } -type myAppsDatabase struct { /* ... */ } -type myAppsClock struct { /* ... */ } - -var ( - // Your app will implement pub.CommonBehavior, and either - // pub.SocialProtocol, pub.FederatingProtocol, or both. - myApp = &myActivityPubApp{} - myCommonBehavior pub.CommonBehavior = myApp - mySocialProtocol pub.SocialProtocol = myApp - myFederatingProtocol pub.FederatingProtocol = myApp - // Your app's database implementation. - myDatabase pub.Database = &myAppsDatabase{} - // Your app's clock. - myClock pub.Clock = &myAppsClock{} -) - -// Only support the C2S Social protocol -actor := pub.NewSocialActor( - myCommonBehavior, - mySocialProtocol, - myDatabase, - myClock) -// OR -// -// Only support S2S Federating protocol -actor = pub.NewFederatingActor( - myCommonBehavior, - myFederatingProtocol, - myDatabase, - myClock) -// OR -// -// Support both C2S Social and S2S Federating protocol. -actor = pub.NewActor( - myCommonBehavior, - mySocialProtocol, - myFederatingProtocol, - myDatabase, - myClock) -``` - -Next, hook the `Actor` into your web server: - -```golang -// The application's actor -var actor pub.Actor -var outboxHandler http.HandlerFunc = func(w http.ResponseWriter, r *http.Request) { - c := context.Background() - // Populate c with request-specific information - if handled, err := actor.PostOutbox(c, w, r); err != nil { - // Write to w - return - } else if handled { - return - } else if handled, err = actor.GetOutbox(c, w, r); err != nil { - // Write to w - return - } else if handled { - return - } - // else: - // - // Handle non-ActivityPub request, such as serving a webpage. -} -var inboxHandler http.HandlerFunc = func(w http.ResponseWriter, r *http.Request) { - c := context.Background() - // Populate c with request-specific information - if handled, err := actor.PostInbox(c, w, r); err != nil { - // Write to w - return - } else if handled { - return - } else if handled, err = actor.GetInbox(c, w, r); err != nil { - // Write to w - return - } else if handled { - return - } - // else: - // - // Handle non-ActivityPub request, such as serving a webpage. -} -// Add the handlers to a HTTP server -serveMux := http.NewServeMux() -serveMux.HandleFunc("/actor/outbox", outboxHandler) -serveMux.HandleFunc("/actor/inbox", inboxHandler) -var server http.Server -server.Handler = serveMux -``` - -To serve ActivityStreams data: - -```golang -myHander := pub.NewActivityStreamsHandler(myDatabase, myClock) -var activityStreamsHandler http.HandlerFunc = func(w http.ResponseWriter, r *http.Request) { - c := context.Background() - // Populate c with request-specific information - if handled, err := myHandler(c, w, r); err != nil { - // Write to w - return - } else if handled { - return - } - // else: - // - // Handle non-ActivityPub request, such as serving a webpage. -} -serveMux.HandleFunc("/some/data/like/a/note", activityStreamsHandler) -``` - -### Dependency Injection - -Package `pub` relies on dependency injection to provide out-of-the-box support -for ActivityPub. The interfaces to be satisfied are: - -* `CommonBehavior` - Behavior needed regardless of which Protocol is used. -* `SocialProtocol` - Behavior needed for the Social Protocol. -* `FederatingProtocol` - Behavior needed for the Federating Protocol. -* `Database` - The data store abstraction, not tied to the `database/sql` -package. -* `Clock` - The server's internal clock. -* `Transport` - Responsible for the network that serves requests and deliveries -of ActivityStreams data. A `HttpSigTransport` type is provided. - -These implementations form the core of an application's behavior without -worrying about the particulars and pitfalls of the ActivityPub protocol. -Implementing these interfaces gives you greater assurance about being -ActivityPub compliant. - -### Application Logic - -The `SocialProtocol` and `FederatingProtocol` are responsible for returning -callback functions compatible with `streams.TypeResolver`. They also return -`SocialWrappedCallbacks` and `FederatingWrappedCallbacks`, which are nothing -more than a bundle of default behaviors for types like `Create`, `Update`, and -so on. - -Applications will want to focus on implementing their specific behaviors in the -callbacks, and have fine-grained control over customization: - -```golang -// Implements the FederatingProtocol interface. -// -// This illustration can also be applied for the Social Protocol. -func (m *myAppsFederatingProtocol) Callbacks(c context.Context) (wrapped pub.FederatingWrappedCallbacks, other []interface{}) { - // The context 'c' has request-specific logic and can be used to apply complex - // logic building the right behaviors, if desired. - // - // 'c' will later be passed through to the callbacks created below. - wrapped = pub.FederatingWrappedCallbacks{ - Create: func(ctx context.Context, create vocab.ActivityStreamsCreate) error { - // This function is wrapped by default behavior. - // - // More application specific logic can be written here. - // - // 'ctx' will have request-specific information from the HTTP handler. It - // is the same as the 'c' passed to the Callbacks method. - // 'create' has, at this point, already triggered the recommended - // ActivityPub side effect behavior. The application can process it - // further as needed. - return nil - }, - } - // The 'other' must contain functions that satisfy the signature pattern - // required by streams.JSONResolver. - // - // If they are not, at runtime errors will be returned to indicate this. - other = []interface{}{ - // The FederatingWrappedCallbacks has default behavior for an "Update" type, - // but since we are providing this behavior in "other" and not in the - // FederatingWrappedCallbacks.Update member, we will entirely replace the - // default behavior provided by go-fed. Be careful that this still - // implements ActivityPub properly. - func(ctx context.Context, update vocab.ActivityStreamsUpdate) error { - // This function is NOT wrapped by default behavior. - // - // Application specific logic can be written here. - // - // 'ctx' will have request-specific information from the HTTP handler. It - // is the same as the 'c' passed to the Callbacks method. - // 'update' will NOT trigger the recommended ActivityPub side effect - // behavior. The application should do so in addition to any other custom - // side effects required. - return nil - }, - // The "Listen" type has no default suggested behavior in ActivityPub, so - // this just makes this application able to handle "Listen" activities. - func(ctx context.Context, listen vocab.ActivityStreamsListen) error { - // This function is NOT wrapped by default behavior. There's not a - // FederatingWrappedCallbacks.Listen member to wrap. - // - // Application specific logic can be written here. - // - // 'ctx' will have request-specific information from the HTTP handler. It - // is the same as the 'c' passed to the Callbacks method. - // 'listen' can be processed with side effects as the application needs. - return nil - }, - } - return -} -``` - -The `pub` package supports applications that grow into more custom solutions by -overriding the default behaviors as needed. - -### ActivityStreams Extensions: Future-Proofing An Application - -Package `pub` relies on the `streams.TypeResolver` and `streams.JSONResolver` -code generated types. As new ActivityStreams extensions are developed and their -code is generated, `pub` will automatically pick up support for these -extensions. - -The steps to rapidly implement a new extension in a `pub` application are: - -1. Generate an OWL definition of the ActivityStreams extension. This definition -could be the same one defining the vocabulary at the `@context` IRI. -2. Run `astool` to autogenerate the golang types in the `streams` package. -3. Implement the application's callbacks in the `FederatingProtocol.Callbacks` -or `SocialProtocol.Callbacks` for the new behaviors needed. -4. Build the application, which builds `pub`, with the newly generated `streams` -code. No code changes in `pub` are required. - -Whether an author of an ActivityStreams extension or an application developer, -these quick steps should reduce the barrier to adopion in a statically-typed -environment. - -### DelegateActor - -For those that need a near-complete custom ActivityPub solution, or want to have -that possibility in the future after adopting go-fed, the `DelegateActor` -interface can be used to obtain an `Actor`: - -```golang -// Use custom ActivityPub implementation -actor = pub.NewCustomActor( - myDelegateActor, - isSocialProtocolEnabled, - isFederatedProtocolEnabled, - myAppsClock) -``` - -It does not guarantee that an implementation adheres to the ActivityPub -specification. It acts as a stepping stone for applications that want to build -up to a fully custom solution and not be locked into the `pub` package -implementation. diff --git a/vendor/codeberg.org/superseriousbusiness/activity/pub/activity.go b/vendor/codeberg.org/superseriousbusiness/activity/pub/activity.go deleted file mode 100644 index 8b3d5b486..000000000 --- a/vendor/codeberg.org/superseriousbusiness/activity/pub/activity.go +++ /dev/null @@ -1,49 +0,0 @@ -package pub - -import ( - "codeberg.org/superseriousbusiness/activity/streams/vocab" -) - -// Activity represents any ActivityStreams Activity type. -// -// The Activity types provided in the streams package implement this. -type Activity interface { - // Activity is also a vocab.Type - vocab.Type - // GetActivityStreamsActor returns the "actor" property if it exists, and - // nil otherwise. - GetActivityStreamsActor() vocab.ActivityStreamsActorProperty - // GetActivityStreamsAudience returns the "audience" property if it - // exists, and nil otherwise. - GetActivityStreamsAudience() vocab.ActivityStreamsAudienceProperty - // GetActivityStreamsBcc returns the "bcc" property if it exists, and nil - // otherwise. - GetActivityStreamsBcc() vocab.ActivityStreamsBccProperty - // GetActivityStreamsBto returns the "bto" property if it exists, and nil - // otherwise. - GetActivityStreamsBto() vocab.ActivityStreamsBtoProperty - // GetActivityStreamsCc returns the "cc" property if it exists, and nil - // otherwise. - GetActivityStreamsCc() vocab.ActivityStreamsCcProperty - // GetActivityStreamsTo returns the "to" property if it exists, and nil - // otherwise. - GetActivityStreamsTo() vocab.ActivityStreamsToProperty - // GetActivityStreamsAttributedTo returns the "attributedTo" property if - // it exists, and nil otherwise. - GetActivityStreamsAttributedTo() vocab.ActivityStreamsAttributedToProperty - // GetActivityStreamsObject returns the "object" property if it exists, - // and nil otherwise. - GetActivityStreamsObject() vocab.ActivityStreamsObjectProperty - // SetActivityStreamsActor sets the "actor" property. - SetActivityStreamsActor(i vocab.ActivityStreamsActorProperty) - // SetActivityStreamsObject sets the "object" property. - SetActivityStreamsObject(i vocab.ActivityStreamsObjectProperty) - // SetActivityStreamsTo sets the "to" property. - SetActivityStreamsTo(i vocab.ActivityStreamsToProperty) - // SetActivityStreamsBto sets the "bto" property. - SetActivityStreamsBto(i vocab.ActivityStreamsBtoProperty) - // SetActivityStreamsBcc sets the "bcc" property. - SetActivityStreamsBcc(i vocab.ActivityStreamsBccProperty) - // SetActivityStreamsAttributedTo sets the "attributedTo" property. - SetActivityStreamsAttributedTo(i vocab.ActivityStreamsAttributedToProperty) -} diff --git a/vendor/codeberg.org/superseriousbusiness/activity/pub/actor.go b/vendor/codeberg.org/superseriousbusiness/activity/pub/actor.go deleted file mode 100644 index a9b19d1c1..000000000 --- a/vendor/codeberg.org/superseriousbusiness/activity/pub/actor.go +++ /dev/null @@ -1,128 +0,0 @@ -package pub - -import ( - "context" - "net/http" - "net/url" - - "codeberg.org/superseriousbusiness/activity/streams/vocab" -) - -// Actor represents ActivityPub's actor concept. It conceptually has an inbox -// and outbox that receives either a POST or GET request, which triggers side -// effects in the federating application. -// -// An Actor within an application may federate server-to-server (Federation -// Protocol), client-to-server (Social API), or both. The Actor represents the -// server in either use case. -// -// An actor can be created by calling NewSocialActor (only the Social Protocol -// is supported), NewFederatingActor (only the Federating Protocol is -// supported), NewActor (both are supported), or NewCustomActor (neither are). -// -// Not all Actors have the same behaviors depending on the constructor used to -// create them. Refer to the constructor's documentation to determine the exact -// behavior of the Actor on an application. -// -// The behaviors documented here are common to all Actors returned by any -// constructor. -type Actor interface { - // PostInbox returns true if the request was handled as an ActivityPub - // POST to an actor's inbox. If false, the request was not an - // ActivityPub request and may still be handled by the caller in - // another way, such as serving a web page. - // - // If the error is nil, then the ResponseWriter's headers and response - // has already been written. If a non-nil error is returned, then no - // response has been written. - // - // If the Actor was constructed with the Federated Protocol enabled, - // side effects will occur. - // - // If the Federated Protocol is not enabled, writes the - // http.StatusMethodNotAllowed status code in the response. No side - // effects occur. - // - // The request and data of your application will be interpreted as - // having an HTTPS protocol scheme. - PostInbox(c context.Context, w http.ResponseWriter, r *http.Request) (bool, error) - // PostInboxScheme is similar to PostInbox, except clients are able to - // specify which protocol scheme to handle the incoming request and the - // data stored within the application (HTTP, HTTPS, etc). - PostInboxScheme(c context.Context, w http.ResponseWriter, r *http.Request, scheme string) (bool, error) - // GetInbox returns true if the request was handled as an ActivityPub - // GET to an actor's inbox. If false, the request was not an ActivityPub - // request and may still be handled by the caller in another way, such - // as serving a web page. - // - // If the error is nil, then the ResponseWriter's headers and response - // has already been written. If a non-nil error is returned, then no - // response has been written. - // - // If the request is an ActivityPub request, the Actor will defer to the - // application to determine the correct authorization of the request and - // the resulting OrderedCollection to respond with. The Actor handles - // serializing this OrderedCollection and responding with the correct - // headers and http.StatusOK. - GetInbox(c context.Context, w http.ResponseWriter, r *http.Request) (bool, error) - // PostOutbox returns true if the request was handled as an ActivityPub - // POST to an actor's outbox. If false, the request was not an - // ActivityPub request and may still be handled by the caller in another - // way, such as serving a web page. - // - // If the error is nil, then the ResponseWriter's headers and response - // has already been written. If a non-nil error is returned, then no - // response has been written. - // - // If the Actor was constructed with the Social Protocol enabled, side - // effects will occur. - // - // If the Social Protocol is not enabled, writes the - // http.StatusMethodNotAllowed status code in the response. No side - // effects occur. - // - // If the Social and Federated Protocol are both enabled, it will handle - // the side effects of receiving an ActivityStream Activity, and then - // federate the Activity to peers. - // - // The request will be interpreted as having an HTTPS scheme. - PostOutbox(c context.Context, w http.ResponseWriter, r *http.Request) (bool, error) - // PostOutboxScheme is similar to PostOutbox, except clients are able to - // specify which protocol scheme to handle the incoming request and the - // data stored within the application (HTTP, HTTPS, etc). - PostOutboxScheme(c context.Context, w http.ResponseWriter, r *http.Request, scheme string) (bool, error) - // GetOutbox returns true if the request was handled as an ActivityPub - // GET to an actor's outbox. If false, the request was not an - // ActivityPub request. - // - // If the error is nil, then the ResponseWriter's headers and response - // has already been written. If a non-nil error is returned, then no - // response has been written. - // - // If the request is an ActivityPub request, the Actor will defer to the - // application to determine the correct authorization of the request and - // the resulting OrderedCollection to respond with. The Actor handles - // serializing this OrderedCollection and responding with the correct - // headers and http.StatusOK. - GetOutbox(c context.Context, w http.ResponseWriter, r *http.Request) (bool, error) -} - -// FederatingActor is an Actor that allows programmatically delivering an -// Activity to a federating peer. -type FederatingActor interface { - Actor - // Send a federated activity. - // - // The provided url must be the outbox of the sender. All processing of - // the activity occurs similarly to the C2S flow: - // - If t is not an Activity, it is wrapped in a Create activity. - // - A new ID is generated for the activity. - // - The activity is added to the specified outbox. - // - The activity is prepared and delivered to recipients. - // - // Note that this function will only behave as expected if the - // implementation has been constructed to support federation. This - // method will guaranteed work for non-custom Actors. For custom actors, - // care should be used to not call this method if only C2S is supported. - Send(c context.Context, outbox *url.URL, t vocab.Type) (Activity, error) -} diff --git a/vendor/codeberg.org/superseriousbusiness/activity/pub/base_actor.go b/vendor/codeberg.org/superseriousbusiness/activity/pub/base_actor.go deleted file mode 100644 index c509383d6..000000000 --- a/vendor/codeberg.org/superseriousbusiness/activity/pub/base_actor.go +++ /dev/null @@ -1,475 +0,0 @@ -package pub - -import ( - "context" - "encoding/json" - "fmt" - "net/http" - "net/url" - - "codeberg.org/superseriousbusiness/activity/streams" - "codeberg.org/superseriousbusiness/activity/streams/vocab" -) - -// baseActor must satisfy the Actor interface. -var _ Actor = &baseActor{} - -// baseActor is an application-independent ActivityPub implementation. It does -// not implement the entire protocol, and relies on a delegate to do so. It -// only implements the part of the protocol that is side-effect-free, allowing -// an existing application to write a DelegateActor that glues their application -// into the ActivityPub world. -// -// It is preferred to use a DelegateActor provided by this library, so that the -// application does not need to worry about the ActivityPub implementation. -type baseActor struct { - // delegate contains application-specific delegation logic. - delegate DelegateActor - // enableSocialProtocol enables or disables the Social API, the client to - // server part of ActivityPub. Useful if permitting remote clients to - // act on behalf of the users of the client application. - enableSocialProtocol bool - // enableFederatedProtocol enables or disables the Federated Protocol, or the - // server to server part of ActivityPub. Useful to permit integrating - // with the rest of the federative web. - enableFederatedProtocol bool - // clock simply tracks the current time. - clock Clock -} - -// baseActorFederating must satisfy the FederatingActor interface. -var _ FederatingActor = &baseActorFederating{} - -// baseActorFederating is a baseActor that also satisfies the FederatingActor -// interface. -// -// The baseActor is preserved as an Actor which will not successfully cast to a -// FederatingActor. -type baseActorFederating struct { - baseActor -} - -// NewSocialActor builds a new Actor concept that handles only the Social -// Protocol part of ActivityPub. -// -// This Actor can be created once in an application and reused to handle -// multiple requests concurrently and for different endpoints. -// -// It leverages as much of go-fed as possible to ensure the implementation is -// compliant with the ActivityPub specification, while providing enough freedom -// to be productive without shooting one's self in the foot. -// -// Do not try to use NewSocialActor and NewFederatingActor together to cover -// both the Social and Federating parts of the protocol. Instead, use NewActor. -func NewSocialActor(c CommonBehavior, - c2s SocialProtocol, - db Database, - clock Clock) Actor { - return &baseActor{ - // Use SideEffectActor without s2s. - delegate: NewSideEffectActor(c, nil, c2s, db, clock), - enableSocialProtocol: true, - clock: clock, - } -} - -// NewFederatingActor builds a new Actor concept that handles only the Federating -// Protocol part of ActivityPub. -// -// This Actor can be created once in an application and reused to handle -// multiple requests concurrently and for different endpoints. -// -// It leverages as much of go-fed as possible to ensure the implementation is -// compliant with the ActivityPub specification, while providing enough freedom -// to be productive without shooting one's self in the foot. -// -// Do not try to use NewSocialActor and NewFederatingActor together to cover -// both the Social and Federating parts of the protocol. Instead, use NewActor. -func NewFederatingActor(c CommonBehavior, - s2s FederatingProtocol, - db Database, - clock Clock) FederatingActor { - return &baseActorFederating{ - baseActor{ - // Use SideEffectActor without c2s. - delegate: NewSideEffectActor(c, s2s, nil, db, clock), - enableFederatedProtocol: true, - clock: clock, - }, - } -} - -// NewActor builds a new Actor concept that handles both the Social and -// Federating Protocol parts of ActivityPub. -// -// This Actor can be created once in an application and reused to handle -// multiple requests concurrently and for different endpoints. -// -// It leverages as much of go-fed as possible to ensure the implementation is -// compliant with the ActivityPub specification, while providing enough freedom -// to be productive without shooting one's self in the foot. -func NewActor(c CommonBehavior, - c2s SocialProtocol, - s2s FederatingProtocol, - db Database, - clock Clock) FederatingActor { - return &baseActorFederating{ - baseActor{ - delegate: NewSideEffectActor(c, s2s, c2s, db, clock), - enableSocialProtocol: true, - enableFederatedProtocol: true, - clock: clock, - }, - } -} - -// NewCustomActor allows clients to create a custom ActivityPub implementation -// for the Social Protocol, Federating Protocol, or both. -// -// It still uses the library as a high-level scaffold, which has the benefit of -// allowing applications to grow into a custom ActivityPub solution without -// having to refactor the code that passes HTTP requests into the Actor. -// -// It is possible to create a DelegateActor that is not ActivityPub compliant. -// Use with due care. -// -// If you find yourself passing a SideEffectActor in as the DelegateActor, -// consider using NewActor, NewFederatingActor, or NewSocialActor instead. -func NewCustomActor(delegate DelegateActor, - enableSocialProtocol, enableFederatedProtocol bool, - clock Clock) FederatingActor { - return &baseActorFederating{ - baseActor{ - delegate: delegate, - enableSocialProtocol: enableSocialProtocol, - enableFederatedProtocol: enableFederatedProtocol, - clock: clock, - }, - } -} - -// PostInbox implements the generic algorithm for handling a POST request to an -// actor's inbox independent on an application. It relies on a delegate to -// implement application specific functionality. -// -// Only supports serving data with identifiers having the HTTPS scheme. -func (b *baseActor) PostInbox(c context.Context, w http.ResponseWriter, r *http.Request) (bool, error) { - return b.PostInboxScheme(c, w, r, "https") -} - -// PostInbox implements the generic algorithm for handling a POST request to an -// actor's inbox independent on an application. It relies on a delegate to -// implement application specific functionality. -// -// Specifying the "scheme" allows for retrieving ActivityStreams content with -// identifiers such as HTTP, HTTPS, or other protocol schemes. -func (b *baseActor) PostInboxScheme(c context.Context, w http.ResponseWriter, r *http.Request, scheme string) (bool, error) { - // Do nothing if it is not an ActivityPub POST request. - if !isActivityPubPost(r) { - return false, nil - } - // If the Federated Protocol is not enabled, then this endpoint is not - // enabled. - if !b.enableFederatedProtocol { - w.WriteHeader(http.StatusMethodNotAllowed) - return true, nil - } - // Check the peer request is authentic. - c, authenticated, err := b.delegate.AuthenticatePostInbox(c, w, r) - if err != nil { - return true, err - } else if !authenticated { - return true, nil - } - // Begin processing the request, but have not yet applied - // authorization (ex: blocks). Obtain the activity reject unknown - // activities. - m, err := readActivityPubReq(r) - if err != nil { - return true, err - } - asValue, err := streams.ToType(c, m) - if err != nil && !streams.IsUnmatchedErr(err) { - return true, err - } else if streams.IsUnmatchedErr(err) { - // Respond with bad request -- we do not understand the type. - w.WriteHeader(http.StatusBadRequest) - return true, nil - } - activity, ok := asValue.(Activity) - if !ok { - return true, fmt.Errorf("activity streams value is not an Activity: %T", asValue) - } - if activity.GetJSONLDId() == nil { - w.WriteHeader(http.StatusBadRequest) - return true, nil - } - // Allow server implementations to set context data with a hook. - c, err = b.delegate.PostInboxRequestBodyHook(c, r, activity) - if err != nil { - return true, err - } - // Check authorization of the activity. - authorized, err := b.delegate.AuthorizePostInbox(c, w, activity) - if err != nil { - return true, err - } else if !authorized { - return true, nil - } - // Post the activity to the actor's inbox and trigger side effects for - // that particular Activity type. It is up to the delegate to resolve - // the given map. - inboxId := requestId(r, scheme) - err = b.delegate.PostInbox(c, inboxId, activity) - if err != nil { - // Special case: We know it is a bad request if the object or - // target properties needed to be populated, but weren't. - // - // Send the rejection to the peer. - if err == ErrObjectRequired || err == ErrTargetRequired { - w.WriteHeader(http.StatusBadRequest) - return true, nil - } - return true, err - } - // Our side effects are complete, now delegate determining whether to - // do inbox forwarding, as well as the action to do it. - if err := b.delegate.InboxForwarding(c, inboxId, activity); err != nil { - return true, err - } - // Request has been processed. Begin responding to the request. - // - // Simply respond with an OK status to the peer. - w.WriteHeader(http.StatusOK) - return true, nil -} - -// GetInbox implements the generic algorithm for handling a GET request to an -// actor's inbox independent on an application. It relies on a delegate to -// implement application specific functionality. -func (b *baseActor) GetInbox(c context.Context, w http.ResponseWriter, r *http.Request) (bool, error) { - // Do nothing if it is not an ActivityPub GET request. - if !isActivityPubGet(r) { - return false, nil - } - // Delegate authenticating and authorizing the request. - c, authenticated, err := b.delegate.AuthenticateGetInbox(c, w, r) - if err != nil { - return true, err - } else if !authenticated { - return true, nil - } - // Everything is good to begin processing the request. - oc, err := b.delegate.GetInbox(c, r) - if err != nil { - return true, err - } - // Deduplicate the 'orderedItems' property by ID. - err = dedupeOrderedItems(oc) - if err != nil { - return true, err - } - // Request has been processed. Begin responding to the request. - // - // Serialize the OrderedCollection. - m, err := streams.Serialize(oc) - if err != nil { - return true, err - } - raw, err := json.Marshal(m) - if err != nil { - return true, err - } - // Write the response. - addResponseHeaders(w.Header(), b.clock, raw) - w.WriteHeader(http.StatusOK) - n, err := w.Write(raw) - if err != nil { - return true, err - } else if n != len(raw) { - return true, fmt.Errorf("ResponseWriter.Write wrote %d of %d bytes", n, len(raw)) - } - return true, nil -} - -// PostOutbox implements the generic algorithm for handling a POST request to an -// actor's outbox independent on an application. It relies on a delegate to -// implement application specific functionality. -// -// Only supports serving data with identifiers having the HTTPS scheme. -func (b *baseActor) PostOutbox(c context.Context, w http.ResponseWriter, r *http.Request) (bool, error) { - return b.PostOutboxScheme(c, w, r, "https") -} - -// PostOutbox implements the generic algorithm for handling a POST request to an -// actor's outbox independent on an application. It relies on a delegate to -// implement application specific functionality. -// -// Specifying the "scheme" allows for retrieving ActivityStreams content with -// identifiers such as HTTP, HTTPS, or other protocol schemes. -func (b *baseActor) PostOutboxScheme(c context.Context, w http.ResponseWriter, r *http.Request, scheme string) (bool, error) { - // Do nothing if it is not an ActivityPub POST request. - if !isActivityPubPost(r) { - return false, nil - } - // If the Social API is not enabled, then this endpoint is not enabled. - if !b.enableSocialProtocol { - w.WriteHeader(http.StatusMethodNotAllowed) - return true, nil - } - // Delegate authenticating and authorizing the request. - c, authenticated, err := b.delegate.AuthenticatePostOutbox(c, w, r) - if err != nil { - return true, err - } else if !authenticated { - return true, nil - } - // Everything is good to begin processing the request. - m, err := readActivityPubReq(r) - if err != nil { - return true, err - } - // Note that converting to a Type will NOT successfully convert types - // not known to go-fed. This prevents accidentally wrapping an Activity - // type unknown to go-fed in a Create below. Instead, - // streams.ErrUnhandledType will be returned here. - asValue, err := streams.ToType(c, m) - if err != nil && !streams.IsUnmatchedErr(err) { - return true, err - } else if streams.IsUnmatchedErr(err) { - // Respond with bad request -- we do not understand the type. - w.WriteHeader(http.StatusBadRequest) - return true, nil - } - // Allow server implementations to set context data with a hook. - c, err = b.delegate.PostOutboxRequestBodyHook(c, r, asValue) - if err != nil { - return true, err - } - // The HTTP request steps are complete, complete the rest of the outbox - // and delivery process. - outboxId := requestId(r, scheme) - activity, err := b.deliver(c, outboxId, asValue, m) - // Special case: We know it is a bad request if the object or - // target properties needed to be populated, but weren't. - // - // Send the rejection to the client. - if err == ErrObjectRequired || err == ErrTargetRequired { - w.WriteHeader(http.StatusBadRequest) - return true, nil - } else if err != nil { - return true, err - } - // Respond to the request with the new Activity's IRI location. - w.Header().Set(locationHeader, activity.GetJSONLDId().Get().String()) - w.WriteHeader(http.StatusCreated) - return true, nil -} - -// GetOutbox implements the generic algorithm for handling a Get request to an -// actor's outbox independent on an application. It relies on a delegate to -// implement application specific functionality. -func (b *baseActor) GetOutbox(c context.Context, w http.ResponseWriter, r *http.Request) (bool, error) { - // Do nothing if it is not an ActivityPub GET request. - if !isActivityPubGet(r) { - return false, nil - } - // Delegate authenticating and authorizing the request. - c, authenticated, err := b.delegate.AuthenticateGetOutbox(c, w, r) - if err != nil { - return true, err - } else if !authenticated { - return true, nil - } - // Everything is good to begin processing the request. - oc, err := b.delegate.GetOutbox(c, r) - if err != nil { - return true, err - } - // Request has been processed. Begin responding to the request. - // - // Serialize the OrderedCollection. - m, err := streams.Serialize(oc) - if err != nil { - return true, err - } - raw, err := json.Marshal(m) - if err != nil { - return true, err - } - // Write the response. - addResponseHeaders(w.Header(), b.clock, raw) - w.WriteHeader(http.StatusOK) - n, err := w.Write(raw) - if err != nil { - return true, err - } else if n != len(raw) { - return true, fmt.Errorf("ResponseWriter.Write wrote %d of %d bytes", n, len(raw)) - } - return true, nil -} - -// deliver delegates all outbox handling steps and optionally will federate the -// activity if the federated protocol is enabled. -// -// This function is not exported so an Actor that only supports C2S cannot be -// type casted to a FederatingActor. It doesn't exactly fit the Send method -// signature anyways. -// -// Note: 'm' is nilable. -func (b *baseActor) deliver(c context.Context, outbox *url.URL, asValue vocab.Type, m map[string]interface{}) (activity Activity, err error) { - // If the value is not an Activity or type extending from Activity, then - // we need to wrap it in a Create Activity. - if !streams.IsOrExtendsActivityStreamsActivity(asValue) { - asValue, err = b.delegate.WrapInCreate(c, asValue, outbox) - if err != nil { - return - } - } - // At this point, this should be a safe conversion. If this error is - // triggered, then there is either a bug in the delegation of - // WrapInCreate, behavior is not lining up in the generated ExtendedBy - // code, or something else is incorrect with the type system. - var ok bool - activity, ok = asValue.(Activity) - if !ok { - err = fmt.Errorf("activity streams value is not an Activity: %T", asValue) - return - } - // Delegate generating new IDs for the activity and all new objects. - if err = b.delegate.AddNewIDs(c, activity); err != nil { - return - } - // Post the activity to the actor's outbox and trigger side effects for - // that particular Activity type. - // - // Since 'm' is nil-able and side effects may need access to literal nil - // values, such as for Update activities, ensure 'm' is non-nil. - if m == nil { - m, err = asValue.Serialize() - if err != nil { - return - } - } - deliverable, err := b.delegate.PostOutbox(c, activity, outbox, m) - if err != nil { - return - } - // Request has been processed and all side effects internal to this - // application server have finished. Begin side effects affecting other - // servers and/or the client who sent this request. - // - // If we are federating and the type is a deliverable one, then deliver - // the activity to federating peers. - if b.enableFederatedProtocol && deliverable { - if err = b.delegate.Deliver(c, outbox, activity); err != nil { - return - } - } - return -} - -// Send is programmatically accessible if the federated protocol is enabled. -func (b *baseActorFederating) Send(c context.Context, outbox *url.URL, t vocab.Type) (Activity, error) { - return b.deliver(c, outbox, t, nil) -} diff --git a/vendor/codeberg.org/superseriousbusiness/activity/pub/clock.go b/vendor/codeberg.org/superseriousbusiness/activity/pub/clock.go deleted file mode 100644 index bf19e49f7..000000000 --- a/vendor/codeberg.org/superseriousbusiness/activity/pub/clock.go +++ /dev/null @@ -1,11 +0,0 @@ -package pub - -import ( - "time" -) - -// Clock determines the time. -type Clock interface { - // Now returns the current time. - Now() time.Time -} diff --git a/vendor/codeberg.org/superseriousbusiness/activity/pub/common_behavior.go b/vendor/codeberg.org/superseriousbusiness/activity/pub/common_behavior.go deleted file mode 100644 index e33dce842..000000000 --- a/vendor/codeberg.org/superseriousbusiness/activity/pub/common_behavior.go +++ /dev/null @@ -1,90 +0,0 @@ -package pub - -import ( - "context" - "net/http" - "net/url" - - "codeberg.org/superseriousbusiness/activity/streams/vocab" -) - -// Common contains functions required for both the Social API and Federating -// Protocol. -// -// It is passed to the library as a dependency injection from the client -// application. -type CommonBehavior interface { - // AuthenticateGetInbox delegates the authentication of a GET to an - // inbox. - // - // Always called, regardless whether the Federated Protocol or Social - // API is enabled. - // - // If an error is returned, it is passed back to the caller of - // GetInbox. In this case, the implementation must not write a - // response to the ResponseWriter as is expected that the client will - // do so when handling the error. The 'authenticated' is ignored. - // - // If no error is returned, but authentication or authorization fails, - // then authenticated must be false and error nil. It is expected that - // the implementation handles writing to the ResponseWriter in this - // case. - // - // Finally, if the authentication and authorization succeeds, then - // authenticated must be true and error nil. The request will continue - // to be processed. - AuthenticateGetInbox(c context.Context, w http.ResponseWriter, r *http.Request) (out context.Context, authenticated bool, err error) - // AuthenticateGetOutbox delegates the authentication of a GET to an - // outbox. - // - // Always called, regardless whether the Federated Protocol or Social - // API is enabled. - // - // If an error is returned, it is passed back to the caller of - // GetOutbox. In this case, the implementation must not write a - // response to the ResponseWriter as is expected that the client will - // do so when handling the error. The 'authenticated' is ignored. - // - // If no error is returned, but authentication or authorization fails, - // then authenticated must be false and error nil. It is expected that - // the implementation handles writing to the ResponseWriter in this - // case. - // - // Finally, if the authentication and authorization succeeds, then - // authenticated must be true and error nil. The request will continue - // to be processed. - AuthenticateGetOutbox(c context.Context, w http.ResponseWriter, r *http.Request) (out context.Context, authenticated bool, err error) - // GetOutbox returns the OrderedCollection inbox of the actor for this - // context. It is up to the implementation to provide the correct - // collection for the kind of authorization given in the request. - // - // AuthenticateGetOutbox will be called prior to this. - // - // Always called, regardless whether the Federated Protocol or Social - // API is enabled. - GetOutbox(c context.Context, r *http.Request) (vocab.ActivityStreamsOrderedCollectionPage, error) - // NewTransport returns a new Transport on behalf of a specific actor. - // - // The actorBoxIRI will be either the inbox or outbox of an actor who is - // attempting to do the dereferencing or delivery. Any authentication - // scheme applied on the request must be based on this actor. The - // request must contain some sort of credential of the user, such as a - // HTTP Signature. - // - // The gofedAgent passed in should be used by the Transport - // implementation in the User-Agent, as well as the application-specific - // user agent string. The gofedAgent will indicate this library's use as - // well as the library's version number. - // - // Any server-wide rate-limiting that needs to occur should happen in a - // Transport implementation. This factory function allows this to be - // created, so peer servers are not DOS'd. - // - // Any retry logic should also be handled by the Transport - // implementation. - // - // Note that the library will not maintain a long-lived pointer to the - // returned Transport so that any private credentials are able to be - // garbage collected. - NewTransport(c context.Context, actorBoxIRI *url.URL, gofedAgent string) (t Transport, err error) -} diff --git a/vendor/codeberg.org/superseriousbusiness/activity/pub/database.go b/vendor/codeberg.org/superseriousbusiness/activity/pub/database.go deleted file mode 100644 index 5e25455d5..000000000 --- a/vendor/codeberg.org/superseriousbusiness/activity/pub/database.go +++ /dev/null @@ -1,152 +0,0 @@ -package pub - -import ( - "context" - "net/url" - - "codeberg.org/superseriousbusiness/activity/streams/vocab" -) - -type Database interface { - // Lock takes a lock for the object at the specified id. If an error - // is returned, the lock must not have been taken. - // - // The lock must be able to succeed for an id that does not exist in - // the database. This means acquiring the lock does not guarantee the - // entry exists in the database. - // - // Locks are encouraged to be lightweight and in the Go layer, as some - // processes require tight loops acquiring and releasing locks. - // - // Used to ensure race conditions in multiple requests do not occur. - Lock(c context.Context, id *url.URL) (unlock func(), err error) - // InboxContains returns true if the OrderedCollection at 'inbox' - // contains the specified 'id'. - // - // The library makes this call only after acquiring a lock first. - InboxContains(c context.Context, inbox, id *url.URL) (contains bool, err error) - // GetInbox returns the first ordered collection page of the outbox at - // the specified IRI, for prepending new items. - // - // The library makes this call only after acquiring a lock first. - GetInbox(c context.Context, inboxIRI *url.URL) (inbox vocab.ActivityStreamsOrderedCollectionPage, err error) - // SetInbox saves the inbox value given from GetInbox, with new items - // prepended. Note that the new items must not be added as independent - // database entries. Separate calls to Create will do that. - // - // The library makes this call only after acquiring a lock first. - SetInbox(c context.Context, inbox vocab.ActivityStreamsOrderedCollectionPage) error - // Owns returns true if the database has an entry for the IRI and it - // exists in the database. - // - // The library makes this call only after acquiring a lock first. - Owns(c context.Context, id *url.URL) (owns bool, err error) - // ActorForOutbox fetches the actor's IRI for the given outbox IRI. - // - // The library makes this call only after acquiring a lock first. - ActorForOutbox(c context.Context, outboxIRI *url.URL) (actorIRI *url.URL, err error) - // ActorForInbox fetches the actor's IRI for the given outbox IRI. - // - // The library makes this call only after acquiring a lock first. - ActorForInbox(c context.Context, inboxIRI *url.URL) (actorIRI *url.URL, err error) - // OutboxForInbox fetches the corresponding actor's outbox IRI for the - // actor's inbox IRI. - // - // The library makes this call only after acquiring a lock first. - OutboxForInbox(c context.Context, inboxIRI *url.URL) (outboxIRI *url.URL, err error) - // InboxesForIRI fetches inboxes corresponding to the given iri. - // This allows your server to skip remote dereferencing of iris - // in order to speed up message delivery, if desired. - // - // It is acceptable to just return nil or an empty slice for the inboxIRIs, - // if you don't know the inbox iri, or you don't wish to use this feature. - // In this case, the library will attempt to resolve inboxes of the iri - // by remote dereferencing instead. - // - // If the input iri is the iri of an Actor, then the inbox for the actor - // should be returned as a single-entry slice. - // - // If the input iri is a Collection (such as a Collection of followers), - // then each follower inbox IRI should be returned in the inboxIRIs slice. - // - // The library makes this call only after acquiring a lock first. - InboxesForIRI(c context.Context, iri *url.URL) (inboxIRIs []*url.URL, err error) - // Exists returns true if the database has an entry for the specified - // id. It may not be owned by this application instance. - // - // The library makes this call only after acquiring a lock first. - Exists(c context.Context, id *url.URL) (exists bool, err error) - // Get returns the database entry for the specified id. - // - // The library makes this call only after acquiring a lock first. - Get(c context.Context, id *url.URL) (value vocab.Type, err error) - // Create adds a new entry to the database which must be able to be - // keyed by its id. - // - // Note that Activity values received from federated peers may also be - // created in the database this way if the Federating Protocol is - // enabled. The client may freely decide to store only the id instead of - // the entire value. - // - // The library makes this call only after acquiring a lock first. - // - // Under certain conditions and network activities, Create may be called - // multiple times for the same ActivityStreams object. - Create(c context.Context, asType vocab.Type) error - // Update sets an existing entry to the database based on the value's - // id. - // - // Note that Activity values received from federated peers may also be - // updated in the database this way if the Federating Protocol is - // enabled. The client may freely decide to store only the id instead of - // the entire value. - // - // The library makes this call only after acquiring a lock first. - Update(c context.Context, asType vocab.Type) error - // Delete removes the entry with the given id. - // - // Delete is only called for federated objects. Deletes from the Social - // Protocol instead call Update to create a Tombstone. - // - // The library makes this call only after acquiring a lock first. - Delete(c context.Context, id *url.URL) error - // GetOutbox returns the first ordered collection page of the outbox - // at the specified IRI, for prepending new items. - // - // The library makes this call only after acquiring a lock first. - GetOutbox(c context.Context, outboxIRI *url.URL) (outbox vocab.ActivityStreamsOrderedCollectionPage, err error) - // SetOutbox saves the outbox value given from GetOutbox, with new items - // prepended. Note that the new items must not be added as independent - // database entries. Separate calls to Create will do that. - // - // The library makes this call only after acquiring a lock first. - SetOutbox(c context.Context, outbox vocab.ActivityStreamsOrderedCollectionPage) error - // NewID creates a new IRI id for the provided activity or object. The - // implementation does not need to set the 'id' property and simply - // needs to determine the value. - // - // The go-fed library will handle setting the 'id' property on the - // activity or object provided with the value returned. - NewID(c context.Context, t vocab.Type) (id *url.URL, err error) - // Followers obtains the Followers Collection for an actor with the - // given id. - // - // If modified, the library will then call Update. - // - // The library makes this call only after acquiring a lock first. - Followers(c context.Context, actorIRI *url.URL) (followers vocab.ActivityStreamsCollection, err error) - // Following obtains the Following Collection for an actor with the - // given id. - // - // If modified, the library will then call Update. - // - // The library makes this call only after acquiring a lock first. - Following(c context.Context, actorIRI *url.URL) (following vocab.ActivityStreamsCollection, err error) - // Liked obtains the Liked Collection for an actor with the - // given id. - // - // If modified, the library will then call Update. - // - // The library makes this call only after acquiring a lock first. - Liked(c context.Context, actorIRI *url.URL) (liked vocab.ActivityStreamsCollection, err error) -} diff --git a/vendor/codeberg.org/superseriousbusiness/activity/pub/delegate_actor.go b/vendor/codeberg.org/superseriousbusiness/activity/pub/delegate_actor.go deleted file mode 100644 index 0250f62ee..000000000 --- a/vendor/codeberg.org/superseriousbusiness/activity/pub/delegate_actor.go +++ /dev/null @@ -1,249 +0,0 @@ -package pub - -import ( - "context" - "net/http" - "net/url" - - "codeberg.org/superseriousbusiness/activity/streams/vocab" -) - -// DelegateActor contains the detailed interface an application must satisfy in -// order to implement the ActivityPub specification. -// -// Note that an implementation of this interface is implicitly provided in the -// calls to NewActor, NewSocialActor, and NewFederatingActor. -// -// Implementing the DelegateActor requires familiarity with the ActivityPub -// specification because it does not a strong enough abstraction for the client -// application to ignore the ActivityPub spec. It is very possible to implement -// this interface and build a foot-gun that trashes the fediverse without being -// ActivityPub compliant. Please use with due consideration. -// -// Alternatively, build an application that uses the parts of the pub library -// that do not require implementing a DelegateActor so that the ActivityPub -// implementation is completely provided out of the box. -type DelegateActor interface { - // Hook callback after parsing the request body for a federated request - // to the Actor's inbox. - // - // Can be used to set contextual information based on the Activity - // received. - // - // Only called if the Federated Protocol is enabled. - // - // Warning: Neither authentication nor authorization has taken place at - // this time. Doing anything beyond setting contextual information is - // strongly discouraged. - // - // If an error is returned, it is passed back to the caller of - // PostInbox. In this case, the DelegateActor implementation must not - // write a response to the ResponseWriter as is expected that the caller - // to PostInbox will do so when handling the error. - PostInboxRequestBodyHook(c context.Context, r *http.Request, activity Activity) (context.Context, error) - // Hook callback after parsing the request body for a client request - // to the Actor's outbox. - // - // Can be used to set contextual information based on the - // ActivityStreams object received. - // - // Only called if the Social API is enabled. - // - // Warning: Neither authentication nor authorization has taken place at - // this time. Doing anything beyond setting contextual information is - // strongly discouraged. - // - // If an error is returned, it is passed back to the caller of - // PostOutbox. In this case, the DelegateActor implementation must not - // write a response to the ResponseWriter as is expected that the caller - // to PostOutbox will do so when handling the error. - PostOutboxRequestBodyHook(c context.Context, r *http.Request, data vocab.Type) (context.Context, error) - // AuthenticatePostInbox delegates the authentication of a POST to an - // inbox. - // - // Only called if the Federated Protocol is enabled. - // - // If an error is returned, it is passed back to the caller of - // PostInbox. In this case, the implementation must not write a - // response to the ResponseWriter as is expected that the client will - // do so when handling the error. The 'authenticated' is ignored. - // - // If no error is returned, but authentication or authorization fails, - // then authenticated must be false and error nil. It is expected that - // the implementation handles writing to the ResponseWriter in this - // case. - // - // Finally, if the authentication and authorization succeeds, then - // authenticated must be true and error nil. The request will continue - // to be processed. - AuthenticatePostInbox(c context.Context, w http.ResponseWriter, r *http.Request) (out context.Context, authenticated bool, err error) - // AuthenticateGetInbox delegates the authentication of a GET to an - // inbox. - // - // Always called, regardless whether the Federated Protocol or Social - // API is enabled. - // - // If an error is returned, it is passed back to the caller of - // GetInbox. In this case, the implementation must not write a - // response to the ResponseWriter as is expected that the client will - // do so when handling the error. The 'authenticated' is ignored. - // - // If no error is returned, but authentication or authorization fails, - // then authenticated must be false and error nil. It is expected that - // the implementation handles writing to the ResponseWriter in this - // case. - // - // Finally, if the authentication and authorization succeeds, then - // authenticated must be true and error nil. The request will continue - // to be processed. - AuthenticateGetInbox(c context.Context, w http.ResponseWriter, r *http.Request) (out context.Context, authenticated bool, err error) - // AuthorizePostInbox delegates the authorization of an activity that - // has been sent by POST to an inbox. - // - // Only called if the Federated Protocol is enabled. - // - // If an error is returned, it is passed back to the caller of - // PostInbox. In this case, the implementation must not write a - // response to the ResponseWriter as is expected that the client will - // do so when handling the error. The 'authorized' is ignored. - // - // If no error is returned, but authorization fails, then authorized - // must be false and error nil. It is expected that the implementation - // handles writing to the ResponseWriter in this case. - // - // Finally, if the authentication and authorization succeeds, then - // authorized must be true and error nil. The request will continue - // to be processed. - AuthorizePostInbox(c context.Context, w http.ResponseWriter, activity Activity) (authorized bool, err error) - // PostInbox delegates the side effects of adding to the inbox and - // determining if it is a request that should be blocked. - // - // Only called if the Federated Protocol is enabled. - // - // As a side effect, PostInbox sets the federated data in the inbox, but - // not on its own in the database, as InboxForwarding (which is called - // later) must decide whether it has seen this activity before in order - // to determine whether to do the forwarding algorithm. - // - // If the error is ErrObjectRequired or ErrTargetRequired, then a Bad - // Request status is sent in the response. - PostInbox(c context.Context, inboxIRI *url.URL, activity Activity) error - // InboxForwarding delegates inbox forwarding logic when a POST request - // is received in the Actor's inbox. - // - // Only called if the Federated Protocol is enabled. - // - // The delegate is responsible for determining whether to do the inbox - // forwarding, as well as actually conducting it if it determines it - // needs to. - // - // As a side effect, InboxForwarding must set the federated data in the - // database, independently of the inbox, however it sees fit in order to - // determine whether it has seen the activity before. - // - // The provided url is the inbox of the recipient of the Activity. The - // Activity is examined for the information about who to inbox forward - // to. - // - // If an error is returned, it is returned to the caller of PostInbox. - InboxForwarding(c context.Context, inboxIRI *url.URL, activity Activity) error - // PostOutbox delegates the logic for side effects and adding to the - // outbox. - // - // Always called, regardless whether the Federated Protocol or Social - // API is enabled. In the case of the Social API being enabled, side - // effects of the Activity must occur. - // - // The delegate is responsible for adding the activity to the database's - // general storage for independent retrieval, and not just within the - // actor's outbox. - // - // If the error is ErrObjectRequired or ErrTargetRequired, then a Bad - // Request status is sent in the response. - // - // Note that 'rawJSON' is an unfortunate consequence where an 'Update' - // Activity is the only one that explicitly cares about 'null' values in - // JSON. Since go-fed does not differentiate between 'null' values and - // values that are simply not present, the 'rawJSON' map is ONLY needed - // for this narrow and specific use case. - PostOutbox(c context.Context, a Activity, outboxIRI *url.URL, rawJSON map[string]interface{}) (deliverable bool, e error) - // AddNewIDs sets new URL ids on the activity. It also does so for all - // 'object' properties if the Activity is a Create type. - // - // Only called if the Social API is enabled. - // - // If an error is returned, it is returned to the caller of PostOutbox. - AddNewIDs(c context.Context, a Activity) error - // Deliver sends a federated message. Called only if federation is - // enabled. - // - // Called if the Federated Protocol is enabled. - // - // The provided url is the outbox of the sender. The Activity contains - // the information about the intended recipients. - // - // If an error is returned, it is returned to the caller of PostOutbox. - Deliver(c context.Context, outbox *url.URL, activity Activity) error - // AuthenticatePostOutbox delegates the authentication and authorization - // of a POST to an outbox. - // - // Only called if the Social API is enabled. - // - // If an error is returned, it is passed back to the caller of - // PostOutbox. In this case, the implementation must not write a - // response to the ResponseWriter as is expected that the client will - // do so when handling the error. The 'authenticated' is ignored. - // - // If no error is returned, but authentication or authorization fails, - // then authenticated must be false and error nil. It is expected that - // the implementation handles writing to the ResponseWriter in this - // case. - // - // Finally, if the authentication and authorization succeeds, then - // authenticated must be true and error nil. The request will continue - // to be processed. - AuthenticatePostOutbox(c context.Context, w http.ResponseWriter, r *http.Request) (out context.Context, authenticated bool, err error) - // AuthenticateGetOutbox delegates the authentication of a GET to an - // outbox. - // - // Always called, regardless whether the Federated Protocol or Social - // API is enabled. - // - // If an error is returned, it is passed back to the caller of - // GetOutbox. In this case, the implementation must not write a - // response to the ResponseWriter as is expected that the client will - // do so when handling the error. The 'authenticated' is ignored. - // - // If no error is returned, but authentication or authorization fails, - // then authenticated must be false and error nil. It is expected that - // the implementation handles writing to the ResponseWriter in this - // case. - // - // Finally, if the authentication and authorization succeeds, then - // authenticated must be true and error nil. The request will continue - // to be processed. - AuthenticateGetOutbox(c context.Context, w http.ResponseWriter, r *http.Request) (out context.Context, authenticated bool, err error) - // WrapInCreate wraps the provided object in a Create ActivityStreams - // activity. The provided URL is the actor's outbox endpoint. - // - // Only called if the Social API is enabled. - WrapInCreate(c context.Context, value vocab.Type, outboxIRI *url.URL) (vocab.ActivityStreamsCreate, error) - // GetOutbox returns the OrderedCollection inbox of the actor for this - // context. It is up to the implementation to provide the correct - // collection for the kind of authorization given in the request. - // - // AuthenticateGetOutbox will be called prior to this. - // - // Always called, regardless whether the Federated Protocol or Social - // API is enabled. - GetOutbox(c context.Context, r *http.Request) (vocab.ActivityStreamsOrderedCollectionPage, error) - // GetInbox returns the OrderedCollection inbox of the actor for this - // context. It is up to the implementation to provide the correct - // collection for the kind of authorization given in the request. - // - // AuthenticateGetInbox will be called prior to this. - // - // Always called, regardless whether the Federated Protocol or Social - // API is enabled. - GetInbox(c context.Context, r *http.Request) (vocab.ActivityStreamsOrderedCollectionPage, error) -} diff --git a/vendor/codeberg.org/superseriousbusiness/activity/pub/doc.go b/vendor/codeberg.org/superseriousbusiness/activity/pub/doc.go deleted file mode 100644 index 93c778179..000000000 --- a/vendor/codeberg.org/superseriousbusiness/activity/pub/doc.go +++ /dev/null @@ -1,9 +0,0 @@ -// Package pub implements the ActivityPub protocol. -// -// Note that every time the ActivityStreams types are changed (added, removed) -// due to code generation, the internal function toASType needs to be modified -// to know about these types. -// -// Note that every version change should also include a change in the version.go -// file. -package pub diff --git a/vendor/codeberg.org/superseriousbusiness/activity/pub/federating_protocol.go b/vendor/codeberg.org/superseriousbusiness/activity/pub/federating_protocol.go deleted file mode 100644 index 62bcf44c1..000000000 --- a/vendor/codeberg.org/superseriousbusiness/activity/pub/federating_protocol.go +++ /dev/null @@ -1,125 +0,0 @@ -package pub - -import ( - "context" - "net/http" - "net/url" - - "codeberg.org/superseriousbusiness/activity/streams/vocab" -) - -// FederatingProtocol contains behaviors an application needs to satisfy for the -// full ActivityPub S2S implementation to be supported by this library. -// -// It is only required if the client application wants to support the server-to- -// server, or federating, protocol. -// -// It is passed to the library as a dependency injection from the client -// application. -type FederatingProtocol interface { - // Hook callback after parsing the request body for a federated request - // to the Actor's inbox. - // - // Can be used to set contextual information based on the Activity - // received. - // - // Only called if the Federated Protocol is enabled. - // - // Warning: Neither authentication nor authorization has taken place at - // this time. Doing anything beyond setting contextual information is - // strongly discouraged. - // - // If an error is returned, it is passed back to the caller of - // PostInbox. In this case, the DelegateActor implementation must not - // write a response to the ResponseWriter as is expected that the caller - // to PostInbox will do so when handling the error. - PostInboxRequestBodyHook(c context.Context, r *http.Request, activity Activity) (context.Context, error) - // AuthenticatePostInbox delegates the authentication of a POST to an - // inbox. - // - // If an error is returned, it is passed back to the caller of - // PostInbox. In this case, the implementation must not write a - // response to the ResponseWriter as is expected that the client will - // do so when handling the error. The 'authenticated' is ignored. - // - // If no error is returned, but authentication or authorization fails, - // then authenticated must be false and error nil. It is expected that - // the implementation handles writing to the ResponseWriter in this - // case. - // - // Finally, if the authentication and authorization succeeds, then - // authenticated must be true and error nil. The request will continue - // to be processed. - AuthenticatePostInbox(c context.Context, w http.ResponseWriter, r *http.Request) (out context.Context, authenticated bool, err error) - // Blocked should determine whether to permit a set of actors given by - // their ids are able to interact with this particular end user due to - // being blocked or other application-specific logic. - // - // If an error is returned, it is passed back to the caller of - // PostInbox. - // - // If no error is returned, but authentication or authorization fails, - // then blocked must be true and error nil. An http.StatusForbidden - // will be written in the wresponse. - // - // Finally, if the authentication and authorization succeeds, then - // blocked must be false and error nil. The request will continue - // to be processed. - Blocked(c context.Context, actorIRIs []*url.URL) (blocked bool, err error) - // FederatingCallbacks returns the application logic that handles - // ActivityStreams received from federating peers. - // - // Note that certain types of callbacks will be 'wrapped' with default - // behaviors supported natively by the library. Other callbacks - // compatible with streams.TypeResolver can be specified by 'other'. - // - // For example, setting the 'Create' field in the - // FederatingWrappedCallbacks lets an application dependency inject - // additional behaviors they want to take place, including the default - // behavior supplied by this library. This is guaranteed to be compliant - // with the ActivityPub Social protocol. - // - // To override the default behavior, instead supply the function in - // 'other', which does not guarantee the application will be compliant - // with the ActivityPub Social Protocol. - // - // Applications are not expected to handle every single ActivityStreams - // type and extension. The unhandled ones are passed to DefaultCallback. - FederatingCallbacks(c context.Context) (wrapped FederatingWrappedCallbacks, other []interface{}, err error) - // DefaultCallback is called for types that go-fed can deserialize but - // are not handled by the application's callbacks returned in the - // Callbacks method. - // - // Applications are not expected to handle every single ActivityStreams - // type and extension, so the unhandled ones are passed to - // DefaultCallback. - DefaultCallback(c context.Context, activity Activity) error - // MaxInboxForwardingRecursionDepth determines how deep to search within - // an activity to determine if inbox forwarding needs to occur. - // - // Zero or negative numbers indicate infinite recursion. - MaxInboxForwardingRecursionDepth(c context.Context) int - // MaxDeliveryRecursionDepth determines how deep to search within - // collections owned by peers when they are targeted to receive a - // delivery. - // - // Zero or negative numbers indicate infinite recursion. - MaxDeliveryRecursionDepth(c context.Context) int - // FilterForwarding allows the implementation to apply business logic - // such as blocks, spam filtering, and so on to a list of potential - // Collections and OrderedCollections of recipients when inbox - // forwarding has been triggered. - // - // The activity is provided as a reference for more intelligent - // logic to be used, but the implementation must not modify it. - FilterForwarding(c context.Context, potentialRecipients []*url.URL, a Activity) (filteredRecipients []*url.URL, err error) - // GetInbox returns the OrderedCollection inbox of the actor for this - // context. It is up to the implementation to provide the correct - // collection for the kind of authorization given in the request. - // - // AuthenticateGetInbox will be called prior to this. - // - // Always called, regardless whether the Federated Protocol or Social - // API is enabled. - GetInbox(c context.Context, r *http.Request) (vocab.ActivityStreamsOrderedCollectionPage, error) -} diff --git a/vendor/codeberg.org/superseriousbusiness/activity/pub/federating_wrapped_callbacks.go b/vendor/codeberg.org/superseriousbusiness/activity/pub/federating_wrapped_callbacks.go deleted file mode 100644 index 9af4f86f1..000000000 --- a/vendor/codeberg.org/superseriousbusiness/activity/pub/federating_wrapped_callbacks.go +++ /dev/null @@ -1,917 +0,0 @@ -package pub - -import ( - "context" - "fmt" - "net/url" - - "codeberg.org/superseriousbusiness/activity/streams" - "codeberg.org/superseriousbusiness/activity/streams/vocab" -) - -// OnFollowBehavior enumerates the different default actions that the go-fed -// library can provide when receiving a Follow Activity from a peer. -type OnFollowBehavior int - -const ( - // OnFollowDoNothing does not take any action when a Follow Activity - // is received. - OnFollowDoNothing OnFollowBehavior = iota - // OnFollowAutomaticallyAccept triggers the side effect of sending an - // Accept of this Follow request in response. - OnFollowAutomaticallyAccept - // OnFollowAutomaticallyAccept triggers the side effect of sending a - // Reject of this Follow request in response. - OnFollowAutomaticallyReject -) - -// FederatingWrappedCallbacks lists the callback functions that already have -// some side effect behavior provided by the pub library. -// -// These functions are wrapped for the Federating Protocol. -type FederatingWrappedCallbacks struct { - // Create handles additional side effects for the Create ActivityStreams - // type, specific to the application using go-fed. - // - // The wrapping callback for the Federating Protocol ensures the - // 'object' property is created in the database. - // - // Create calls Create for each object in the federated Activity. - Create func(context.Context, vocab.ActivityStreamsCreate) error - // Update handles additional side effects for the Update ActivityStreams - // type, specific to the application using go-fed. - // - // The wrapping callback for the Federating Protocol ensures the - // 'object' property is updated in the database. - // - // Update calls Update on the federated entry from the database, with a - // new value. - Update func(context.Context, vocab.ActivityStreamsUpdate) error - // Delete handles additional side effects for the Delete ActivityStreams - // type, specific to the application using go-fed. - // - // Delete removes the federated entry from the database. - Delete func(context.Context, vocab.ActivityStreamsDelete) error - // Follow handles additional side effects for the Follow ActivityStreams - // type, specific to the application using go-fed. - // - // The wrapping function can have one of several default behaviors, - // depending on the value of the OnFollow setting. - Follow func(context.Context, vocab.ActivityStreamsFollow) error - // OnFollow determines what action to take for this particular callback - // if a Follow Activity is handled. - OnFollow OnFollowBehavior - // Accept handles additional side effects for the Accept ActivityStreams - // type, specific to the application using go-fed. - // - // The wrapping function determines if this 'Accept' is in response to a - // 'Follow'. If so, then the 'actor' is added to the original 'actor's - // 'following' collection. - // - // Otherwise, no side effects are done by go-fed. - Accept func(context.Context, vocab.ActivityStreamsAccept) error - // Reject handles additional side effects for the Reject ActivityStreams - // type, specific to the application using go-fed. - // - // The wrapping function has no default side effects. However, if this - // 'Reject' is in response to a 'Follow' then the client MUST NOT go - // forward with adding the 'actor' to the original 'actor's 'following' - // collection by the client application. - Reject func(context.Context, vocab.ActivityStreamsReject) error - // Add handles additional side effects for the Add ActivityStreams - // type, specific to the application using go-fed. - // - // The wrapping function will add the 'object' IRIs to a specific - // 'target' collection if the 'target' collection(s) live on this - // server. - Add func(context.Context, vocab.ActivityStreamsAdd) error - // Remove handles additional side effects for the Remove ActivityStreams - // type, specific to the application using go-fed. - // - // The wrapping function will remove all 'object' IRIs from a specific - // 'target' collection if the 'target' collection(s) live on this - // server. - Remove func(context.Context, vocab.ActivityStreamsRemove) error - // Like handles additional side effects for the Like ActivityStreams - // type, specific to the application using go-fed. - // - // The wrapping function will add the activity to the "likes" collection - // on all 'object' targets owned by this server. - Like func(context.Context, vocab.ActivityStreamsLike) error - // Announce handles additional side effects for the Announce - // ActivityStreams type, specific to the application using go-fed. - // - // The wrapping function will add the activity to the "shares" - // collection on all 'object' targets owned by this server. - Announce func(context.Context, vocab.ActivityStreamsAnnounce) error - // Undo handles additional side effects for the Undo ActivityStreams - // type, specific to the application using go-fed. - // - // The wrapping function ensures the 'actor' on the 'Undo' - // is be the same as the 'actor' on all Activities being undone. - // It enforces that the actors on the Undo must correspond to all of the - // 'object' actors in some manner. - // - // It is expected that the application will implement the proper - // reversal of activities that are being undone. - Undo func(context.Context, vocab.ActivityStreamsUndo) error - // Block handles additional side effects for the Block ActivityStreams - // type, specific to the application using go-fed. - // - // The wrapping function provides no default side effects. It simply - // calls the wrapped function. However, note that Blocks should not be - // received from a federated peer, as delivering Blocks explicitly - // deviates from the original ActivityPub specification. - Block func(context.Context, vocab.ActivityStreamsBlock) error - - // Sidechannel data -- this is set at request handling time. These must - // be set before the callbacks are used. - - // db is the Database the FederatingWrappedCallbacks should use. - db Database - // inboxIRI is the inboxIRI that is handling this callback. - inboxIRI *url.URL - // addNewIds creates new 'id' entries on an activity and its objects if - // it is a Create activity. - addNewIds func(c context.Context, activity Activity) error - // deliver delivers an outgoing message. - deliver func(c context.Context, outboxIRI *url.URL, activity Activity) error - // newTransport creates a new Transport. - newTransport func(c context.Context, actorBoxIRI *url.URL, gofedAgent string) (t Transport, err error) -} - -// callbacks returns the WrappedCallbacks members into a single interface slice -// for use in streams.Resolver callbacks. -// -// If the given functions have a type that collides with the default behavior, -// then disable our default behavior -func (w FederatingWrappedCallbacks) callbacks(fns []interface{}) []interface{} { - enableCreate := true - enableUpdate := true - enableDelete := true - enableFollow := true - enableAccept := true - enableReject := true - enableAdd := true - enableRemove := true - enableLike := true - enableAnnounce := true - enableUndo := true - enableBlock := true - for _, fn := range fns { - switch fn.(type) { - default: - continue - case func(context.Context, vocab.ActivityStreamsCreate) error: - enableCreate = false - case func(context.Context, vocab.ActivityStreamsUpdate) error: - enableUpdate = false - case func(context.Context, vocab.ActivityStreamsDelete) error: - enableDelete = false - case func(context.Context, vocab.ActivityStreamsFollow) error: - enableFollow = false - case func(context.Context, vocab.ActivityStreamsAccept) error: - enableAccept = false - case func(context.Context, vocab.ActivityStreamsReject) error: - enableReject = false - case func(context.Context, vocab.ActivityStreamsAdd) error: - enableAdd = false - case func(context.Context, vocab.ActivityStreamsRemove) error: - enableRemove = false - case func(context.Context, vocab.ActivityStreamsLike) error: - enableLike = false - case func(context.Context, vocab.ActivityStreamsAnnounce) error: - enableAnnounce = false - case func(context.Context, vocab.ActivityStreamsUndo) error: - enableUndo = false - case func(context.Context, vocab.ActivityStreamsBlock) error: - enableBlock = false - } - } - if enableCreate { - fns = append(fns, w.create) - } - if enableUpdate { - fns = append(fns, w.update) - } - if enableDelete { - fns = append(fns, w.deleteFn) - } - if enableFollow { - fns = append(fns, w.follow) - } - if enableAccept { - fns = append(fns, w.accept) - } - if enableReject { - fns = append(fns, w.reject) - } - if enableAdd { - fns = append(fns, w.add) - } - if enableRemove { - fns = append(fns, w.remove) - } - if enableLike { - fns = append(fns, w.like) - } - if enableAnnounce { - fns = append(fns, w.announce) - } - if enableUndo { - fns = append(fns, w.undo) - } - if enableBlock { - fns = append(fns, w.block) - } - return fns -} - -// create implements the federating Create activity side effects. -func (w FederatingWrappedCallbacks) create(c context.Context, a vocab.ActivityStreamsCreate) error { - op := a.GetActivityStreamsObject() - if op == nil || op.Len() == 0 { - return ErrObjectRequired - } - // Create anonymous loop function to be able to properly scope the defer - // for the database lock at each iteration. - loopFn := func(iter vocab.ActivityStreamsObjectPropertyIterator) error { - t := iter.GetType() - if t == nil && iter.IsIRI() { - // Attempt to dereference the IRI instead - tport, err := w.newTransport(c, w.inboxIRI, goFedUserAgent()) - if err != nil { - return err - } - resp, err := tport.Dereference(c, iter.GetIRI()) - if err != nil { - return err - } - m, err := readActivityPubResp(resp) - if err != nil { - return err - } - t, err = streams.ToType(c, m) - if err != nil { - return err - } - } else if t == nil { - return fmt.Errorf("cannot handle federated create: object is neither a value nor IRI") - } - id, err := GetId(t) - if err != nil { - return err - } - var unlock func() - unlock, err = w.db.Lock(c, id) - if err != nil { - return err - } - defer unlock() - if err := w.db.Create(c, t); err != nil { - return err - } - return nil - } - for iter := op.Begin(); iter != op.End(); iter = iter.Next() { - if err := loopFn(iter); err != nil { - return err - } - } - if w.Create != nil { - return w.Create(c, a) - } - return nil -} - -// update implements the federating Update activity side effects. -func (w FederatingWrappedCallbacks) update(c context.Context, a vocab.ActivityStreamsUpdate) error { - op := a.GetActivityStreamsObject() - if op == nil || op.Len() == 0 { - return ErrObjectRequired - } - if err := mustHaveActivityOriginMatchObjects(a); err != nil { - return err - } - // Create anonymous loop function to be able to properly scope the defer - // for the database lock at each iteration. - loopFn := func(iter vocab.ActivityStreamsObjectPropertyIterator) error { - t := iter.GetType() - if t == nil { - return fmt.Errorf("update requires an object to be wholly provided") - } - id, err := GetId(t) - if err != nil { - return err - } - var unlock func() - unlock, err = w.db.Lock(c, id) - if err != nil { - return err - } - defer unlock() - if err := w.db.Update(c, t); err != nil { - return err - } - return nil - } - for iter := op.Begin(); iter != op.End(); iter = iter.Next() { - if err := loopFn(iter); err != nil { - return err - } - } - if w.Update != nil { - return w.Update(c, a) - } - return nil -} - -// deleteFn implements the federating Delete activity side effects. -func (w FederatingWrappedCallbacks) deleteFn(c context.Context, a vocab.ActivityStreamsDelete) error { - op := a.GetActivityStreamsObject() - if op == nil || op.Len() == 0 { - return ErrObjectRequired - } - if err := mustHaveActivityOriginMatchObjects(a); err != nil { - return err - } - // Create anonymous loop function to be able to properly scope the defer - // for the database lock at each iteration. - loopFn := func(iter vocab.ActivityStreamsObjectPropertyIterator) error { - id, err := ToId(iter) - if err != nil { - return err - } - var unlock func() - unlock, err = w.db.Lock(c, id) - if err != nil { - return err - } - defer unlock() - if err := w.db.Delete(c, id); err != nil { - return err - } - return nil - } - for iter := op.Begin(); iter != op.End(); iter = iter.Next() { - if err := loopFn(iter); err != nil { - return err - } - } - if w.Delete != nil { - return w.Delete(c, a) - } - return nil -} - -// follow implements the federating Follow activity side effects. -func (w FederatingWrappedCallbacks) follow(c context.Context, a vocab.ActivityStreamsFollow) error { - op := a.GetActivityStreamsObject() - if op == nil || op.Len() == 0 { - return ErrObjectRequired - } - // Check that we own at least one of the 'object' properties, and ensure - // it is to the actor that owns this inbox. - // - // If not then don't send a response. It was federated to us as an FYI, - // by mistake, or some other reason. - unlock, err := w.db.Lock(c, w.inboxIRI) - if err != nil { - return err - } - // WARNING: Unlock not deferred. - actorIRI, err := w.db.ActorForInbox(c, w.inboxIRI) - unlock() // unlock even on error - if err != nil { - return err - } - // Unlock must be called by now and every branch above. - isMe := false - if w.OnFollow != OnFollowDoNothing { - for iter := op.Begin(); iter != op.End(); iter = iter.Next() { - id, err := ToId(iter) - if err != nil { - return err - } - if id.String() == actorIRI.String() { - isMe = true - break - } - } - } - if isMe { - // Prepare the response. - var response Activity - if w.OnFollow == OnFollowAutomaticallyAccept { - response = streams.NewActivityStreamsAccept() - } else if w.OnFollow == OnFollowAutomaticallyReject { - response = streams.NewActivityStreamsReject() - } else { - return fmt.Errorf("unknown OnFollowBehavior: %d", w.OnFollow) - } - // Set us as the 'actor'. - me := streams.NewActivityStreamsActorProperty() - response.SetActivityStreamsActor(me) - me.AppendIRI(actorIRI) - // Set the Follow as the 'object' property. - op := streams.NewActivityStreamsObjectProperty() - response.SetActivityStreamsObject(op) - op.AppendActivityStreamsFollow(a) - // Add all actors on the original Follow to the 'to' property. - recipients := make([]*url.URL, 0) - to := streams.NewActivityStreamsToProperty() - response.SetActivityStreamsTo(to) - followActors := a.GetActivityStreamsActor() - for iter := followActors.Begin(); iter != followActors.End(); iter = iter.Next() { - id, err := ToId(iter) - if err != nil { - return err - } - to.AppendIRI(id) - recipients = append(recipients, id) - } - if w.OnFollow == OnFollowAutomaticallyAccept { - // If automatically accepting, then also update our - // followers collection with the new actors. - // - // If automatically rejecting, do not update the - // followers collection. - unlock, err := w.db.Lock(c, actorIRI) - if err != nil { - return err - } - // WARNING: Unlock not deferred. - followers, err := w.db.Followers(c, actorIRI) - if err != nil { - unlock() - return err - } - items := followers.GetActivityStreamsItems() - if items == nil { - items = streams.NewActivityStreamsItemsProperty() - followers.SetActivityStreamsItems(items) - } - for _, elem := range recipients { - items.PrependIRI(elem) - } - err = w.db.Update(c, followers) - unlock() // unlock even on error - if err != nil { - return err - } - // Unlock must be called by now and every branch above. - } - // Lock without defer! - unlock, err := w.db.Lock(c, w.inboxIRI) - if err != nil { - return err - } - outboxIRI, err := w.db.OutboxForInbox(c, w.inboxIRI) - unlock() // unlock after, regardless - if err != nil { - return err - } - // Everything must be unlocked by now. - if err := w.addNewIds(c, response); err != nil { - return err - } else if err := w.deliver(c, outboxIRI, response); err != nil { - return err - } - } - if w.Follow != nil { - return w.Follow(c, a) - } - return nil -} - -// accept implements the federating Accept activity side effects. -func (w FederatingWrappedCallbacks) accept(c context.Context, a vocab.ActivityStreamsAccept) error { - op := a.GetActivityStreamsObject() - if op != nil && op.Len() > 0 { - // Get this actor's id. - unlock, err := w.db.Lock(c, w.inboxIRI) - if err != nil { - return err - } - // WARNING: Unlock not deferred. - actorIRI, err := w.db.ActorForInbox(c, w.inboxIRI) - unlock() // unlock after regardless - if err != nil { - return err - } - // Unlock must be called by now and every branch above. - // - // Determine if we are in a follow on the 'object' property. - // - // TODO: Handle Accept multiple Follow. - var maybeMyFollowIRI *url.URL - for iter := op.Begin(); iter != op.End(); iter = iter.Next() { - t := iter.GetType() - if t == nil && iter.IsIRI() { - // Attempt to dereference the IRI instead - tport, err := w.newTransport(c, w.inboxIRI, goFedUserAgent()) - if err != nil { - return err - } - resp, err := tport.Dereference(c, iter.GetIRI()) - if err != nil { - return err - } - m, err := readActivityPubResp(resp) - if err != nil { - return err - } - t, err = streams.ToType(c, m) - if err != nil { - return err - } - } else if t == nil { - return fmt.Errorf("cannot handle federated create: object is neither a value nor IRI") - } - // Ensure it is a Follow. - if !streams.IsOrExtendsActivityStreamsFollow(t) { - continue - } - follow, ok := t.(Activity) - if !ok { - return fmt.Errorf("a Follow in an Accept does not satisfy the Activity interface") - } - followId, err := GetId(follow) - if err != nil { - return err - } - // Ensure that we are one of the actors on the Follow. - actors := follow.GetActivityStreamsActor() - for iter := actors.Begin(); iter != actors.End(); iter = iter.Next() { - id, err := ToId(iter) - if err != nil { - return err - } - if id.String() == actorIRI.String() { - maybeMyFollowIRI = followId - break - } - } - // Continue breaking if we found ourselves - if maybeMyFollowIRI != nil { - break - } - } - // If we received an Accept whose 'object' is a Follow with an - // Accept that we sent, add to the following collection. - if maybeMyFollowIRI != nil { - // Verify our Follow request exists and the peer didn't - // fabricate it. - activityActors := a.GetActivityStreamsActor() - if activityActors == nil || activityActors.Len() == 0 { - return fmt.Errorf("an Accept with a Follow has no actors") - } - // This may be a duplicate check if we dereferenced the - // Follow above. TODO: Separate this logic to avoid - // redundancy. - // - // Use an anonymous function to properly scope the - // database lock, immediately call it. - err = func() error { - unlock, err := w.db.Lock(c, maybeMyFollowIRI) - if err != nil { - return err - } - defer unlock() - t, err := w.db.Get(c, maybeMyFollowIRI) - if err != nil { - return err - } - if !streams.IsOrExtendsActivityStreamsFollow(t) { - return fmt.Errorf("peer gave an Accept wrapping a Follow but provided a non-Follow id") - } - follow, ok := t.(Activity) - if !ok { - return fmt.Errorf("a Follow in an Accept does not satisfy the Activity interface") - } - // Ensure that we are one of the actors on the Follow. - ok = false - actors := follow.GetActivityStreamsActor() - for iter := actors.Begin(); iter != actors.End(); iter = iter.Next() { - id, err := ToId(iter) - if err != nil { - return err - } - if id.String() == actorIRI.String() { - ok = true - break - } - } - if !ok { - return fmt.Errorf("peer gave an Accept wrapping a Follow but we are not the actor on that Follow") - } - // Build map of original Accept actors - acceptActors := make(map[string]bool) - for iter := activityActors.Begin(); iter != activityActors.End(); iter = iter.Next() { - id, err := ToId(iter) - if err != nil { - return err - } - acceptActors[id.String()] = false - } - // Verify all actor(s) were on the original Follow. - followObj := follow.GetActivityStreamsObject() - for iter := followObj.Begin(); iter != followObj.End(); iter = iter.Next() { - id, err := ToId(iter) - if err != nil { - return err - } - if _, ok := acceptActors[id.String()]; ok { - acceptActors[id.String()] = true - } - } - for _, found := range acceptActors { - if !found { - return fmt.Errorf("peer gave an Accept wrapping a Follow but was not an object in the original Follow") - } - } - return nil - }() - if err != nil { - return err - } - // Add the peer to our following collection. - unlock, err := w.db.Lock(c, actorIRI) - if err != nil { - return err - } - // WARNING: Unlock not deferred. - following, err := w.db.Following(c, actorIRI) - if err != nil { - unlock() - return err - } - items := following.GetActivityStreamsItems() - if items == nil { - items = streams.NewActivityStreamsItemsProperty() - following.SetActivityStreamsItems(items) - } - for iter := activityActors.Begin(); iter != activityActors.End(); iter = iter.Next() { - id, err := ToId(iter) - if err != nil { - unlock() - return err - } - items.PrependIRI(id) - } - err = w.db.Update(c, following) - unlock() // unlock after regardless - if err != nil { - return err - } - // Unlock must be called by now and every branch above. - } - } - if w.Accept != nil { - return w.Accept(c, a) - } - return nil -} - -// reject implements the federating Reject activity side effects. -func (w FederatingWrappedCallbacks) reject(c context.Context, a vocab.ActivityStreamsReject) error { - if w.Reject != nil { - return w.Reject(c, a) - } - return nil -} - -// add implements the federating Add activity side effects. -func (w FederatingWrappedCallbacks) add(c context.Context, a vocab.ActivityStreamsAdd) error { - op := a.GetActivityStreamsObject() - if op == nil || op.Len() == 0 { - return ErrObjectRequired - } - target := a.GetActivityStreamsTarget() - if target == nil || target.Len() == 0 { - return ErrTargetRequired - } - if err := add(c, op, target, w.db); err != nil { - return err - } - if w.Add != nil { - return w.Add(c, a) - } - return nil -} - -// remove implements the federating Remove activity side effects. -func (w FederatingWrappedCallbacks) remove(c context.Context, a vocab.ActivityStreamsRemove) error { - op := a.GetActivityStreamsObject() - if op == nil || op.Len() == 0 { - return ErrObjectRequired - } - target := a.GetActivityStreamsTarget() - if target == nil || target.Len() == 0 { - return ErrTargetRequired - } - if err := remove(c, op, target, w.db); err != nil { - return err - } - if w.Remove != nil { - return w.Remove(c, a) - } - return nil -} - -// like implements the federating Like activity side effects. -func (w FederatingWrappedCallbacks) like(c context.Context, a vocab.ActivityStreamsLike) error { - op := a.GetActivityStreamsObject() - if op == nil || op.Len() == 0 { - return ErrObjectRequired - } - id, err := GetId(a) - if err != nil { - return err - } - // Create anonymous loop function to be able to properly scope the defer - // for the database lock at each iteration. - loopFn := func(iter vocab.ActivityStreamsObjectPropertyIterator) error { - objId, err := ToId(iter) - if err != nil { - return err - } - unlock, err := w.db.Lock(c, objId) - if err != nil { - return err - } - defer unlock() - if owns, err := w.db.Owns(c, objId); err != nil { - return err - } else if !owns { - return nil - } - t, err := w.db.Get(c, objId) - if err != nil { - return err - } - l, ok := t.(likeser) - if !ok { - return fmt.Errorf("cannot add Like to likes collection for type %T", t) - } - // Get 'likes' property on the object, creating default if - // necessary. - likes := l.GetActivityStreamsLikes() - if likes == nil { - likes = streams.NewActivityStreamsLikesProperty() - l.SetActivityStreamsLikes(likes) - } - // Get 'likes' value, defaulting to a collection. - likesT := likes.GetType() - if likesT == nil { - col := streams.NewActivityStreamsCollection() - likesT = col - likes.SetActivityStreamsCollection(col) - } - // Prepend the activity's 'id' on the 'likes' Collection or - // OrderedCollection. - if col, ok := likesT.(itemser); ok { - items := col.GetActivityStreamsItems() - if items == nil { - items = streams.NewActivityStreamsItemsProperty() - col.SetActivityStreamsItems(items) - } - items.PrependIRI(id) - } else if oCol, ok := likesT.(orderedItemser); ok { - oItems := oCol.GetActivityStreamsOrderedItems() - if oItems == nil { - oItems = streams.NewActivityStreamsOrderedItemsProperty() - oCol.SetActivityStreamsOrderedItems(oItems) - } - oItems.PrependIRI(id) - } else { - return fmt.Errorf("likes type is neither a Collection nor an OrderedCollection: %T", likesT) - } - err = w.db.Update(c, t) - if err != nil { - return err - } - return nil - } - for iter := op.Begin(); iter != op.End(); iter = iter.Next() { - if err := loopFn(iter); err != nil { - return err - } - } - if w.Like != nil { - return w.Like(c, a) - } - return nil -} - -// announce implements the federating Announce activity side effects. -func (w FederatingWrappedCallbacks) announce(c context.Context, a vocab.ActivityStreamsAnnounce) error { - id, err := GetId(a) - if err != nil { - return err - } - op := a.GetActivityStreamsObject() - // Create anonymous loop function to be able to properly scope the defer - // for the database lock at each iteration. - loopFn := func(iter vocab.ActivityStreamsObjectPropertyIterator) error { - objId, err := ToId(iter) - if err != nil { - return err - } - unlock, err := w.db.Lock(c, objId) - if err != nil { - return err - } - defer unlock() - if owns, err := w.db.Owns(c, objId); err != nil { - return err - } else if !owns { - return nil - } - t, err := w.db.Get(c, objId) - if err != nil { - return err - } - s, ok := t.(shareser) - if !ok { - return fmt.Errorf("cannot add Announce to Shares collection for type %T", t) - } - // Get 'shares' property on the object, creating default if - // necessary. - shares := s.GetActivityStreamsShares() - if shares == nil { - shares = streams.NewActivityStreamsSharesProperty() - s.SetActivityStreamsShares(shares) - } - // Get 'shares' value, defaulting to a collection. - sharesT := shares.GetType() - if sharesT == nil { - col := streams.NewActivityStreamsCollection() - sharesT = col - shares.SetActivityStreamsCollection(col) - } - // Prepend the activity's 'id' on the 'shares' Collection or - // OrderedCollection. - if col, ok := sharesT.(itemser); ok { - items := col.GetActivityStreamsItems() - if items == nil { - items = streams.NewActivityStreamsItemsProperty() - col.SetActivityStreamsItems(items) - } - items.PrependIRI(id) - } else if oCol, ok := sharesT.(orderedItemser); ok { - oItems := oCol.GetActivityStreamsOrderedItems() - if oItems == nil { - oItems = streams.NewActivityStreamsOrderedItemsProperty() - oCol.SetActivityStreamsOrderedItems(oItems) - } - oItems.PrependIRI(id) - } else { - return fmt.Errorf("shares type is neither a Collection nor an OrderedCollection: %T", sharesT) - } - err = w.db.Update(c, t) - if err != nil { - return err - } - return nil - } - if op != nil { - for iter := op.Begin(); iter != op.End(); iter = iter.Next() { - if err := loopFn(iter); err != nil { - return err - } - } - } - if w.Announce != nil { - return w.Announce(c, a) - } - return nil -} - -// undo implements the federating Undo activity side effects. -func (w FederatingWrappedCallbacks) undo(c context.Context, a vocab.ActivityStreamsUndo) error { - op := a.GetActivityStreamsObject() - if op == nil || op.Len() == 0 { - return ErrObjectRequired - } - actors := a.GetActivityStreamsActor() - if err := mustHaveActivityActorsMatchObjectActors(c, actors, op, w.newTransport, w.inboxIRI); err != nil { - return err - } - if w.Undo != nil { - return w.Undo(c, a) - } - return nil -} - -// block implements the federating Block activity side effects. -func (w FederatingWrappedCallbacks) block(c context.Context, a vocab.ActivityStreamsBlock) error { - op := a.GetActivityStreamsObject() - if op == nil || op.Len() == 0 { - return ErrObjectRequired - } - if w.Block != nil { - return w.Block(c, a) - } - return nil -} diff --git a/vendor/codeberg.org/superseriousbusiness/activity/pub/handlers.go b/vendor/codeberg.org/superseriousbusiness/activity/pub/handlers.go deleted file mode 100644 index 688b8158f..000000000 --- a/vendor/codeberg.org/superseriousbusiness/activity/pub/handlers.go +++ /dev/null @@ -1,115 +0,0 @@ -package pub - -import ( - "context" - "encoding/json" - "errors" - "fmt" - "net/http" - - "codeberg.org/superseriousbusiness/activity/streams" -) - -var ErrNotFound = errors.New("go-fed/activity: ActivityStreams data not found") - -// HandlerFunc determines whether an incoming HTTP request is an ActivityStreams -// GET request, and if so attempts to serve ActivityStreams data. -// -// If an error is returned, then the calling function is responsible for writing -// to the ResponseWriter as part of error handling. -// -// If 'isASRequest' is false and there is no error, then the calling function -// may continue processing the request, and the HandlerFunc will not have -// written anything to the ResponseWriter. For example, a webpage may be served -// instead. -// -// If 'isASRequest' is true and there is no error, then the HandlerFunc -// successfully served the request and wrote to the ResponseWriter. -// -// Callers are responsible for authorized access to this resource. -type HandlerFunc func(c context.Context, w http.ResponseWriter, r *http.Request) (isASRequest bool, err error) - -// NewActivityStreamsHandler creates a HandlerFunc to serve ActivityStreams -// requests which are coming from other clients or servers that wish to obtain -// an ActivityStreams representation of data. -// -// Strips retrieved ActivityStreams values of sensitive fields ('bto' and 'bcc') -// before responding with them. Sets the appropriate HTTP status code for -// Tombstone Activities as well. -// -// Defaults to supporting content to be retrieved by HTTPS only. -func NewActivityStreamsHandler(db Database, clock Clock) HandlerFunc { - return NewActivityStreamsHandlerScheme(db, clock, "https") -} - -// NewActivityStreamsHandlerScheme creates a HandlerFunc to serve -// ActivityStreams requests which are coming from other clients or servers that -// wish to obtain an ActivityStreams representation of data provided by the -// specified protocol scheme. -// -// Strips retrieved ActivityStreams values of sensitive fields ('bto' and 'bcc') -// before responding with them. Sets the appropriate HTTP status code for -// Tombstone Activities as well. -// -// Specifying the "scheme" allows for retrieving ActivityStreams content with -// identifiers such as HTTP, HTTPS, or other protocol schemes. -// -// Returns ErrNotFound when the database does not retrieve any data and no -// errors occurred during retrieval. -func NewActivityStreamsHandlerScheme(db Database, clock Clock, scheme string) HandlerFunc { - return func(c context.Context, w http.ResponseWriter, r *http.Request) (isASRequest bool, err error) { - // Do nothing if it is not an ActivityPub GET request - if !isActivityPubGet(r) { - return - } - isASRequest = true - id := requestId(r, scheme) - - var unlock func() - - // Lock and obtain a copy of the requested ActivityStreams value - unlock, err = db.Lock(c, id) - if err != nil { - return - } - // WARNING: Unlock not deferred - t, err := db.Get(c, id) - unlock() // unlock even on error - if err != nil { - return - } - // Unlock must have been called by this point and in every - // branch above - if t == nil { - err = ErrNotFound - return - } - // Remove sensitive fields. - clearSensitiveFields(t) - // Serialize the fetched value. - m, err := streams.Serialize(t) - if err != nil { - return - } - raw, err := json.Marshal(m) - if err != nil { - return - } - // Construct the response. - addResponseHeaders(w.Header(), clock, raw) - // Write the response. - if streams.IsOrExtendsActivityStreamsTombstone(t) { - w.WriteHeader(http.StatusGone) - } else { - w.WriteHeader(http.StatusOK) - } - n, err := w.Write(raw) - if err != nil { - return - } else if n != len(raw) { - err = fmt.Errorf("only wrote %d of %d bytes", n, len(raw)) - return - } - return - } -} diff --git a/vendor/codeberg.org/superseriousbusiness/activity/pub/property_interfaces.go b/vendor/codeberg.org/superseriousbusiness/activity/pub/property_interfaces.go deleted file mode 100644 index abec7ad53..000000000 --- a/vendor/codeberg.org/superseriousbusiness/activity/pub/property_interfaces.go +++ /dev/null @@ -1,118 +0,0 @@ -package pub - -import ( - "net/url" - - "codeberg.org/superseriousbusiness/activity/streams/vocab" -) - -// inReplyToer is an ActivityStreams type with an 'inReplyTo' property -type inReplyToer interface { - GetActivityStreamsInReplyTo() vocab.ActivityStreamsInReplyToProperty -} - -// objecter is an ActivityStreams type with an 'object' property -type objecter interface { - GetActivityStreamsObject() vocab.ActivityStreamsObjectProperty -} - -// targeter is an ActivityStreams type with a 'target' property -type targeter interface { - GetActivityStreamsTarget() vocab.ActivityStreamsTargetProperty -} - -// tagger is an ActivityStreams type with a 'tag' property -type tagger interface { - GetActivityStreamsTag() vocab.ActivityStreamsTagProperty -} - -// hrefer is an ActivityStreams type with a 'href' property -type hrefer interface { - GetActivityStreamsHref() vocab.ActivityStreamsHrefProperty -} - -// itemser is an ActivityStreams type with an 'items' property -type itemser interface { - GetActivityStreamsItems() vocab.ActivityStreamsItemsProperty - SetActivityStreamsItems(vocab.ActivityStreamsItemsProperty) -} - -// orderedItemser is an ActivityStreams type with an 'orderedItems' property -type orderedItemser interface { - GetActivityStreamsOrderedItems() vocab.ActivityStreamsOrderedItemsProperty - SetActivityStreamsOrderedItems(vocab.ActivityStreamsOrderedItemsProperty) -} - -// publisheder is an ActivityStreams type with a 'published' property -type publisheder interface { - GetActivityStreamsPublished() vocab.ActivityStreamsPublishedProperty -} - -// updateder is an ActivityStreams type with an 'updateder' property -type updateder interface { - GetActivityStreamsUpdated() vocab.ActivityStreamsUpdatedProperty -} - -// toer is an ActivityStreams type with a 'to' property -type toer interface { - GetActivityStreamsTo() vocab.ActivityStreamsToProperty - SetActivityStreamsTo(i vocab.ActivityStreamsToProperty) -} - -// btoer is an ActivityStreams type with a 'bto' property -type btoer interface { - GetActivityStreamsBto() vocab.ActivityStreamsBtoProperty - SetActivityStreamsBto(i vocab.ActivityStreamsBtoProperty) -} - -// ccer is an ActivityStreams type with a 'cc' property -type ccer interface { - GetActivityStreamsCc() vocab.ActivityStreamsCcProperty - SetActivityStreamsCc(i vocab.ActivityStreamsCcProperty) -} - -// bccer is an ActivityStreams type with a 'bcc' property -type bccer interface { - GetActivityStreamsBcc() vocab.ActivityStreamsBccProperty - SetActivityStreamsBcc(i vocab.ActivityStreamsBccProperty) -} - -// audiencer is an ActivityStreams type with an 'audience' property -type audiencer interface { - GetActivityStreamsAudience() vocab.ActivityStreamsAudienceProperty - SetActivityStreamsAudience(i vocab.ActivityStreamsAudienceProperty) -} - -// inboxer is an ActivityStreams type with an 'inbox' property -type inboxer interface { - GetActivityStreamsInbox() vocab.ActivityStreamsInboxProperty -} - -// attributedToer is an ActivityStreams type with an 'attributedTo' property -type attributedToer interface { - GetActivityStreamsAttributedTo() vocab.ActivityStreamsAttributedToProperty - SetActivityStreamsAttributedTo(i vocab.ActivityStreamsAttributedToProperty) -} - -// likeser is an ActivityStreams type with a 'likes' property -type likeser interface { - GetActivityStreamsLikes() vocab.ActivityStreamsLikesProperty - SetActivityStreamsLikes(i vocab.ActivityStreamsLikesProperty) -} - -// shareser is an ActivityStreams type with a 'shares' property -type shareser interface { - GetActivityStreamsShares() vocab.ActivityStreamsSharesProperty - SetActivityStreamsShares(i vocab.ActivityStreamsSharesProperty) -} - -// actorer is an ActivityStreams type with an 'actor' property -type actorer interface { - GetActivityStreamsActor() vocab.ActivityStreamsActorProperty - SetActivityStreamsActor(i vocab.ActivityStreamsActorProperty) -} - -// appendIRIer is an ActivityStreams type that can Append IRIs. -type appendIRIer interface { - AppendIRI(v *url.URL) -} diff --git a/vendor/codeberg.org/superseriousbusiness/activity/pub/side_effect_actor.go b/vendor/codeberg.org/superseriousbusiness/activity/pub/side_effect_actor.go deleted file mode 100644 index d98abfdb3..000000000 --- a/vendor/codeberg.org/superseriousbusiness/activity/pub/side_effect_actor.go +++ /dev/null @@ -1,1047 +0,0 @@ -package pub - -import ( - "context" - "fmt" - "net/http" - "net/url" - - "codeberg.org/superseriousbusiness/activity/streams" - "codeberg.org/superseriousbusiness/activity/streams/vocab" -) - -// sideEffectActor must satisfy the DelegateActor interface. -var _ DelegateActor = &SideEffectActor{} - -// SideEffectActor is a DelegateActor that handles the ActivityPub -// implementation side effects, but requires a more opinionated application to -// be written. -// -// Note that when using the SideEffectActor with an application that good-faith -// implements its required interfaces, the ActivityPub specification is -// guaranteed to be correctly followed. -type SideEffectActor struct { - // When doing deliveries to remote servers via the s2s protocol, the side effect - // actor will by default use the Serialize function from the streams package. - // However, this can be overridden after the side effect actor is intantiated, - // by setting the exposed Serialize function on the struct. For example: - // - // a := NewSideEffectActor(...) - // a.Serialize = func(a vocab.Type) (m map[string]interface{}, e error) { - // // Put your custom serializer logic here. - // } - // - // Note that you should only do this *immediately* after instantiating the side - // effect actor -- never while your application is already running, as this will - // likely cause race conditions or other problems! In most cases, you will never - // need to change this; it's provided solely to allow easier customization by - // applications. - Serialize func(a vocab.Type) (m map[string]interface{}, e error) - - // When doing deliveries to remote servers via the s2s protocol, it may be desirable - // for implementations to be able to pre-sort recipients so that higher-priority - // recipients are higher up in the delivery queue, and lower-priority recipients - // are further down. This can be achieved by setting the DeliveryRecipientPreSort - // function on the side effect actor after it's instantiated. For example: - // - // a := NewSideEffectActor(...) - // a.DeliveryRecipientPreSort = func(actorAndCollectionIRIs []*url.URL) []*url.URL { - // // Put your sorting logic here. - // } - // - // The actorAndCollectionIRIs parameter will be the initial list of IRIs derived by - // looking at the "to", "cc", "bto", "bcc", and "audience" properties of the activity - // being delivered, excluding the AP public IRI, and before dereferencing of inboxes. - // It may look something like this: - // - // [ - // "https://example.org/users/someone/followers", // <-- collection IRI - // "https://another.example.org/users/someone_else", // <-- actor IRI - // "[...]" // <-- etc - // ] - // - // In this case, implementers may wish to sort the slice so that the directly-addressed - // actor "https://another.example.org/users/someone_else" occurs at an earlier index in - // the slice than the followers collection "https://example.org/users/someone/followers", - // so that "@someone_else" receives the delivery first. - // - // Note that you should only do this *immediately* after instantiating the side - // effect actor -- never while your application is already running, as this will - // likely cause race conditions or other problems! It's also completely fine to not - // set this function at all -- in this case, no pre-sorting of recipients will be - // performed, and delivery will occur in a non-determinate order. - DeliveryRecipientPreSort func(actorAndCollectionIRIs []*url.URL) []*url.URL - - common CommonBehavior - s2s FederatingProtocol - c2s SocialProtocol - db Database - clock Clock -} - -// NewSideEffectActor returns a new SideEffectActor, which satisfies the -// DelegateActor interface. Most of the time you will not need to call this -// function, and should instead rely on the NewSocialActor, NewFederatingActor, -// and NewActor functions, all of which use a SideEffectActor under the hood. -// Nevertheless, this function is exposed in case application developers need -// a SideEffectActor for some other reason (tests, monkey patches, etc). -// -// If you are using the returned SideEffectActor for federation, ensure that s2s -// is not nil. Likewise, if you are using it for the social protocol, ensure -// that c2s is not nil. -func NewSideEffectActor(c CommonBehavior, - s2s FederatingProtocol, - c2s SocialProtocol, - db Database, - clock Clock) *SideEffectActor { - return &SideEffectActor{ - Serialize: streams.Serialize, - common: c, - s2s: s2s, - c2s: c2s, - db: db, - clock: clock, - } -} - -// PostInboxRequestBodyHook defers to the delegate. -func (a *SideEffectActor) PostInboxRequestBodyHook(c context.Context, r *http.Request, activity Activity) (context.Context, error) { - return a.s2s.PostInboxRequestBodyHook(c, r, activity) -} - -// PostOutboxRequestBodyHook defers to the delegate. -func (a *SideEffectActor) PostOutboxRequestBodyHook(c context.Context, r *http.Request, data vocab.Type) (context.Context, error) { - return a.c2s.PostOutboxRequestBodyHook(c, r, data) -} - -// AuthenticatePostInbox defers to the delegate to authenticate the request. -func (a *SideEffectActor) AuthenticatePostInbox(c context.Context, w http.ResponseWriter, r *http.Request) (out context.Context, authenticated bool, err error) { - return a.s2s.AuthenticatePostInbox(c, w, r) -} - -// AuthenticateGetInbox defers to the delegate to authenticate the request. -func (a *SideEffectActor) AuthenticateGetInbox(c context.Context, w http.ResponseWriter, r *http.Request) (out context.Context, authenticated bool, err error) { - return a.common.AuthenticateGetInbox(c, w, r) -} - -// AuthenticatePostOutbox defers to the delegate to authenticate the request. -func (a *SideEffectActor) AuthenticatePostOutbox(c context.Context, w http.ResponseWriter, r *http.Request) (out context.Context, authenticated bool, err error) { - return a.c2s.AuthenticatePostOutbox(c, w, r) -} - -// AuthenticateGetOutbox defers to the delegate to authenticate the request. -func (a *SideEffectActor) AuthenticateGetOutbox(c context.Context, w http.ResponseWriter, r *http.Request) (out context.Context, authenticated bool, err error) { - return a.common.AuthenticateGetOutbox(c, w, r) -} - -// GetOutbox delegates to the SocialProtocol. -func (a *SideEffectActor) GetOutbox(c context.Context, r *http.Request) (vocab.ActivityStreamsOrderedCollectionPage, error) { - return a.common.GetOutbox(c, r) -} - -// GetInbox delegates to the FederatingProtocol. -func (a *SideEffectActor) GetInbox(c context.Context, r *http.Request) (vocab.ActivityStreamsOrderedCollectionPage, error) { - return a.s2s.GetInbox(c, r) -} - -// AuthorizePostInbox defers to the federating protocol whether the peer request -// is authorized based on the actors' ids. -func (a *SideEffectActor) AuthorizePostInbox(c context.Context, w http.ResponseWriter, activity Activity) (authorized bool, err error) { - authorized = false - actor := activity.GetActivityStreamsActor() - if actor == nil { - err = fmt.Errorf("no actors in post to inbox") - return - } - var iris []*url.URL - for i := 0; i < actor.Len(); i++ { - iter := actor.At(i) - if iter.IsIRI() { - iris = append(iris, iter.GetIRI()) - } else if t := iter.GetType(); t != nil { - iris = append(iris, activity.GetJSONLDId().Get()) - } else { - err = fmt.Errorf("actor at index %d is missing an id", i) - return - } - } - // Determine if the actor(s) sending this request are blocked. - var blocked bool - if blocked, err = a.s2s.Blocked(c, iris); err != nil { - return - } else if blocked { - w.WriteHeader(http.StatusForbidden) - return - } - authorized = true - return -} - -// PostInbox handles the side effects of determining whether to block the peer's -// request, adding the activity to the actor's inbox, and triggering side -// effects based on the activity's type. -func (a *SideEffectActor) PostInbox(c context.Context, inboxIRI *url.URL, activity Activity) error { - isNew, err := a.addToInboxIfNew(c, inboxIRI, activity) - if err != nil { - return err - } - if isNew { - wrapped, other, err := a.s2s.FederatingCallbacks(c) - if err != nil { - return err - } - // Populate side channels. - wrapped.db = a.db - wrapped.inboxIRI = inboxIRI - wrapped.newTransport = a.common.NewTransport - wrapped.deliver = a.Deliver - wrapped.addNewIds = a.AddNewIDs - res, err := streams.NewTypeResolver(wrapped.callbacks(other)...) - if err != nil { - return err - } - if err = res.Resolve(c, activity); err != nil && !streams.IsUnmatchedErr(err) { - return err - } else if streams.IsUnmatchedErr(err) { - err = a.s2s.DefaultCallback(c, activity) - if err != nil { - return err - } - } - } - return nil -} - -// InboxForwarding implements the 3-part inbox forwarding algorithm specified in -// the ActivityPub specification. Does not modify the Activity, but may send -// outbound requests as a side effect. -// -// InboxForwarding sets the federated data in the database. -func (a *SideEffectActor) InboxForwarding(c context.Context, inboxIRI *url.URL, activity Activity) error { - // 1. Must be first time we have seen this Activity. - // - // Obtain the id of the activity - id := activity.GetJSONLDId() - // Acquire a lock for the id. To be held for the rest of execution. - unlock, err := a.db.Lock(c, id.Get()) - if err != nil { - return err - } - // WARNING: Unlock is not deferred - // - // If the database already contains the activity, exit early. - exists, err := a.db.Exists(c, id.Get()) - if err != nil { - unlock() - return err - } else if exists { - unlock() - return nil - } - // Attempt to create the activity entry. - err = a.db.Create(c, activity) - unlock() // unlock even on error return - if err != nil { - return err - } - // Unlock by this point and in every branch above. - // - // 2. The values of 'to', 'cc', or 'audience' are Collections owned by - // this server. - var r []*url.URL - to := activity.GetActivityStreamsTo() - if to != nil { - for iter := to.Begin(); iter != to.End(); iter = iter.Next() { - val, err := ToId(iter) - if err != nil { - return err - } - r = append(r, val) - } - } - cc := activity.GetActivityStreamsCc() - if cc != nil { - for iter := cc.Begin(); iter != cc.End(); iter = iter.Next() { - val, err := ToId(iter) - if err != nil { - return err - } - r = append(r, val) - } - } - audience := activity.GetActivityStreamsAudience() - if audience != nil { - for iter := audience.Begin(); iter != audience.End(); iter = iter.Next() { - val, err := ToId(iter) - if err != nil { - return err - } - r = append(r, val) - } - } - // Find all IRIs owned by this server. We need to find all of them so - // that forwarding can properly occur. - var myIRIs []*url.URL - for _, iri := range r { - if err != nil { - return err - } - var unlock func() - unlock, err = a.db.Lock(c, iri) - if err != nil { - return err - } - // WARNING: Unlock is not deferred - owns, err := a.db.Owns(c, iri) - unlock() // unlock even on error - if err != nil { - return err - } else if !owns { - continue - } - // Unlock by this point and in every branch above. - myIRIs = append(myIRIs, iri) - } - // Finally, load our IRIs to determine if they are a Collection or - // OrderedCollection. - // - // Load the unfiltered IRIs. - var colIRIs []*url.URL - col := make(map[string]itemser) - oCol := make(map[string]orderedItemser) - for _, iri := range myIRIs { - var unlock func() - unlock, err = a.db.Lock(c, iri) - if err != nil { - return err - } - // WARNING: Not Unlocked - t, err := a.db.Get(c, iri) - if err != nil { - return err - } - if streams.IsOrExtendsActivityStreamsOrderedCollection(t) { - if im, ok := t.(orderedItemser); ok { - oCol[iri.String()] = im - colIRIs = append(colIRIs, iri) - defer unlock() - } else { - unlock() // unlock instantly - } - } else if streams.IsOrExtendsActivityStreamsCollection(t) { - if im, ok := t.(itemser); ok { - col[iri.String()] = im - colIRIs = append(colIRIs, iri) - defer unlock() - } else { - unlock() // unlock instantly - } - } else { - unlock() // unlock instantly - } - } - // If we own none of the Collection IRIs in 'to', 'cc', or 'audience' - // then no need to do inbox forwarding. We have nothing to forward to. - if len(colIRIs) == 0 { - return nil - } - // 3. The values of 'inReplyTo', 'object', 'target', or 'tag' are owned - // by this server. This is only a boolean trigger: As soon as we get - // a hit that we own something, then we should do inbox forwarding. - maxDepth := a.s2s.MaxInboxForwardingRecursionDepth(c) - ownsValue, err := a.hasInboxForwardingValues(c, inboxIRI, activity, maxDepth, 0) - if err != nil { - return err - } - // If we don't own any of the 'inReplyTo', 'object', 'target', or 'tag' - // values, then no need to do inbox forwarding. - if !ownsValue { - return nil - } - // Do the inbox forwarding since the above conditions hold true. Support - // the behavior of letting the application filter out the resulting - // collections to be targeted. - toSend, err := a.s2s.FilterForwarding(c, colIRIs, activity) - if err != nil { - return err - } - recipients := make([]*url.URL, 0, len(toSend)) - for _, iri := range toSend { - if c, ok := col[iri.String()]; ok { - if it := c.GetActivityStreamsItems(); it != nil { - for iter := it.Begin(); iter != it.End(); iter = iter.Next() { - id, err := ToId(iter) - if err != nil { - return err - } - recipients = append(recipients, id) - } - } - } else if oc, ok := oCol[iri.String()]; ok { - if oit := oc.GetActivityStreamsOrderedItems(); oit != nil { - for iter := oit.Begin(); iter != oit.End(); iter = iter.Next() { - id, err := ToId(iter) - if err != nil { - return err - } - recipients = append(recipients, id) - } - } - } - } - return a.deliverToRecipients(c, inboxIRI, activity, recipients) -} - -// PostOutbox handles the side effects of adding the activity to the actor's -// outbox, and triggering side effects based on the activity's type. -// -// This implementation assumes all types are meant to be delivered except for -// the ActivityStreams Block type. -func (a *SideEffectActor) PostOutbox(c context.Context, activity Activity, outboxIRI *url.URL, rawJSON map[string]interface{}) (deliverable bool, err error) { - // TODO: Determine this if c2s is nil - deliverable = true - if a.c2s != nil { - var wrapped SocialWrappedCallbacks - var other []interface{} - wrapped, other, err = a.c2s.SocialCallbacks(c) - if err != nil { - return - } - // Populate side channels. - wrapped.db = a.db - wrapped.outboxIRI = outboxIRI - wrapped.rawActivity = rawJSON - wrapped.clock = a.clock - wrapped.newTransport = a.common.NewTransport - undeliverable := false - wrapped.undeliverable = &undeliverable - var res *streams.TypeResolver - res, err = streams.NewTypeResolver(wrapped.callbacks(other)...) - if err != nil { - return - } - if err = res.Resolve(c, activity); err != nil && !streams.IsUnmatchedErr(err) { - return - } else if streams.IsUnmatchedErr(err) { - deliverable = true - err = a.c2s.DefaultCallback(c, activity) - if err != nil { - return - } - } else { - deliverable = !undeliverable - } - } - err = a.addToOutbox(c, outboxIRI, activity) - return -} - -// AddNewIDs creates new 'id' entries on an activity and its objects if it is a -// Create activity. -func (a *SideEffectActor) AddNewIDs(c context.Context, activity Activity) error { - id, err := a.db.NewID(c, activity) - if err != nil { - return err - } - activityId := streams.NewJSONLDIdProperty() - activityId.Set(id) - activity.SetJSONLDId(activityId) - if streams.IsOrExtendsActivityStreamsCreate(activity) { - o, ok := activity.(objecter) - if !ok { - return fmt.Errorf("cannot add new id for Create: %T has no object property", activity) - } - if oProp := o.GetActivityStreamsObject(); oProp != nil { - for iter := oProp.Begin(); iter != oProp.End(); iter = iter.Next() { - t := iter.GetType() - if t == nil { - return fmt.Errorf("cannot add new id for object in Create: object is not embedded as a value literal") - } - id, err = a.db.NewID(c, t) - if err != nil { - return err - } - objId := streams.NewJSONLDIdProperty() - objId.Set(id) - t.SetJSONLDId(objId) - } - } - } - return nil -} - -// deliver will complete the peer-to-peer sending of a federated message to -// another server. -// -// Must be called if at least the federated protocol is supported. -func (a *SideEffectActor) Deliver(c context.Context, outboxIRI *url.URL, activity Activity) error { - recipients, err := a.prepare(c, outboxIRI, activity) - if err != nil { - return err - } - return a.deliverToRecipients(c, outboxIRI, activity, recipients) -} - -// WrapInCreate wraps an object with a Create activity. -func (a *SideEffectActor) WrapInCreate(c context.Context, obj vocab.Type, outboxIRI *url.URL) (create vocab.ActivityStreamsCreate, err error) { - var unlock func() - unlock, err = a.db.Lock(c, outboxIRI) - if err != nil { - return - } - // WARNING: No deferring the Unlock - actorIRI, err := a.db.ActorForOutbox(c, outboxIRI) - unlock() // unlock after regardless - if err != nil { - return - } - // Unlock the lock at this point and every branch above - return wrapInCreate(c, obj, actorIRI) -} - -// deliverToRecipients will take a prepared Activity and send it to specific -// recipients on behalf of an actor. -func (a *SideEffectActor) deliverToRecipients(c context.Context, boxIRI *url.URL, activity Activity, recipients []*url.URL) error { - // Call whichever serializer is - // set on the side effect actor. - m, err := a.Serialize(activity) - if err != nil { - return err - } - - tp, err := a.common.NewTransport(c, boxIRI, goFedUserAgent()) - if err != nil { - return err - } - - return tp.BatchDeliver(c, m, recipients) -} - -// addToOutbox adds the activity to the outbox and creates the activity in the -// internal database as its own entry. -func (a *SideEffectActor) addToOutbox(c context.Context, outboxIRI *url.URL, activity Activity) error { - // Set the activity in the database first. - id := activity.GetJSONLDId() - unlock, err := a.db.Lock(c, id.Get()) - if err != nil { - return err - } - // WARNING: Unlock not deferred - err = a.db.Create(c, activity) - unlock() // unlock after regardless - if err != nil { - return err - } - // WARNING: Unlock(c, id) should be called by this point and in every - // return before here. - // - // Acquire a lock to read the outbox. Defer release. - unlock, err = a.db.Lock(c, outboxIRI) - if err != nil { - return err - } - defer unlock() - outbox, err := a.db.GetOutbox(c, outboxIRI) - if err != nil { - return err - } - // Prepend the activity to the list of 'orderedItems'. - oi := outbox.GetActivityStreamsOrderedItems() - if oi == nil { - oi = streams.NewActivityStreamsOrderedItemsProperty() - } - oi.PrependIRI(id.Get()) - outbox.SetActivityStreamsOrderedItems(oi) - // Save in the database. - err = a.db.SetOutbox(c, outbox) - return err -} - -// addToInboxIfNew will add the activity to the inbox at the specified IRI if -// the activity's ID has not yet been added to the inbox. -// -// It does not add the activity to this database's know federated data. -// -// Returns true when the activity is novel. -func (a *SideEffectActor) addToInboxIfNew(c context.Context, inboxIRI *url.URL, activity Activity) (isNew bool, err error) { - // Acquire a lock to read the inbox. Defer release. - var unlock func() - unlock, err = a.db.Lock(c, inboxIRI) - if err != nil { - return - } - defer unlock() - // Obtain the id of the activity - id := activity.GetJSONLDId() - // If the inbox already contains the URL, early exit. - contains, err := a.db.InboxContains(c, inboxIRI, id.Get()) - if err != nil { - return - } else if contains { - return - } - // It is a new id, acquire the inbox. - isNew = true - inbox, err := a.db.GetInbox(c, inboxIRI) - if err != nil { - return - } - // Prepend the activity to the list of 'orderedItems'. - oi := inbox.GetActivityStreamsOrderedItems() - if oi == nil { - oi = streams.NewActivityStreamsOrderedItemsProperty() - } - oi.PrependIRI(id.Get()) - inbox.SetActivityStreamsOrderedItems(oi) - // Save in the database. - err = a.db.SetInbox(c, inbox) - return -} - -// Given an ActivityStreams value, recursively examines ownership of the id or -// href and the ones on properties applicable to inbox forwarding. -// -// Recursion may be limited by providing a 'maxDepth' greater than zero. A -// value of zero or a negative number will result in infinite recursion. -func (a *SideEffectActor) hasInboxForwardingValues(c context.Context, inboxIRI *url.URL, val vocab.Type, maxDepth, currDepth int) (bool, error) { - // Stop recurring if we are exceeding the maximum depth and the maximum - // is a positive number. - if maxDepth > 0 && currDepth >= maxDepth { - return false, nil - } - // Determine if we own the 'id' of any values on the properties we care - // about. - types, iris := getInboxForwardingValues(val) - // For IRIs, simply check if we own them. - for _, iri := range iris { - unlock, err := a.db.Lock(c, iri) - if err != nil { - return false, err - } - // WARNING: Unlock is not deferred - owns, err := a.db.Owns(c, iri) - unlock() // unlock after regardless - if err != nil { - return false, err - } else if owns { - return true, nil - } - // Unlock by this point and in every branch above - } - // For embedded literals, check the id. - for _, val := range types { - id, err := GetId(val) - if err != nil { - return false, err - } - var unlock func() - unlock, err = a.db.Lock(c, id) - if err != nil { - return false, err - } - // WARNING: Unlock is not deferred - owns, err := a.db.Owns(c, id) - unlock() // unlock after regardless - if err != nil { - return false, err - } else if owns { - return true, nil - } - // Unlock by this point and in every branch above - } - // Recur Preparation: Try fetching the IRIs so we can recur into them. - for _, iri := range iris { - // Dereferencing the IRI. - tport, err := a.common.NewTransport(c, inboxIRI, goFedUserAgent()) - if err != nil { - return false, err - } - resp, err := tport.Dereference(c, iri) - if err != nil { - // Do not fail the entire process if the data is - // missing. - continue - } - m, err := readActivityPubResp(resp) - if err != nil { - return false, err - } - t, err := streams.ToType(c, m) - if err != nil { - // Do not fail the entire process if we cannot handle - // the type. - continue - } - types = append(types, t) - } - // Recur. - for _, nextVal := range types { - if has, err := a.hasInboxForwardingValues(c, inboxIRI, nextVal, maxDepth, currDepth+1); err != nil { - return false, err - } else if has { - return true, nil - } - } - return false, nil -} - -// prepare takes a deliverableObject and returns a list of the final -// recipient inbox IRIs. Additionally, the deliverableObject will have -// any hidden hidden recipients ("bto" and "bcc") stripped from it. -// -// Only call if both the social and federated protocol are supported. -func (a *SideEffectActor) prepare( - ctx context.Context, - outboxIRI *url.URL, - activity Activity, -) ([]*url.URL, error) { - // Iterate through to, bto, cc, bcc, and audience - // to extract a slice of addressee IRIs / IDs. - // - // The resulting slice might look something like: - // - // [ - // "https://example.org/users/someone/followers", // <-- collection IRI - // "https://another.example.org/users/someone_else", // <-- actor IRI - // "[...]" // <-- etc - // ] - var actorsAndCollections []*url.URL - if to := activity.GetActivityStreamsTo(); to != nil { - for iter := to.Begin(); iter != to.End(); iter = iter.Next() { - var err error - actorsAndCollections, err = appendToActorsAndCollectionsIRIs( - iter, actorsAndCollections, - ) - if err != nil { - return nil, err - } - } - } - - if bto := activity.GetActivityStreamsBto(); bto != nil { - for iter := bto.Begin(); iter != bto.End(); iter = iter.Next() { - var err error - actorsAndCollections, err = appendToActorsAndCollectionsIRIs( - iter, actorsAndCollections, - ) - if err != nil { - return nil, err - } - } - } - - if cc := activity.GetActivityStreamsCc(); cc != nil { - for iter := cc.Begin(); iter != cc.End(); iter = iter.Next() { - var err error - actorsAndCollections, err = appendToActorsAndCollectionsIRIs( - iter, actorsAndCollections, - ) - if err != nil { - return nil, err - } - } - } - - if bcc := activity.GetActivityStreamsBcc(); bcc != nil { - for iter := bcc.Begin(); iter != bcc.End(); iter = iter.Next() { - var err error - actorsAndCollections, err = appendToActorsAndCollectionsIRIs( - iter, actorsAndCollections, - ) - if err != nil { - return nil, err - } - } - } - - if audience := activity.GetActivityStreamsAudience(); audience != nil { - for iter := audience.Begin(); iter != audience.End(); iter = iter.Next() { - var err error - actorsAndCollections, err = appendToActorsAndCollectionsIRIs( - iter, actorsAndCollections, - ) - if err != nil { - return nil, err - } - } - } - - // PRE-SORTING - - // If the pre-delivery sort function is defined, - // call it now so that implementations can sort - // delivery order to their preferences. - if a.DeliveryRecipientPreSort != nil { - actorsAndCollections = a.DeliveryRecipientPreSort(actorsAndCollections) - } - - // We now need to dereference the actor or collection - // IRIs to derive inboxes that we can POST requests to. - var ( - inboxes = make([]*url.URL, 0, len(actorsAndCollections)) - derefdEntries = make(map[string]struct{}, len(actorsAndCollections)) - ) - - // First check if the implemented database logic - // can return any of these inboxes without having - // to make remote dereference calls (much cheaper). - for _, actorOrCollection := range actorsAndCollections { - actorOrCollectionStr := actorOrCollection.String() - if _, derefd := derefdEntries[actorOrCollectionStr]; derefd { - // Ignore potential duplicates - // we've already derefd to inbox(es). - continue - } - - // BEGIN LOCK - unlock, err := a.db.Lock(ctx, actorOrCollection) - if err != nil { - return nil, err - } - - // Try to get inbox(es) for this actor or collection. - gotInboxes, err := a.db.InboxesForIRI(ctx, actorOrCollection) - - // END LOCK - unlock() - - if err != nil { - return nil, err - } - - if len(gotInboxes) == 0 { - // No hit(s). - continue - } - - // We have one or more hits. - inboxes = append(inboxes, gotInboxes...) - - // Mark this actor or collection as deref'd. - derefdEntries[actorOrCollectionStr] = struct{}{} - } - - // Now look for any remaining actors/collections - // that weren't already dereferenced into inboxes - // with db calls; find these by making deref calls - // to remote instances. - // - // First get a transport to do the http calls. - t, err := a.common.NewTransport(ctx, outboxIRI, goFedUserAgent()) - if err != nil { - return nil, err - } - - // Make HTTP calls to unpack collection IRIs into - // Actor IRIs and then into Actor types, ignoring - // actors or collections we've already deref'd. - actorsFromRemote, err := a.resolveActors( - ctx, - t, - actorsAndCollections, - derefdEntries, - 0, a.s2s.MaxDeliveryRecursionDepth(ctx), - ) - if err != nil { - return nil, err - } - - // Release no-longer-needed collections. - clear(derefdEntries) - clear(actorsAndCollections) - - // Extract inbox IRI from each deref'd Actor (if any). - inboxesFromRemote, err := actorsToInboxIRIs(actorsFromRemote) - if err != nil { - return nil, err - } - - // Combine db-discovered inboxes and remote-discovered - // inboxes into a final list of destination inboxes. - inboxes = append(inboxes, inboxesFromRemote...) - - // POST FILTERING - - // Do a final pass of the inboxes to: - // - // 1. Deduplicate entries. - // 2. Ensure that the list of inboxes doesn't - // contain the inbox of whoever the outbox - // belongs to, no point delivering to oneself. - // - // To do this we first need to get the - // inbox IRI of this outbox's Actor. - - // BEGIN LOCK - unlock, err := a.db.Lock(ctx, outboxIRI) - if err != nil { - return nil, err - } - - // Get the IRI of the Actor who owns this outbox. - outboxActorIRI, err := a.db.ActorForOutbox(ctx, outboxIRI) - - // END LOCK - unlock() - - if err != nil { - return nil, err - } - - // BEGIN LOCK - unlock, err = a.db.Lock(ctx, outboxActorIRI) - if err != nil { - return nil, err - } - - // Now get the Actor who owns this outbox. - outboxActor, err := a.db.Get(ctx, outboxActorIRI) - - // END LOCK - unlock() - - if err != nil { - return nil, err - } - - // Extract the inbox IRI for the outbox Actor. - inboxOfOutboxActor, err := getInbox(outboxActor) - if err != nil { - return nil, err - } - - // Deduplicate the final inboxes slice, and filter - // out of the inbox of this outbox actor (if present). - inboxes = filterInboxIRIs(inboxes, inboxOfOutboxActor) - - // Now that we've derived inboxes to deliver - // the activity to, strip off any bto or bcc - // recipients, as per the AP spec requirements. - stripHiddenRecipients(activity) - - // All done! - return inboxes, nil -} - -// resolveActors takes a list of Actor id URIs and returns them as concrete -// instances of actorObject. It attempts to apply recursively when it encounters -// a target that is a Collection or OrderedCollection. -// -// Any IRI strings in the ignores map will be skipped (use this when -// you've already dereferenced some of the actorAndCollectionIRIs). -// -// If maxDepth is zero or negative, then recursion is infinitely applied. -// -// If a recipient is a Collection or OrderedCollection, then the server MUST -// dereference the collection, WITH the user's credentials. -// -// Note that this also applies to CollectionPage and OrderedCollectionPage. -func (a *SideEffectActor) resolveActors( - ctx context.Context, - t Transport, - actorAndCollectionIRIs []*url.URL, - ignores map[string]struct{}, - depth, maxDepth int, -) ([]vocab.Type, error) { - if maxDepth > 0 && depth >= maxDepth { - // Hit our max depth. - return nil, nil - } - - if len(actorAndCollectionIRIs) == 0 { - // Nothing to do. - return nil, nil - } - - // Optimistically assume 1:1 mapping of IRIs to actors. - actors := make([]vocab.Type, 0, len(actorAndCollectionIRIs)) - - // Deref each actorOrCollectionIRI if not ignored. - for _, actorOrCollectionIRI := range actorAndCollectionIRIs { - _, ignore := ignores[actorOrCollectionIRI.String()] - if ignore { - // Don't try to - // deref this one. - continue - } - - // TODO: Determine if more logic is needed here for - // inaccessible collections owned by peer servers. - actor, more, err := a.dereferenceForResolvingInboxes(ctx, t, actorOrCollectionIRI) - if err != nil { - // Missing recipient -- skip. - continue - } - - if actor != nil { - // Got a hit. - actors = append(actors, actor) - } - - // If this was a collection, get more. - recurActors, err := a.resolveActors( - ctx, - t, - more, - ignores, - depth+1, maxDepth, - ) - if err != nil { - return nil, err - } - - actors = append(actors, recurActors...) - } - - return actors, nil -} - -// dereferenceForResolvingInboxes dereferences an IRI solely for finding an -// actor's inbox IRI to deliver to. -// -// The returned actor could be nil, if it wasn't an actor (ex: a Collection or -// OrderedCollection). -func (a *SideEffectActor) dereferenceForResolvingInboxes(c context.Context, t Transport, actorIRI *url.URL) (actor vocab.Type, moreActorIRIs []*url.URL, err error) { - var resp *http.Response - resp, err = t.Dereference(c, actorIRI) - if err != nil { - return - } - var m map[string]interface{} - m, err = readActivityPubResp(resp) - if err != nil { - return - } - actor, err = streams.ToType(c, m) - if err != nil { - return - } - // Attempt to see if the 'actor' is really some sort of type that has - // an 'items' or 'orderedItems' property. - if v, ok := actor.(itemser); ok { - if i := v.GetActivityStreamsItems(); i != nil { - for iter := i.Begin(); iter != i.End(); iter = iter.Next() { - var id *url.URL - id, err = ToId(iter) - if err != nil { - return - } - moreActorIRIs = append(moreActorIRIs, id) - } - } - actor = nil - } else if v, ok := actor.(orderedItemser); ok { - if i := v.GetActivityStreamsOrderedItems(); i != nil { - for iter := i.Begin(); iter != i.End(); iter = iter.Next() { - var id *url.URL - id, err = ToId(iter) - if err != nil { - return - } - moreActorIRIs = append(moreActorIRIs, id) - } - } - actor = nil - } - return -} diff --git a/vendor/codeberg.org/superseriousbusiness/activity/pub/social_protocol.go b/vendor/codeberg.org/superseriousbusiness/activity/pub/social_protocol.go deleted file mode 100644 index 1f882b284..000000000 --- a/vendor/codeberg.org/superseriousbusiness/activity/pub/social_protocol.go +++ /dev/null @@ -1,83 +0,0 @@ -package pub - -import ( - "context" - "net/http" - - "codeberg.org/superseriousbusiness/activity/streams/vocab" -) - -// SocialProtocol contains behaviors an application needs to satisfy for the -// full ActivityPub C2S implementation to be supported by this library. -// -// It is only required if the client application wants to support the client-to- -// server, or social, protocol. -// -// It is passed to the library as a dependency injection from the client -// application. -type SocialProtocol interface { - // Hook callback after parsing the request body for a client request - // to the Actor's outbox. - // - // Can be used to set contextual information based on the - // ActivityStreams object received. - // - // Only called if the Social API is enabled. - // - // Warning: Neither authentication nor authorization has taken place at - // this time. Doing anything beyond setting contextual information is - // strongly discouraged. - // - // If an error is returned, it is passed back to the caller of - // PostOutbox. In this case, the DelegateActor implementation must not - // write a response to the ResponseWriter as is expected that the caller - // to PostOutbox will do so when handling the error. - PostOutboxRequestBodyHook(c context.Context, r *http.Request, data vocab.Type) (context.Context, error) - // AuthenticatePostOutbox delegates the authentication of a POST to an - // outbox. - // - // Only called if the Social API is enabled. - // - // If an error is returned, it is passed back to the caller of - // PostOutbox. In this case, the implementation must not write a - // response to the ResponseWriter as is expected that the client will - // do so when handling the error. The 'authenticated' is ignored. - // - // If no error is returned, but authentication or authorization fails, - // then authenticated must be false and error nil. It is expected that - // the implementation handles writing to the ResponseWriter in this - // case. - // - // Finally, if the authentication and authorization succeeds, then - // authenticated must be true and error nil. The request will continue - // to be processed. - AuthenticatePostOutbox(c context.Context, w http.ResponseWriter, r *http.Request) (out context.Context, authenticated bool, err error) - // SocialCallbacks returns the application logic that handles - // ActivityStreams received from C2S clients. - // - // Note that certain types of callbacks will be 'wrapped' with default - // behaviors supported natively by the library. Other callbacks - // compatible with streams.TypeResolver can be specified by 'other'. - // - // For example, setting the 'Create' field in the SocialWrappedCallbacks - // lets an application dependency inject additional behaviors they want - // to take place, including the default behavior supplied by this - // library. This is guaranteed to be compliant with the ActivityPub - // Social protocol. - // - // To override the default behavior, instead supply the function in - // 'other', which does not guarantee the application will be compliant - // with the ActivityPub Social Protocol. - // - // Applications are not expected to handle every single ActivityStreams - // type and extension. The unhandled ones are passed to DefaultCallback. - SocialCallbacks(c context.Context) (wrapped SocialWrappedCallbacks, other []interface{}, err error) - // DefaultCallback is called for types that go-fed can deserialize but - // are not handled by the application's callbacks returned in the - // Callbacks method. - // - // Applications are not expected to handle every single ActivityStreams - // type and extension, so the unhandled ones are passed to - // DefaultCallback. - DefaultCallback(c context.Context, activity Activity) error -} diff --git a/vendor/codeberg.org/superseriousbusiness/activity/pub/social_wrapped_callbacks.go b/vendor/codeberg.org/superseriousbusiness/activity/pub/social_wrapped_callbacks.go deleted file mode 100644 index 9f93a9c65..000000000 --- a/vendor/codeberg.org/superseriousbusiness/activity/pub/social_wrapped_callbacks.go +++ /dev/null @@ -1,534 +0,0 @@ -package pub - -import ( - "context" - "fmt" - "net/url" - - "codeberg.org/superseriousbusiness/activity/streams" - "codeberg.org/superseriousbusiness/activity/streams/vocab" -) - -// SocialWrappedCallbacks lists the callback functions that already have some -// side effect behavior provided by the pub library. -// -// These functions are wrapped for the Social Protocol. -type SocialWrappedCallbacks struct { - // Create handles additional side effects for the Create ActivityStreams - // type. - // - // The wrapping callback copies the actor(s) to the 'attributedTo' - // property and copies recipients between the Create activity and all - // objects. It then saves the entry in the database. - Create func(context.Context, vocab.ActivityStreamsCreate) error - // Update handles additional side effects for the Update ActivityStreams - // type. - // - // The wrapping callback applies new top-level values on an object to - // the stored objects. Any top-level null literals will be deleted on - // the stored objects as well. - Update func(context.Context, vocab.ActivityStreamsUpdate) error - // Delete handles additional side effects for the Delete ActivityStreams - // type. - // - // The wrapping callback replaces the object(s) with tombstones in the - // database. - Delete func(context.Context, vocab.ActivityStreamsDelete) error - // Follow handles additional side effects for the Follow ActivityStreams - // type. - // - // The wrapping callback only ensures the 'Follow' has at least one - // 'object' entry, but otherwise has no default side effect. - Follow func(context.Context, vocab.ActivityStreamsFollow) error - // Add handles additional side effects for the Add ActivityStreams - // type. - // - // - // The wrapping function will add the 'object' IRIs to a specific - // 'target' collection if the 'target' collection(s) live on this - // server. - Add func(context.Context, vocab.ActivityStreamsAdd) error - // Remove handles additional side effects for the Remove ActivityStreams - // type. - // - // The wrapping function will remove all 'object' IRIs from a specific - // 'target' collection if the 'target' collection(s) live on this - // server. - Remove func(context.Context, vocab.ActivityStreamsRemove) error - // Like handles additional side effects for the Like ActivityStreams - // type. - // - // The wrapping function will add the objects on the activity to the - // "liked" collection of this actor. - Like func(context.Context, vocab.ActivityStreamsLike) error - // Undo handles additional side effects for the Undo ActivityStreams - // type. - // - // - // The wrapping function ensures the 'actor' on the 'Undo' - // is be the same as the 'actor' on all Activities being undone. - // It enforces that the actors on the Undo must correspond to all of the - // 'object' actors in some manner. - // - // It is expected that the application will implement the proper - // reversal of activities that are being undone. - Undo func(context.Context, vocab.ActivityStreamsUndo) error - // Block handles additional side effects for the Block ActivityStreams - // type. - // - // The wrapping callback only ensures the 'Block' has at least one - // 'object' entry, but otherwise has no default side effect. It is up - // to the wrapped application function to properly enforce the new - // blocking behavior. - // - // Note that go-fed does not federate 'Block' activities received in the - // Social Protocol. - Block func(context.Context, vocab.ActivityStreamsBlock) error - - // Sidechannel data -- this is set at request handling time. These must - // be set before the callbacks are used. - - // db is the Database the SocialWrappedCallbacks should use. It must be - // set before calling the callbacks. - db Database - // outboxIRI is the outboxIRI that is handling this callback. - outboxIRI *url.URL - // rawActivity is the JSON map literal received when deserializing the - // request body. - rawActivity map[string]interface{} - // clock is the server's clock. - clock Clock - // newTransport creates a new Transport. - newTransport func(c context.Context, actorBoxIRI *url.URL, gofedAgent string) (t Transport, err error) - // undeliverable is a sidechannel out, indicating if the handled activity - // should not be delivered to a peer. - // - // Its provided default value will always be used when a custom function - // is called. - undeliverable *bool -} - -// callbacks returns the WrappedCallbacks members into a single interface slice -// for use in streams.Resolver callbacks. -// -// If the given functions have a type that collides with the default behavior, -// then disable our default behavior -func (w SocialWrappedCallbacks) callbacks(fns []interface{}) []interface{} { - enableCreate := true - enableUpdate := true - enableDelete := true - enableFollow := true - enableAdd := true - enableRemove := true - enableLike := true - enableUndo := true - enableBlock := true - for _, fn := range fns { - switch fn.(type) { - default: - continue - case func(context.Context, vocab.ActivityStreamsCreate) error: - enableCreate = false - case func(context.Context, vocab.ActivityStreamsUpdate) error: - enableUpdate = false - case func(context.Context, vocab.ActivityStreamsDelete) error: - enableDelete = false - case func(context.Context, vocab.ActivityStreamsFollow) error: - enableFollow = false - case func(context.Context, vocab.ActivityStreamsAdd) error: - enableAdd = false - case func(context.Context, vocab.ActivityStreamsRemove) error: - enableRemove = false - case func(context.Context, vocab.ActivityStreamsLike) error: - enableLike = false - case func(context.Context, vocab.ActivityStreamsUndo) error: - enableUndo = false - case func(context.Context, vocab.ActivityStreamsBlock) error: - enableBlock = false - } - } - if enableCreate { - fns = append(fns, w.create) - } - if enableUpdate { - fns = append(fns, w.update) - } - if enableDelete { - fns = append(fns, w.deleteFn) - } - if enableFollow { - fns = append(fns, w.follow) - } - if enableAdd { - fns = append(fns, w.add) - } - if enableRemove { - fns = append(fns, w.remove) - } - if enableLike { - fns = append(fns, w.like) - } - if enableUndo { - fns = append(fns, w.undo) - } - if enableBlock { - fns = append(fns, w.block) - } - return fns -} - -// create implements the social Create activity side effects. -func (w SocialWrappedCallbacks) create(c context.Context, a vocab.ActivityStreamsCreate) error { - *w.undeliverable = false - op := a.GetActivityStreamsObject() - if op == nil || op.Len() == 0 { - return ErrObjectRequired - } - // Obtain all actor IRIs. - actors := a.GetActivityStreamsActor() - createActorIds := make(map[string]*url.URL) - if actors != nil { - createActorIds = make(map[string]*url.URL, actors.Len()) - for iter := actors.Begin(); iter != actors.End(); iter = iter.Next() { - id, err := ToId(iter) - if err != nil { - return err - } - createActorIds[id.String()] = id - } - } - // Obtain each object's 'attributedTo' IRIs. - objectAttributedToIds := make([]map[string]*url.URL, op.Len()) - for i := range objectAttributedToIds { - objectAttributedToIds[i] = make(map[string]*url.URL) - } - for i := 0; i < op.Len(); i++ { - t := op.At(i).GetType() - attrToer, ok := t.(attributedToer) - if !ok { - continue - } - attr := attrToer.GetActivityStreamsAttributedTo() - if attr == nil { - attr = streams.NewActivityStreamsAttributedToProperty() - attrToer.SetActivityStreamsAttributedTo(attr) - } - for iter := attr.Begin(); iter != attr.End(); iter = iter.Next() { - id, err := ToId(iter) - if err != nil { - return err - } - objectAttributedToIds[i][id.String()] = id - } - } - // Put all missing actor IRIs onto all object attributedTo properties. - for k, v := range createActorIds { - for i, attributedToMap := range objectAttributedToIds { - if _, ok := attributedToMap[k]; !ok { - t := op.At(i).GetType() - attrToer, ok := t.(attributedToer) - if !ok { - continue - } - attr := attrToer.GetActivityStreamsAttributedTo() - attr.AppendIRI(v) - } - } - } - // Put all missing object attributedTo IRIs onto the actor property - // if there is one. - if actors != nil { - for _, attributedToMap := range objectAttributedToIds { - for k, v := range attributedToMap { - if _, ok := createActorIds[k]; !ok { - actors.AppendIRI(v) - } - } - } - } - // Copy over the 'to', 'bto', 'cc', 'bcc', and 'audience' recipients - // between the activity and all child objects and vice versa. - if err := normalizeRecipients(a); err != nil { - return err - } - // Create anonymous loop function to be able to properly scope the defer - // for the database lock at each iteration. - loopFn := func(i int) error { - obj := op.At(i).GetType() - id, err := GetId(obj) - if err != nil { - return err - } - var unlock func() - unlock, err = w.db.Lock(c, id) - if err != nil { - return err - } - defer unlock() - if err := w.db.Create(c, obj); err != nil { - return err - } - return nil - } - // Persist all objects we've created, which will include sensitive - // recipients such as 'bcc' and 'bto'. - for i := 0; i < op.Len(); i++ { - if err := loopFn(i); err != nil { - return err - } - } - if w.Create != nil { - return w.Create(c, a) - } - return nil -} - -// update implements the social Update activity side effects. -func (w SocialWrappedCallbacks) update(c context.Context, a vocab.ActivityStreamsUpdate) error { - *w.undeliverable = false - op := a.GetActivityStreamsObject() - if op == nil || op.Len() == 0 { - return ErrObjectRequired - } - // Obtain all object ids, which should be owned by this server. - objIds := make([]*url.URL, 0, op.Len()) - for iter := op.Begin(); iter != op.End(); iter = iter.Next() { - id, err := ToId(iter) - if err != nil { - return err - } - objIds = append(objIds, id) - } - // Create anonymous loop function to be able to properly scope the defer - // for the database lock at each iteration. - loopFn := func(idx int, loopId *url.URL) error { - unlock, err := w.db.Lock(c, loopId) - if err != nil { - return err - } - defer unlock() - t, err := w.db.Get(c, loopId) - if err != nil { - return err - } - m, err := t.Serialize() - if err != nil { - return err - } - // Copy over new top-level values. - objType := op.At(idx).GetType() - if objType == nil { - return fmt.Errorf("object at index %d is not a literal type value", idx) - } - newM, err := objType.Serialize() - if err != nil { - return err - } - for k, v := range newM { - m[k] = v - } - // Delete top-level values where the raw Activity had nils. - for k, v := range w.rawActivity { - if _, ok := m[k]; v == nil && ok { - delete(m, k) - } - } - newT, err := streams.ToType(c, m) - if err != nil { - return err - } - if err = w.db.Update(c, newT); err != nil { - return err - } - return nil - } - for i, id := range objIds { - if err := loopFn(i, id); err != nil { - return err - } - } - if w.Update != nil { - return w.Update(c, a) - } - return nil -} - -// deleteFn implements the social Delete activity side effects. -func (w SocialWrappedCallbacks) deleteFn(c context.Context, a vocab.ActivityStreamsDelete) error { - *w.undeliverable = false - op := a.GetActivityStreamsObject() - if op == nil || op.Len() == 0 { - return ErrObjectRequired - } - // Obtain all object ids, which should be owned by this server. - objIds := make([]*url.URL, 0, op.Len()) - for iter := op.Begin(); iter != op.End(); iter = iter.Next() { - id, err := ToId(iter) - if err != nil { - return err - } - objIds = append(objIds, id) - } - // Create anonymous loop function to be able to properly scope the defer - // for the database lock at each iteration. - loopFn := func(idx int, loopId *url.URL) error { - unlock, err := w.db.Lock(c, loopId) - if err != nil { - return err - } - defer unlock() - t, err := w.db.Get(c, loopId) - if err != nil { - return err - } - tomb := toTombstone(t, loopId, w.clock.Now()) - if err := w.db.Update(c, tomb); err != nil { - return err - } - return nil - } - for i, id := range objIds { - if err := loopFn(i, id); err != nil { - return err - } - } - if w.Delete != nil { - return w.Delete(c, a) - } - return nil -} - -// follow implements the social Follow activity side effects. -func (w SocialWrappedCallbacks) follow(c context.Context, a vocab.ActivityStreamsFollow) error { - *w.undeliverable = false - op := a.GetActivityStreamsObject() - if op == nil || op.Len() == 0 { - return ErrObjectRequired - } - if w.Follow != nil { - return w.Follow(c, a) - } - return nil -} - -// add implements the social Add activity side effects. -func (w SocialWrappedCallbacks) add(c context.Context, a vocab.ActivityStreamsAdd) error { - *w.undeliverable = false - op := a.GetActivityStreamsObject() - if op == nil || op.Len() == 0 { - return ErrObjectRequired - } - target := a.GetActivityStreamsTarget() - if target == nil || target.Len() == 0 { - return ErrTargetRequired - } - if err := add(c, op, target, w.db); err != nil { - return err - } - if w.Add != nil { - return w.Add(c, a) - } - return nil -} - -// remove implements the social Remove activity side effects. -func (w SocialWrappedCallbacks) remove(c context.Context, a vocab.ActivityStreamsRemove) error { - *w.undeliverable = false - op := a.GetActivityStreamsObject() - if op == nil || op.Len() == 0 { - return ErrObjectRequired - } - target := a.GetActivityStreamsTarget() - if target == nil || target.Len() == 0 { - return ErrTargetRequired - } - if err := remove(c, op, target, w.db); err != nil { - return err - } - if w.Remove != nil { - return w.Remove(c, a) - } - return nil -} - -// like implements the social Like activity side effects. -func (w SocialWrappedCallbacks) like(c context.Context, a vocab.ActivityStreamsLike) error { - *w.undeliverable = false - op := a.GetActivityStreamsObject() - if op == nil || op.Len() == 0 { - return ErrObjectRequired - } - // Get this actor's IRI. - unlock, err := w.db.Lock(c, w.outboxIRI) - if err != nil { - return err - } - // WARNING: Unlock not deferred. - actorIRI, err := w.db.ActorForOutbox(c, w.outboxIRI) - unlock() // unlock even on error - if err != nil { - return err - } - // Unlock must be called by now and every branch above. - // - // Now obtain this actor's 'liked' collection. - unlock, err = w.db.Lock(c, actorIRI) - if err != nil { - return err - } - defer unlock() - liked, err := w.db.Liked(c, actorIRI) - if err != nil { - return err - } - likedItems := liked.GetActivityStreamsItems() - if likedItems == nil { - likedItems = streams.NewActivityStreamsItemsProperty() - liked.SetActivityStreamsItems(likedItems) - } - for iter := op.Begin(); iter != op.End(); iter = iter.Next() { - objId, err := ToId(iter) - if err != nil { - return err - } - likedItems.PrependIRI(objId) - } - err = w.db.Update(c, liked) - if err != nil { - return err - } - if w.Like != nil { - return w.Like(c, a) - } - return nil -} - -// undo implements the social Undo activity side effects. -func (w SocialWrappedCallbacks) undo(c context.Context, a vocab.ActivityStreamsUndo) error { - *w.undeliverable = false - op := a.GetActivityStreamsObject() - if op == nil || op.Len() == 0 { - return ErrObjectRequired - } - actors := a.GetActivityStreamsActor() - if err := mustHaveActivityActorsMatchObjectActors(c, actors, op, w.newTransport, w.outboxIRI); err != nil { - return err - } - if w.Undo != nil { - return w.Undo(c, a) - } - return nil -} - -// block implements the social Block activity side effects. -func (w SocialWrappedCallbacks) block(c context.Context, a vocab.ActivityStreamsBlock) error { - *w.undeliverable = true - op := a.GetActivityStreamsObject() - if op == nil || op.Len() == 0 { - return ErrObjectRequired - } - if w.Block != nil { - return w.Block(c, a) - } - return nil -} diff --git a/vendor/codeberg.org/superseriousbusiness/activity/pub/transport.go b/vendor/codeberg.org/superseriousbusiness/activity/pub/transport.go deleted file mode 100644 index 101ff5c07..000000000 --- a/vendor/codeberg.org/superseriousbusiness/activity/pub/transport.go +++ /dev/null @@ -1,219 +0,0 @@ -package pub - -import ( - "bytes" - "context" - "crypto" - "encoding/json" - "fmt" - "net/http" - "net/url" - "strings" - "sync" - - "github.com/go-fed/httpsig" -) - -const ( - // acceptHeaderValue is the Accept header value indicating that the - // response should contain an ActivityStreams object. - acceptHeaderValue = "application/ld+json; profile=\"https://www.w3.org/ns/activitystreams\"" -) - -// isSuccess returns true if the HTTP status code is either OK, Created, or -// Accepted. -func isSuccess(code int) bool { - return code == http.StatusOK || - code == http.StatusCreated || - code == http.StatusAccepted -} - -// Transport makes ActivityStreams calls to other servers in order to send or -// receive ActivityStreams data. -// -// It is responsible for setting the appropriate request headers, signing the -// requests if needed, and facilitating the traffic between this server and -// another. -// -// The transport is exclusively used to issue requests on behalf of an actor, -// and is never sending requests on behalf of the server in general. -// -// It may be reused multiple times, but never concurrently. -type Transport interface { - // Dereference fetches the ActivityStreams object located at this IRI with - // a GET request. Note that Response will only be returned on status = OK. - Dereference(c context.Context, iri *url.URL) (*http.Response, error) - - // Deliver sends an ActivityStreams object. - Deliver(c context.Context, obj map[string]interface{}, to *url.URL) error - - // BatchDeliver sends an ActivityStreams object to multiple recipients. - BatchDeliver(c context.Context, obj map[string]interface{}, recipients []*url.URL) error -} - -// Transport must be implemented by HttpSigTransport. -var _ Transport = &HttpSigTransport{} - -// HttpSigTransport makes a dereference call using HTTP signatures to -// authenticate the request on behalf of a particular actor. -// -// No rate limiting is applied. -// -// Only one request is tried per call. -type HttpSigTransport struct { - client HttpClient - appAgent string - gofedAgent string - clock Clock - getSigner httpsig.Signer - getSignerMu *sync.Mutex - postSigner httpsig.Signer - postSignerMu *sync.Mutex - pubKeyId string - privKey crypto.PrivateKey -} - -// NewHttpSigTransport returns a new Transport. -// -// It sends requests specifically on behalf of a specific actor on this server. -// The actor's credentials are used to add an HTTP Signature to requests, which -// requires an actor's private key, a unique identifier for their public key, -// and an HTTP Signature signing algorithm. -// -// The client lets users issue requests through any HTTP client, including the -// standard library's HTTP client. -// -// The appAgent uniquely identifies the calling application's requests, so peers -// may aid debugging the requests incoming from this server. Note that the -// agent string will also include one for go-fed, so at minimum peer servers can -// reach out to the go-fed library to aid in notifying implementors of malformed -// or unsupported requests. -func NewHttpSigTransport( - client HttpClient, - appAgent string, - clock Clock, - getSigner, postSigner httpsig.Signer, - pubKeyId string, - privKey crypto.PrivateKey) *HttpSigTransport { - return &HttpSigTransport{ - client: client, - appAgent: appAgent, - gofedAgent: goFedUserAgent(), - clock: clock, - getSigner: getSigner, - getSignerMu: &sync.Mutex{}, - postSigner: postSigner, - postSignerMu: &sync.Mutex{}, - pubKeyId: pubKeyId, - privKey: privKey, - } -} - -// Dereference sends a GET request signed with an HTTP Signature to obtain an ActivityStreams value. -func (h HttpSigTransport) Dereference(c context.Context, iri *url.URL) (*http.Response, error) { - req, err := http.NewRequest("GET", iri.String(), nil) - if err != nil { - return nil, err - } - req = req.WithContext(c) - req.Header.Add(acceptHeader, acceptHeaderValue) - req.Header.Add("Accept-Charset", "utf-8") - req.Header.Add("Date", h.clock.Now().UTC().Format("Mon, 02 Jan 2006 15:04:05")+" GMT") - req.Header.Add("User-Agent", fmt.Sprintf("%s %s", h.appAgent, h.gofedAgent)) - req.Header.Set("Host", iri.Host) - h.getSignerMu.Lock() - err = h.getSigner.SignRequest(h.privKey, h.pubKeyId, req, nil) - h.getSignerMu.Unlock() - if err != nil { - return nil, err - } - resp, err := h.client.Do(req) - if err != nil { - return nil, err - } - if resp.StatusCode != http.StatusOK { - _ = resp.Body.Close() - return nil, fmt.Errorf("GET request to %s failed (%d): %s", iri.String(), resp.StatusCode, resp.Status) - } - return resp, nil -} - -// Deliver sends a POST request with an HTTP Signature. -func (h HttpSigTransport) Deliver(c context.Context, data map[string]interface{}, to *url.URL) error { - b, err := json.Marshal(data) - if err != nil { - return err - } - return h.deliver(c, b, to) -} - -// BatchDeliver sends concurrent POST requests. Returns an error if any of the requests had an error. -func (h HttpSigTransport) BatchDeliver(c context.Context, data map[string]interface{}, recipients []*url.URL) error { - b, err := json.Marshal(data) - if err != nil { - return err - } - var wg sync.WaitGroup - errCh := make(chan error, len(recipients)) - for _, recipient := range recipients { - wg.Add(1) - go func(r *url.URL) { - defer wg.Done() - if err := h.deliver(c, b, r); err != nil { - errCh <- err - } - }(recipient) - } - wg.Wait() - errs := make([]string, 0, len(recipients)) -outer: - for { - select { - case e := <-errCh: - errs = append(errs, e.Error()) - default: - break outer - } - } - if len(errs) > 0 { - return fmt.Errorf("batch deliver had at least one failure: %s", strings.Join(errs, "; ")) - } - return nil -} - -func (h HttpSigTransport) deliver(c context.Context, b []byte, to *url.URL) error { - req, err := http.NewRequest("POST", to.String(), bytes.NewReader(b)) - if err != nil { - return err - } - req = req.WithContext(c) - req.Header.Add(contentTypeHeader, contentTypeHeaderValue) - req.Header.Add("Accept-Charset", "utf-8") - req.Header.Add("Date", h.clock.Now().UTC().Format("Mon, 02 Jan 2006 15:04:05")+" GMT") - req.Header.Add("User-Agent", fmt.Sprintf("%s %s", h.appAgent, h.gofedAgent)) - req.Header.Set("Host", to.Host) - h.postSignerMu.Lock() - err = h.postSigner.SignRequest(h.privKey, h.pubKeyId, req, b) - h.postSignerMu.Unlock() - if err != nil { - return err - } - resp, err := h.client.Do(req) - if err != nil { - return err - } - defer resp.Body.Close() - if !isSuccess(resp.StatusCode) { - return fmt.Errorf("POST request to %s failed (%d): %s", to.String(), resp.StatusCode, resp.Status) - } - return nil -} - -// HttpClient sends http requests, and is an abstraction only needed by the -// HttpSigTransport. The standard library's Client satisfies this interface. -type HttpClient interface { - Do(req *http.Request) (*http.Response, error) -} - -// HttpClient must be implemented by http.Client. -var _ HttpClient = &http.Client{} diff --git a/vendor/codeberg.org/superseriousbusiness/activity/pub/util.go b/vendor/codeberg.org/superseriousbusiness/activity/pub/util.go deleted file mode 100644 index 1a5ea7640..000000000 --- a/vendor/codeberg.org/superseriousbusiness/activity/pub/util.go +++ /dev/null @@ -1,1077 +0,0 @@ -package pub - -import ( - "bytes" - "context" - "crypto/sha256" - "encoding/base64" - "encoding/json" - "errors" - "fmt" - "io" - "net/http" - "net/url" - "strings" - "time" - - "codeberg.org/superseriousbusiness/activity/streams" - "codeberg.org/superseriousbusiness/activity/streams/vocab" -) - -var ( - // ErrObjectRequired indicates the activity needs its object property - // set. Can be returned by DelegateActor's PostInbox or PostOutbox so a - // Bad Request response is set. - ErrObjectRequired = errors.New("object property required on the provided activity") - // ErrTargetRequired indicates the activity needs its target property - // set. Can be returned by DelegateActor's PostInbox or PostOutbox so a - // Bad Request response is set. - ErrTargetRequired = errors.New("target property required on the provided activity") -) - -// activityStreamsMediaTypes contains all of the accepted ActivityStreams media -// types. Generated at init time. -var activityStreamsMediaTypes []string - -func init() { - activityStreamsMediaTypes = []string{ - "application/activity+json", - } - jsonLdType := "application/ld+json" - for _, semi := range []string{";", " ;", " ; ", "; "} { - for _, profile := range []string{ - "profile=https://www.w3.org/ns/activitystreams", - "profile=\"https://www.w3.org/ns/activitystreams\"", - } { - activityStreamsMediaTypes = append( - activityStreamsMediaTypes, - fmt.Sprintf("%s%s%s", jsonLdType, semi, profile)) - } - } -} - -// headerIsActivityPubMediaType returns true if the header string contains one -// of the accepted ActivityStreams media types. -// -// Note we don't try to build a comprehensive parser and instead accept a -// tolerable amount of whitespace since the HTTP specification is ambiguous -// about the format and significance of whitespace. -func headerIsActivityPubMediaType(header string) bool { - for _, mediaType := range activityStreamsMediaTypes { - if strings.Contains(header, mediaType) { - return true - } - } - return false -} - -const ( - // The Content-Type header. - contentTypeHeader = "Content-Type" - // The Accept header. - acceptHeader = "Accept" -) - -// readActivityPubReq reads ActivityPub data from an incoming request, handling body close. -func readActivityPubReq(req *http.Request) (map[string]interface{}, error) { - // Ensure closed when done. - defer req.Body.Close() - - var m map[string]interface{} - - // Wrap body in a JSON decoder. - dec := json.NewDecoder(req.Body) - - // Decode JSON body as "raw" AP data map. - if err := dec.Decode(&m); err != nil { - return nil, err - } - - // Perform a final second decode to ensure no trailing - // garbage data or second JSON value (indicates malformed). - if err := dec.Decode(&struct{}{}); err != io.EOF { - return nil, errors.New("trailing data after json") - } - - return m, nil -} - -// readActivityPubResp reads ActivityPub data from a dereference response, handling media type check and body close. -func readActivityPubResp(resp *http.Response) (map[string]interface{}, error) { - // Ensure closed when done. - defer resp.Body.Close() - - // Check the incoming response media type is the expected ActivityPub content-type. - if mediaType := resp.Header.Get("Content-Type"); !headerIsActivityPubMediaType(mediaType) { - return nil, fmt.Errorf("%s %s resp was not ActivityPub media type: %s", resp.Request.Method, resp.Request.URL, mediaType) - } - - var m map[string]interface{} - - // Wrap body in a JSON decoder. - dec := json.NewDecoder(resp.Body) - - // Decode JSON body as "raw" AP data map. - if err := dec.Decode(&m); err != nil { - return nil, err - } - - // Perform a final second decode to ensure no trailing - // garbage data or second JSON value (indicates malformed). - if err := dec.Decode(&struct{}{}); err != io.EOF { - return nil, errors.New("trailing data after json") - } - - return m, nil -} - -// isActivityPubPost returns true if the request is a POST request that has the -// ActivityStreams content type header -func isActivityPubPost(r *http.Request) bool { - return r.Method == "POST" && headerIsActivityPubMediaType(r.Header.Get(contentTypeHeader)) -} - -// isActivityPubGet returns true if the request is a GET request that has the -// ActivityStreams content type header -func isActivityPubGet(r *http.Request) bool { - return r.Method == "GET" && headerIsActivityPubMediaType(r.Header.Get(acceptHeader)) -} - -// dedupeOrderedItems deduplicates the 'orderedItems' within an ordered -// collection type. Deduplication happens by the 'id' property. -func dedupeOrderedItems(oc orderedItemser) error { - oi := oc.GetActivityStreamsOrderedItems() - if oi == nil { - return nil - } - seen := make(map[string]bool, oi.Len()) - for i := 0; i < oi.Len(); { - var id *url.URL - - iter := oi.At(i) - asType := iter.GetType() - if asType != nil { - var err error - id, err = GetId(asType) - if err != nil { - return err - } - } else if iter.IsIRI() { - id = iter.GetIRI() - } else { - return fmt.Errorf("element %d in OrderedCollection does not have an ID nor is an IRI", i) - } - if seen[id.String()] { - oi.Remove(i) - } else { - seen[id.String()] = true - i++ - } - } - return nil -} - -const ( - // The Location header - locationHeader = "Location" - // Contains the ActivityStreams Content-Type value. - contentTypeHeaderValue = "application/ld+json; profile=\"https://www.w3.org/ns/activitystreams\"" - // The Date header. - dateHeader = "Date" - // The Digest header. - digestHeader = "Digest" - // The delimiter used in the Digest header. - digestDelimiter = "=" - // SHA-256 string for the Digest header. - sha256Digest = "SHA-256" -) - -// addResponseHeaders sets headers needed in the HTTP response, such but not -// limited to the Content-Type, Date, and Digest headers. -func addResponseHeaders(h http.Header, c Clock, responseContent []byte) { - h.Set(contentTypeHeader, contentTypeHeaderValue) - // RFC 7231 ยง7.1.1.2 - h.Set(dateHeader, c.Now().UTC().Format("Mon, 02 Jan 2006 15:04:05")+" GMT") - // RFC 3230 and RFC 5843 - var b bytes.Buffer - b.WriteString(sha256Digest) - b.WriteString(digestDelimiter) - hashed := sha256.Sum256(responseContent) - b.WriteString(base64.StdEncoding.EncodeToString(hashed[:])) - h.Set(digestHeader, b.String()) -} - -// IdProperty is a property that can readily have its id obtained -type IdProperty interface { - // GetIRI returns the IRI of this property. When IsIRI returns false, - // GetIRI will return an arbitrary value. - GetIRI() *url.URL - // GetType returns the value in this property as a Type. Returns nil if - // the value is not an ActivityStreams type, such as an IRI or another - // value. - GetType() vocab.Type - // IsIRI returns true if this property is an IRI. - IsIRI() bool -} - -// ToId returns an IdProperty's id. -func ToId(i IdProperty) (*url.URL, error) { - if i.GetType() != nil { - return GetId(i.GetType()) - } else if i.IsIRI() { - return i.GetIRI(), nil - } - return nil, fmt.Errorf("cannot determine id of activitystreams property") -} - -// GetId will attempt to find the 'id' property or, if it happens to be a -// Link or derived from Link type, the 'href' property instead. -// -// Returns an error if the id is not set and either the 'href' property is not -// valid on this type, or it is also not set. -func GetId(t vocab.Type) (*url.URL, error) { - if id := t.GetJSONLDId(); id != nil { - return id.Get(), nil - } else if h, ok := t.(hrefer); ok { - if href := h.GetActivityStreamsHref(); href != nil { - return href.Get(), nil - } - } - return nil, fmt.Errorf("cannot determine id of activitystreams value") -} - -// getInboxForwardingValues obtains the 'inReplyTo', 'object', 'target', and -// 'tag' values on an ActivityStreams value. -func getInboxForwardingValues(o vocab.Type) (t []vocab.Type, iri []*url.URL) { - // 'inReplyTo' - if i, ok := o.(inReplyToer); ok { - if irt := i.GetActivityStreamsInReplyTo(); irt != nil { - for iter := irt.Begin(); iter != irt.End(); iter = iter.Next() { - if tv := iter.GetType(); tv != nil { - t = append(t, tv) - } else { - iri = append(iri, iter.GetIRI()) - } - } - } - } - // 'tag' - if i, ok := o.(tagger); ok { - if tag := i.GetActivityStreamsTag(); tag != nil { - for iter := tag.Begin(); iter != tag.End(); iter = iter.Next() { - if tv := iter.GetType(); tv != nil { - t = append(t, tv) - } else { - iri = append(iri, iter.GetIRI()) - } - } - } - } - // 'object' - if i, ok := o.(objecter); ok { - if obj := i.GetActivityStreamsObject(); obj != nil { - for iter := obj.Begin(); iter != obj.End(); iter = iter.Next() { - if tv := iter.GetType(); tv != nil { - t = append(t, tv) - } else { - iri = append(iri, iter.GetIRI()) - } - } - } - } - // 'target' - if i, ok := o.(targeter); ok { - if tar := i.GetActivityStreamsTarget(); tar != nil { - for iter := tar.Begin(); iter != tar.End(); iter = iter.Next() { - if tv := iter.GetType(); tv != nil { - t = append(t, tv) - } else { - iri = append(iri, iter.GetIRI()) - } - } - } - } - return -} - -// wrapInCreate will automatically wrap the provided object in a Create -// activity. This will copy over the 'to', 'bto', 'cc', 'bcc', and 'audience' -// properties. It will also copy over the published time if present. -func wrapInCreate(ctx context.Context, o vocab.Type, actor *url.URL) (c vocab.ActivityStreamsCreate, err error) { - c = streams.NewActivityStreamsCreate() - // Object property - oProp := streams.NewActivityStreamsObjectProperty() - oProp.AppendType(o) - c.SetActivityStreamsObject(oProp) - // Actor Property - actorProp := streams.NewActivityStreamsActorProperty() - actorProp.AppendIRI(actor) - c.SetActivityStreamsActor(actorProp) - // Published Property - if v, ok := o.(publisheder); ok { - c.SetActivityStreamsPublished(v.GetActivityStreamsPublished()) - } - // Copying over properties. - if v, ok := o.(toer); ok { - if to := v.GetActivityStreamsTo(); to != nil { - activityTo := streams.NewActivityStreamsToProperty() - for iter := to.Begin(); iter != to.End(); iter = iter.Next() { - var id *url.URL - id, err = ToId(iter) - if err != nil { - return - } - activityTo.AppendIRI(id) - } - c.SetActivityStreamsTo(activityTo) - } - } - if v, ok := o.(btoer); ok { - if bto := v.GetActivityStreamsBto(); bto != nil { - activityBto := streams.NewActivityStreamsBtoProperty() - for iter := bto.Begin(); iter != bto.End(); iter = iter.Next() { - var id *url.URL - id, err = ToId(iter) - if err != nil { - return - } - activityBto.AppendIRI(id) - } - c.SetActivityStreamsBto(activityBto) - } - } - if v, ok := o.(ccer); ok { - if cc := v.GetActivityStreamsCc(); cc != nil { - activityCc := streams.NewActivityStreamsCcProperty() - for iter := cc.Begin(); iter != cc.End(); iter = iter.Next() { - var id *url.URL - id, err = ToId(iter) - if err != nil { - return - } - activityCc.AppendIRI(id) - } - c.SetActivityStreamsCc(activityCc) - } - } - if v, ok := o.(bccer); ok { - if bcc := v.GetActivityStreamsBcc(); bcc != nil { - activityBcc := streams.NewActivityStreamsBccProperty() - for iter := bcc.Begin(); iter != bcc.End(); iter = iter.Next() { - var id *url.URL - id, err = ToId(iter) - if err != nil { - return - } - activityBcc.AppendIRI(id) - } - c.SetActivityStreamsBcc(activityBcc) - } - } - if v, ok := o.(audiencer); ok { - if aud := v.GetActivityStreamsAudience(); aud != nil { - activityAudience := streams.NewActivityStreamsAudienceProperty() - for iter := aud.Begin(); iter != aud.End(); iter = iter.Next() { - var id *url.URL - id, err = ToId(iter) - if err != nil { - return - } - activityAudience.AppendIRI(id) - } - c.SetActivityStreamsAudience(activityAudience) - } - } - return -} - -const ( - // PublicActivityPubIRI is the IRI that indicates an Activity is meant - // to be visible for general public consumption. - PublicActivityPubIRI = "https://www.w3.org/ns/activitystreams#Public" - publicJsonLD = "Public" - publicJsonLDAS = "as:Public" -) - -// IsPublic determines if an IRI string is the Public collection as defined in -// the spec, including JSON-LD compliant collections. -func IsPublic(s string) bool { - return s == PublicActivityPubIRI || s == publicJsonLD || s == publicJsonLDAS -} - -// Derives an ID URI from the given IdProperty and, if it's not the -// magic AP Public IRI, appends it to the actorsAndCollections slice. -func appendToActorsAndCollectionsIRIs( - iter IdProperty, - actorsAndCollections []*url.URL, -) ([]*url.URL, error) { - id, err := ToId(iter) - if err != nil { - return nil, err - } - - // Ignore Public IRI as we - // can't deliver to it directly. - if !IsPublic(id.String()) { - actorsAndCollections = append(actorsAndCollections, id) - } - - return actorsAndCollections, nil -} - -// actorsToInboxIRIs extracts the 'inbox' IRIs from actor types. -func actorsToInboxIRIs(t []vocab.Type) (u []*url.URL, err error) { - for _, elem := range t { - var iri *url.URL - iri, err = getInbox(elem) - if err != nil { - return - } - u = append(u, iri) - } - return -} - -// getInbox extracts the 'inbox' IRI from an actor type. -func getInbox(t vocab.Type) (u *url.URL, err error) { - ib, ok := t.(inboxer) - if !ok { - err = fmt.Errorf("actor type %T has no inbox", t) - return - } - inbox := ib.GetActivityStreamsInbox() - return ToId(inbox) -} - -// filterInboxIRIs will deduplicate the given inboxes -// slice, while also leaving out any filtered IRIs. -func filterInboxIRIs( - inboxes []*url.URL, - filtered ...*url.URL, -) []*url.URL { - // Prepopulate the ignored map with each filtered IRI. - ignored := make(map[string]struct{}, len(filtered)+len(inboxes)) - for _, filteredIRI := range filtered { - ignored[filteredIRI.String()] = struct{}{} - } - - deduped := make([]*url.URL, 0, len(inboxes)) - for _, inbox := range inboxes { - inboxStr := inbox.String() - _, ignore := ignored[inboxStr] - if ignore { - // We already included - // this URI in out, or - // we should ignore it. - continue - } - - // Include this IRI in output, and - // add entry to the ignored map to - // ensure we don't include it again. - deduped = append(deduped, inbox) - ignored[inboxStr] = struct{}{} - } - - return deduped -} - -// stripHiddenRecipients removes "bto" and "bcc" from the activity. -// -// Note that this requirement of the specification is under "Section 6: Client -// to Server Interactions", the Social API, and not the Federative API. -func stripHiddenRecipients(activity Activity) { - activity.SetActivityStreamsBto(nil) - activity.SetActivityStreamsBcc(nil) - op := activity.GetActivityStreamsObject() - if op != nil { - for iter := op.Begin(); iter != op.End(); iter = iter.Next() { - if v, ok := iter.GetType().(btoer); ok { - v.SetActivityStreamsBto(nil) - } - if v, ok := iter.GetType().(bccer); ok { - v.SetActivityStreamsBcc(nil) - } - } - } -} - -// mustHaveActivityOriginMatchObjects ensures that the Host in the activity id -// IRI matches all of the Hosts in the object id IRIs. -func mustHaveActivityOriginMatchObjects(a Activity) error { - originIRI, err := GetId(a) - if err != nil { - return err - } - originHost := originIRI.Host - op := a.GetActivityStreamsObject() - if op == nil || op.Len() == 0 { - return nil - } - for iter := op.Begin(); iter != op.End(); iter = iter.Next() { - iri, err := ToId(iter) - if err != nil { - return err - } - if originHost != iri.Host { - return fmt.Errorf("object %q: not in activity origin", iri) - } - } - return nil -} - -// normalizeRecipients ensures the activity and object have the same 'to', -// 'bto', 'cc', 'bcc', and 'audience' properties. Copy the Activity's recipients -// to objects, and the objects to the activity, but does NOT copy objects' -// recipients to each other. -func normalizeRecipients(a vocab.ActivityStreamsCreate) error { - // Phase 0: Acquire all recipients on the activity. - // - // Obtain the actorTo map - actorToMap := make(map[string]*url.URL) - actorTo := a.GetActivityStreamsTo() - if actorTo == nil { - actorTo = streams.NewActivityStreamsToProperty() - a.SetActivityStreamsTo(actorTo) - } - for iter := actorTo.Begin(); iter != actorTo.End(); iter = iter.Next() { - id, err := ToId(iter) - if err != nil { - return err - } - actorToMap[id.String()] = id - } - // Obtain the actorBto map - actorBtoMap := make(map[string]*url.URL) - actorBto := a.GetActivityStreamsBto() - if actorBto == nil { - actorBto = streams.NewActivityStreamsBtoProperty() - a.SetActivityStreamsBto(actorBto) - } - for iter := actorBto.Begin(); iter != actorBto.End(); iter = iter.Next() { - id, err := ToId(iter) - if err != nil { - return err - } - actorBtoMap[id.String()] = id - } - // Obtain the actorCc map - actorCcMap := make(map[string]*url.URL) - actorCc := a.GetActivityStreamsCc() - if actorCc == nil { - actorCc = streams.NewActivityStreamsCcProperty() - a.SetActivityStreamsCc(actorCc) - } - for iter := actorCc.Begin(); iter != actorCc.End(); iter = iter.Next() { - id, err := ToId(iter) - if err != nil { - return err - } - actorCcMap[id.String()] = id - } - // Obtain the actorBcc map - actorBccMap := make(map[string]*url.URL) - actorBcc := a.GetActivityStreamsBcc() - if actorBcc == nil { - actorBcc = streams.NewActivityStreamsBccProperty() - a.SetActivityStreamsBcc(actorBcc) - } - for iter := actorBcc.Begin(); iter != actorBcc.End(); iter = iter.Next() { - id, err := ToId(iter) - if err != nil { - return err - } - actorBccMap[id.String()] = id - } - // Obtain the actorAudience map - actorAudienceMap := make(map[string]*url.URL) - actorAudience := a.GetActivityStreamsAudience() - if actorAudience == nil { - actorAudience = streams.NewActivityStreamsAudienceProperty() - a.SetActivityStreamsAudience(actorAudience) - } - for iter := actorAudience.Begin(); iter != actorAudience.End(); iter = iter.Next() { - id, err := ToId(iter) - if err != nil { - return err - } - actorAudienceMap[id.String()] = id - } - // Obtain the objects maps for each recipient type. - o := a.GetActivityStreamsObject() - objsTo := make([]map[string]*url.URL, o.Len()) - objsBto := make([]map[string]*url.URL, o.Len()) - objsCc := make([]map[string]*url.URL, o.Len()) - objsBcc := make([]map[string]*url.URL, o.Len()) - objsAudience := make([]map[string]*url.URL, o.Len()) - for i := 0; i < o.Len(); i++ { - iter := o.At(i) - // Phase 1: Acquire all existing recipients on the object. - // - // Object to - objsTo[i] = make(map[string]*url.URL) - var oTo vocab.ActivityStreamsToProperty - if tr, ok := iter.GetType().(toer); !ok { - return fmt.Errorf("the Create object at %d has no 'to' property", i) - } else { - oTo = tr.GetActivityStreamsTo() - if oTo == nil { - oTo = streams.NewActivityStreamsToProperty() - tr.SetActivityStreamsTo(oTo) - } - } - for iter := oTo.Begin(); iter != oTo.End(); iter = iter.Next() { - id, err := ToId(iter) - if err != nil { - return err - } - objsTo[i][id.String()] = id - } - // Object bto - objsBto[i] = make(map[string]*url.URL) - var oBto vocab.ActivityStreamsBtoProperty - if tr, ok := iter.GetType().(btoer); !ok { - return fmt.Errorf("the Create object at %d has no 'bto' property", i) - } else { - oBto = tr.GetActivityStreamsBto() - if oBto == nil { - oBto = streams.NewActivityStreamsBtoProperty() - tr.SetActivityStreamsBto(oBto) - } - } - for iter := oBto.Begin(); iter != oBto.End(); iter = iter.Next() { - id, err := ToId(iter) - if err != nil { - return err - } - objsBto[i][id.String()] = id - } - // Object cc - objsCc[i] = make(map[string]*url.URL) - var oCc vocab.ActivityStreamsCcProperty - if tr, ok := iter.GetType().(ccer); !ok { - return fmt.Errorf("the Create object at %d has no 'cc' property", i) - } else { - oCc = tr.GetActivityStreamsCc() - if oCc == nil { - oCc = streams.NewActivityStreamsCcProperty() - tr.SetActivityStreamsCc(oCc) - } - } - for iter := oCc.Begin(); iter != oCc.End(); iter = iter.Next() { - id, err := ToId(iter) - if err != nil { - return err - } - objsCc[i][id.String()] = id - } - // Object bcc - objsBcc[i] = make(map[string]*url.URL) - var oBcc vocab.ActivityStreamsBccProperty - if tr, ok := iter.GetType().(bccer); !ok { - return fmt.Errorf("the Create object at %d has no 'bcc' property", i) - } else { - oBcc = tr.GetActivityStreamsBcc() - if oBcc == nil { - oBcc = streams.NewActivityStreamsBccProperty() - tr.SetActivityStreamsBcc(oBcc) - } - } - for iter := oBcc.Begin(); iter != oBcc.End(); iter = iter.Next() { - id, err := ToId(iter) - if err != nil { - return err - } - objsBcc[i][id.String()] = id - } - // Object audience - objsAudience[i] = make(map[string]*url.URL) - var oAudience vocab.ActivityStreamsAudienceProperty - if tr, ok := iter.GetType().(audiencer); !ok { - return fmt.Errorf("the Create object at %d has no 'audience' property", i) - } else { - oAudience = tr.GetActivityStreamsAudience() - if oAudience == nil { - oAudience = streams.NewActivityStreamsAudienceProperty() - tr.SetActivityStreamsAudience(oAudience) - } - } - for iter := oAudience.Begin(); iter != oAudience.End(); iter = iter.Next() { - id, err := ToId(iter) - if err != nil { - return err - } - objsAudience[i][id.String()] = id - } - // Phase 2: Apply missing recipients to the object from the - // activity. - // - // Activity to -> Object to - for k, v := range actorToMap { - if _, ok := objsTo[i][k]; !ok { - oTo.AppendIRI(v) - } - } - // Activity bto -> Object bto - for k, v := range actorBtoMap { - if _, ok := objsBto[i][k]; !ok { - oBto.AppendIRI(v) - } - } - // Activity cc -> Object cc - for k, v := range actorCcMap { - if _, ok := objsCc[i][k]; !ok { - oCc.AppendIRI(v) - } - } - // Activity bcc -> Object bcc - for k, v := range actorBccMap { - if _, ok := objsBcc[i][k]; !ok { - oBcc.AppendIRI(v) - } - } - // Activity audience -> Object audience - for k, v := range actorAudienceMap { - if _, ok := objsAudience[i][k]; !ok { - oAudience.AppendIRI(v) - } - } - } - // Phase 3: Apply missing recipients to the activity from the objects. - // - // Object to -> Activity to - for i := 0; i < len(objsTo); i++ { - for k, v := range objsTo[i] { - if _, ok := actorToMap[k]; !ok { - actorTo.AppendIRI(v) - } - } - } - // Object bto -> Activity bto - for i := 0; i < len(objsBto); i++ { - for k, v := range objsBto[i] { - if _, ok := actorBtoMap[k]; !ok { - actorBto.AppendIRI(v) - } - } - } - // Object cc -> Activity cc - for i := 0; i < len(objsCc); i++ { - for k, v := range objsCc[i] { - if _, ok := actorCcMap[k]; !ok { - actorCc.AppendIRI(v) - } - } - } - // Object bcc -> Activity bcc - for i := 0; i < len(objsBcc); i++ { - for k, v := range objsBcc[i] { - if _, ok := actorBccMap[k]; !ok { - actorBcc.AppendIRI(v) - } - } - } - // Object audience -> Activity audience - for i := 0; i < len(objsAudience); i++ { - for k, v := range objsAudience[i] { - if _, ok := actorAudienceMap[k]; !ok { - actorAudience.AppendIRI(v) - } - } - } - return nil -} - -// toTombstone creates a Tombstone object for the given ActivityStreams value. -func toTombstone(obj vocab.Type, id *url.URL, now time.Time) vocab.ActivityStreamsTombstone { - tomb := streams.NewActivityStreamsTombstone() - // id property - idProp := streams.NewJSONLDIdProperty() - idProp.Set(id) - tomb.SetJSONLDId(idProp) - // formerType property - former := streams.NewActivityStreamsFormerTypeProperty() - tomb.SetActivityStreamsFormerType(former) - // Populate Former Type - former.AppendXMLSchemaString(obj.GetTypeName()) - // Copy over the published property if it existed - if pubber, ok := obj.(publisheder); ok { - if pub := pubber.GetActivityStreamsPublished(); pub != nil { - tomb.SetActivityStreamsPublished(pub) - } - } - // Copy over the updated property if it existed - if upder, ok := obj.(updateder); ok { - if upd := upder.GetActivityStreamsUpdated(); upd != nil { - tomb.SetActivityStreamsUpdated(upd) - } - } - // Set deleted time to now. - deleted := streams.NewActivityStreamsDeletedProperty() - deleted.Set(now) - tomb.SetActivityStreamsDeleted(deleted) - return tomb -} - -// mustHaveActivityActorsMatchObjectActors ensures that the actors on types in -// the 'object' property are all listed in the 'actor' property. -func mustHaveActivityActorsMatchObjectActors(c context.Context, - actors vocab.ActivityStreamsActorProperty, - op vocab.ActivityStreamsObjectProperty, - newTransport func(c context.Context, actorBoxIRI *url.URL, gofedAgent string) (t Transport, err error), - boxIRI *url.URL, -) error { - activityActorMap := make(map[string]bool, actors.Len()) - for iter := actors.Begin(); iter != actors.End(); iter = iter.Next() { - id, err := ToId(iter) - if err != nil { - return err - } - activityActorMap[id.String()] = true - } - for iter := op.Begin(); iter != op.End(); iter = iter.Next() { - iri, err := ToId(iter) - if err != nil { - return err - } - // Attempt to dereference the IRI, regardless whether it is a - // type or IRI - tport, err := newTransport(c, boxIRI, goFedUserAgent()) - if err != nil { - return err - } - resp, err := tport.Dereference(c, iri) - if err != nil { - return err - } - m, err := readActivityPubResp(resp) - if err != nil { - return err - } - t, err := streams.ToType(c, m) - if err != nil { - return err - } - ac, ok := t.(actorer) - if !ok { - return fmt.Errorf("cannot verify actors: object value has no 'actor' property") - } - objActors := ac.GetActivityStreamsActor() - for iter := objActors.Begin(); iter != objActors.End(); iter = iter.Next() { - id, err := ToId(iter) - if err != nil { - return err - } - if !activityActorMap[id.String()] { - return fmt.Errorf("activity does not have all actors from its object's actors") - } - } - } - return nil -} - -// add implements the logic of adding object ids to a target Collection or -// OrderedCollection. This logic is shared by both the C2S and S2S protocols. -func add(c context.Context, - op vocab.ActivityStreamsObjectProperty, - target vocab.ActivityStreamsTargetProperty, - db Database, -) error { - opIds := make([]*url.URL, 0, op.Len()) - for iter := op.Begin(); iter != op.End(); iter = iter.Next() { - id, err := ToId(iter) - if err != nil { - return err - } - opIds = append(opIds, id) - } - targetIds := make([]*url.URL, 0, op.Len()) - for iter := target.Begin(); iter != target.End(); iter = iter.Next() { - id, err := ToId(iter) - if err != nil { - return err - } - targetIds = append(targetIds, id) - } - // Create anonymous loop function to be able to properly scope the defer - // for the database lock at each iteration. - loopFn := func(t *url.URL) error { - unlock, err := db.Lock(c, t) - if err != nil { - return err - } - defer unlock() - if owns, err := db.Owns(c, t); err != nil { - return err - } else if !owns { - return nil - } - tp, err := db.Get(c, t) - if err != nil { - return err - } - if streams.IsOrExtendsActivityStreamsOrderedCollection(tp) { - oi, ok := tp.(orderedItemser) - if !ok { - return fmt.Errorf("type extending from OrderedCollection cannot convert to orderedItemser interface") - } - oiProp := oi.GetActivityStreamsOrderedItems() - if oiProp == nil { - oiProp = streams.NewActivityStreamsOrderedItemsProperty() - oi.SetActivityStreamsOrderedItems(oiProp) - } - for _, objId := range opIds { - oiProp.AppendIRI(objId) - } - } else if streams.IsOrExtendsActivityStreamsCollection(tp) { - i, ok := tp.(itemser) - if !ok { - return fmt.Errorf("type extending from Collection cannot convert to itemser interface") - } - iProp := i.GetActivityStreamsItems() - if iProp == nil { - iProp = streams.NewActivityStreamsItemsProperty() - i.SetActivityStreamsItems(iProp) - } - for _, objId := range opIds { - iProp.AppendIRI(objId) - } - } else { - return fmt.Errorf("target in Add is neither a Collection nor an OrderedCollection") - } - err = db.Update(c, tp) - if err != nil { - return err - } - return nil - } - for _, t := range targetIds { - if err := loopFn(t); err != nil { - return err - } - } - return nil -} - -// remove implements the logic of removing object ids to a target Collection or -// OrderedCollection. This logic is shared by both the C2S and S2S protocols. -func remove(c context.Context, - op vocab.ActivityStreamsObjectProperty, - target vocab.ActivityStreamsTargetProperty, - db Database, -) error { - opIds := make(map[string]bool, op.Len()) - for iter := op.Begin(); iter != op.End(); iter = iter.Next() { - id, err := ToId(iter) - if err != nil { - return err - } - opIds[id.String()] = true - } - targetIds := make([]*url.URL, 0, op.Len()) - for iter := target.Begin(); iter != target.End(); iter = iter.Next() { - id, err := ToId(iter) - if err != nil { - return err - } - targetIds = append(targetIds, id) - } - // Create anonymous loop function to be able to properly scope the defer - // for the database lock at each iteration. - loopFn := func(t *url.URL) error { - unlock, err := db.Lock(c, t) - if err != nil { - return err - } - defer unlock() - if owns, err := db.Owns(c, t); err != nil { - return err - } else if !owns { - return nil - } - tp, err := db.Get(c, t) - if err != nil { - return err - } - if streams.IsOrExtendsActivityStreamsOrderedCollection(tp) { - oi, ok := tp.(orderedItemser) - if !ok { - return fmt.Errorf("type extending from OrderedCollection cannot convert to orderedItemser interface") - } - oiProp := oi.GetActivityStreamsOrderedItems() - if oiProp != nil { - for i := 0; i < oiProp.Len(); /*Conditional*/ { - id, err := ToId(oiProp.At(i)) - if err != nil { - return err - } - if opIds[id.String()] { - oiProp.Remove(i) - } else { - i++ - } - } - } - } else if streams.IsOrExtendsActivityStreamsCollection(tp) { - i, ok := tp.(itemser) - if !ok { - return fmt.Errorf("type extending from Collection cannot convert to itemser interface") - } - iProp := i.GetActivityStreamsItems() - if iProp != nil { - for i := 0; i < iProp.Len(); /*Conditional*/ { - id, err := ToId(iProp.At(i)) - if err != nil { - return err - } - if opIds[id.String()] { - iProp.Remove(i) - } else { - i++ - } - } - } - } else { - return fmt.Errorf("target in Remove is neither a Collection nor an OrderedCollection") - } - err = db.Update(c, tp) - if err != nil { - return err - } - return nil - } - for _, t := range targetIds { - if err := loopFn(t); err != nil { - return err - } - } - return nil -} - -// clearSensitiveFields removes the 'bto' and 'bcc' entries on the given value -// and recursively on every 'object' property value. -func clearSensitiveFields(obj vocab.Type) { - if t, ok := obj.(btoer); ok { - t.SetActivityStreamsBto(nil) - } - if t, ok := obj.(bccer); ok { - t.SetActivityStreamsBcc(nil) - } - if t, ok := obj.(objecter); ok { - op := t.GetActivityStreamsObject() - if op != nil { - for iter := op.Begin(); iter != op.End(); iter = iter.Next() { - clearSensitiveFields(iter.GetType()) - } - } - } -} - -// requestId forms an ActivityPub id based on the HTTP request. Always assumes -// that the id is HTTPS. -func requestId(r *http.Request, scheme string) *url.URL { - id := r.URL - id.Host = r.Host - id.Scheme = scheme - return id -} diff --git a/vendor/codeberg.org/superseriousbusiness/activity/pub/version.go b/vendor/codeberg.org/superseriousbusiness/activity/pub/version.go deleted file mode 100644 index 23b958ce5..000000000 --- a/vendor/codeberg.org/superseriousbusiness/activity/pub/version.go +++ /dev/null @@ -1,15 +0,0 @@ -package pub - -import ( - "fmt" -) - -const ( - // Version string, used in the User-Agent - version = "v1.0.0" -) - -// goFedUserAgent returns the user agent string for the go-fed library. -func goFedUserAgent() string { - return fmt.Sprintf("(go-fed/activity %s)", version) -} |
