Begin porting C2S side effect behaviors.

このコミットが含まれているのは:
Cory Slep 2019-02-13 23:56:36 +01:00
コミット 0f7dce6839
5個のファイルの変更339行の追加96行の削除

ファイルの表示

@ -125,10 +125,8 @@ type FederatingWrappedCallbacks struct {
// db is the Database the FederatingWrappedCallbacks should use.
db Database
// inboxIRI is the inboxIRI that is handling this callback.
// TODO: Populate
inboxIRI *url.URL
// newTransport creates a new Transport.
// TODO: Populate
newTransport func(c context.Context, actorBoxIRI *url.URL, gofedAgent string) (t Transport, err error)
}
@ -820,7 +818,7 @@ func (w FederatingWrappedCallbacks) announce(c context.Context, a vocab.Activity
}
// Get 'shares' value, defaulting to a collection.
sharesT := shares.GetType()
if sharesT== nil {
if sharesT == nil {
col := streams.NewActivityStreamsCollection()
sharesT = col
shares.SetActivityStreamsCollection(col)

ファイルの表示

@ -2,6 +2,7 @@ package pub
import (
"github.com/go-fed/activity/streams/vocab"
"net/url"
)
// inReplyToer is an ActivityStreams type with a 'inReplyTo' property
@ -49,26 +50,31 @@ type publisheder interface {
// 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 a 'audience' property
type audiencer interface {
GetActivityStreamsAudience() vocab.ActivityStreamsAudienceProperty
SetActivityStreamsAudience(i vocab.ActivityStreamsAudienceProperty)
}
// inboxer is an ActivityStreams type with a 'inbox' property
@ -79,6 +85,7 @@ type inboxer interface {
// attributedToer is an ActivityStreams type with a 'attributedTo' property
type attributedToer interface {
GetActivityStreamsAttributedTo() vocab.ActivityStreamsAttributedToProperty
SetActivityStreamsAttributedTo(i vocab.ActivityStreamsAttributedToProperty)
}
// likeser is an ActivityStreams type with a 'likes' property
@ -98,3 +105,8 @@ 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)
}

ファイルの表示

@ -99,8 +99,8 @@ func (a *sideEffectActor) PostInbox(c context.Context, inboxIRI *url.URL, activi
wrapped, other := a.s2s.Callbacks(c)
// Populate side channels.
wrapped.db = a.db
wrapped.inboxIRI= inboxIRI
wrapped.newTransport= a.s2s.NewTransport
wrapped.inboxIRI = inboxIRI
wrapped.newTransport = a.s2s.NewTransport
if err = wrapped.disjoint(other); err != nil {
return err
}

ファイルの表示

@ -2,7 +2,10 @@ package pub
import (
"context"
"fmt"
"github.com/go-fed/activity/streams"
"github.com/go-fed/activity/streams/vocab"
"net/url"
)
// SocialWrappedCallbacks lists the callback functions that already have some
@ -15,8 +18,7 @@ type SocialWrappedCallbacks struct {
//
// The wrapping callback for the Social Protocol copies the actor(s) to
// the 'attributedTo' property, copying recipients between the Create
// activity and all objects, save the entry in the database, and adds it
// to the outbox.
// 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.
@ -58,9 +60,16 @@ type SocialWrappedCallbacks struct {
//
// TODO: Describe
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
// deliverable is a sidechannel out, indicating if the handled activity
// should be delivered to a peer.
deliverable bool
}
// disjoint ensures that the functions given do not share a type signature with
@ -104,8 +113,6 @@ func (w SocialWrappedCallbacks) callbacks() []interface{} {
w.update,
w.deleteFn,
w.follow,
w.accept,
w.reject,
w.add,
w.remove,
w.like,
@ -116,120 +123,85 @@ func (w SocialWrappedCallbacks) callbacks() []interface{} {
// create implements the social Create activity side effects.
func (w SocialWrappedCallbacks) create(c context.Context, a vocab.ActivityStreamsCreate) error {
*deliverable = true
if s.LenObject() == 0 {
return errObjectRequired
w.deliverable = true
op := a.GetActivityStreamsObject()
if op == nil || op.Len() == 0 {
return ErrObjectRequired
}
c = s.Raw()
// When a Create activity is posted, the actor of the activity
// SHOULD be copied onto the object's attributedTo field.
// Presumably only if it doesn't already exist, to prevent
// duplicate deliveries.
createActorIds := make(map[string]interface{})
for i := 0; i < c.ActorLen(); i++ {
if c.IsActorObject(i) {
obj := c.GetActorObject(i)
id := obj.GetId()
createActorIds[id.String()] = obj
} else if c.IsActorLink(i) {
l := c.GetActorLink(i)
href := l.GetHref()
createActorIds[href.String()] = l
} else if c.IsActorIRI(i) {
iri := c.GetActorIRI(i)
createActorIds[iri.String()] = iri
// Obtain all actor IRIs.
actors := a.GetActivityStreamsActor()
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
}
var obj []vocab.ObjectType
for i := 0; i < c.ObjectLen(); i++ {
if !c.IsObject(i) {
return fmt.Errorf("unsupported: Create Activity with 'object' that is only an IRI")
}
obj = append(obj, c.GetObject(i))
}
objectAttributedToIds := make([]map[string]interface{}, len(obj))
// Obtain each object's 'attributedTo' IRIs.
objectAttributedToIds := make([]map[string]*url.URL, op.Len())
for i := range objectAttributedToIds {
objectAttributedToIds[i] = make(map[string]interface{})
objectAttributedToIds[i] = make(map[string]*url.URL)
}
for k, o := range obj {
for i := 0; i < o.AttributedToLen(); i++ {
if o.IsAttributedToObject(i) {
at := o.GetAttributedToObject(i)
id := o.GetId()
objectAttributedToIds[k][id.String()] = at
} else if o.IsAttributedToLink(i) {
at := o.GetAttributedToLink(i)
href := at.GetHref()
objectAttributedToIds[k][href.String()] = at
} else if o.IsAttributedToIRI(i) {
iri := o.GetAttributedToIRI(i)
objectAttributedToIds[k][iri.String()] = iri
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 {
var iri *url.URL
if vObj, ok := v.(vocab.ObjectType); ok {
if !vObj.HasId() {
return fmt.Errorf("create actor object missing id")
}
iri = vObj.GetId()
} else if vLink, ok := v.(vocab.LinkType); ok {
if !vLink.HasHref() {
return fmt.Errorf("create actor link missing href")
}
iri = vLink.GetHref()
} else if vIRI, ok := v.(*url.URL); ok {
iri = vIRI
t := op.At(i).GetType()
attrToer, ok := t.(attributedToer)
if !ok {
continue
}
obj[i].AppendAttributedToIRI(iri)
attr := attrToer.GetActivityStreamsAttributedTo()
attr.AppendIRI(v)
}
}
}
// Put all missing object attributedTo IRIs onto the actor property.
for _, attributedToMap := range objectAttributedToIds {
for k, v := range attributedToMap {
if _, ok := createActorIds[k]; !ok {
var iri *url.URL
if vObj, ok := v.(vocab.ObjectType); ok {
if !vObj.HasId() {
return fmt.Errorf("attributedTo object missing id")
}
iri = vObj.GetId()
} else if vLink, ok := v.(vocab.LinkType); ok {
if !vLink.HasHref() {
return fmt.Errorf("attributedTo link missing href")
}
iri = vLink.GetHref()
} else if vIRI, ok := v.(*url.URL); ok {
iri = vIRI
}
c.AppendActorIRI(iri)
actors.AppendIRI(v)
}
}
}
// As such, a server SHOULD copy any recipients of the Create activity to its
// object upon initial distribution, and likewise with copying recipients from
// the object to the wrapping Create activity.
if err := f.sameRecipients(c); err != nil {
// 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 requires the client application to persist the 'object' that
// was created.
for _, o := range obj {
if err := f.App.Set(ctx, o); err != nil {
// Persist all objects we've created, which will include sensitive
// recipients such as 'bcc' and 'bto'.
for i := 0; i < op.Len(); i++ {
obj := op.At(i).GetType()
// TODO: Lock
if err := w.db.Create(c, obj); err != nil {
return err
}
}
// Persist the above changes in the outbox
var err error
*toAddToOutbox = make(map[string]interface{})
*toAddToOutbox, err = c.Serialize()
if err != nil {
return err
if w.Create != nil {
return w.Create(c, a)
}
return f.ClientCallbacker.Create(ctx, s)
return nil
}
// update implements the social Update activity side effects.

ファイルの表示

@ -539,3 +539,264 @@ func mustHaveActivityActorsMatchObjectActors(a vocab.ActivityType) error {
}
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]interface{})
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]interface{})
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]interface{})
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]interface{})
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]interface{})
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]interface{}, o.Len())
objsBto := make([]map[string]interface{}, o.Len())
objsCco := make([]map[string]interface{}, o.Len())
objsBcc := make([]map[string]interface{}, o.Len())
objsAudience := make([]map[string]interface{}, o.Len())
for i := 0; i < o.Len(); i++ {
// Phase 1: Acquire all existing recipients on the object.
//
// Object to
objsTo[i] = make(map[string]interface{})
var oTo vocab.ActivityStreamsToProperty
if tr, ok := o.At(i).(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]interface{})
var oBto vocab.ActivityStreamsBtoProperty
if tr, ok := o.At(i).(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]interface{})
var oCc vocab.ActivityStreamsCcProperty
if tr, ok := o.At(i).(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]interface{})
var oBcc vocab.ActivityStreamsBccProperty
if tr, ok := o.At(i).(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]interface{})
var oAudience vocab.ActivityStreamsAudienceProperty
if tr, ok := o.At(i).(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
}