Add tests for PostInbox Object/Target requirements

Also fixed my earlier stupidity at requiring a 'type' when I meant to
require a 'target'. Also added the missing checks to the federated
handlers.
このコミットが含まれているのは:
Cory Slep 2018-04-12 21:55:20 +02:00
コミット 7d049ea3d4
2個のファイルの変更205行の追加30行の削除

ファイルの表示

@ -15,8 +15,8 @@ import (
var (
// ErrObjectRequired means the activity needs its object property set.
ErrObjectRequired = errors.New("object property required")
// ErrTypeRequired means the activity needs its type property set.
ErrTypeRequired = errors.New("type property required")
// ErrTargetRequired means the activity needs its target property set.
ErrTargetRequired = errors.New("target property required")
)
// TODO: Helper http Handler for serving ActivityStream objects
@ -43,7 +43,7 @@ type Pubber interface {
// the request as an ActivityPub request. If it returns an error, it is up to
// the client to determine how to respond via HTTP.
//
// Note that the error could be ErrObjectRequired or ErrTypeRequired.
// Note that the error could be ErrObjectRequired or ErrTargetRequired.
PostOutbox(c context.Context, w http.ResponseWriter, r *http.Request) (bool, error)
GetOutbox(c context.Context, w http.ResponseWriter, r *http.Request) (bool, error)
}
@ -559,8 +559,8 @@ func (f *federator) handleClientAdd(c context.Context, deliverable *bool) func(s
*deliverable = true
if s.LenObject() == 0 {
return ErrObjectRequired
} else if s.LenType() == 0 {
return ErrTypeRequired
} else if s.LenTarget() == 0 {
return ErrTargetRequired
}
raw := s.Raw()
ids, err := getTargetIds(raw)
@ -623,8 +623,8 @@ func (f *federator) handleClientRemove(c context.Context, deliverable *bool) fun
*deliverable = true
if s.LenObject() == 0 {
return ErrObjectRequired
} else if s.LenType() == 0 {
return ErrTypeRequired
} else if s.LenTarget() == 0 {
return ErrTargetRequired
}
raw := s.Raw()
ids, err := getTargetIds(raw)
@ -750,6 +750,7 @@ func (f *federator) getPostInboxResolver(c context.Context) *streams.Resolver {
RemoveCallback: f.handleRemove(c),
LikeCallback: f.handleLike(c),
UndoCallback: f.handleUndo(c),
BlockCallback: f.handleBlock(c),
// TODO: Extended activity types, such as Announce, Arrive, etc.
}
}
@ -758,6 +759,9 @@ func (f *federator) handleCreate(c context.Context) func(s *streams.Create) erro
return func(s *streams.Create) error {
// Create requires the client application to persist the 'object' that
// was created.
if s.LenObject() == 0 {
return ErrObjectRequired
}
raw := s.Raw()
for i := 0; i < raw.ObjectLen(); i++ {
if !raw.IsObject(i) {
@ -777,6 +781,9 @@ func (f *federator) handleUpdate(c context.Context) func(s *streams.Update) erro
return func(s *streams.Update) error {
// TODO: The receiving server MUST take care to be sure that the Update
// is authorized to modify its object.
if s.LenObject() == 0 {
return ErrObjectRequired
}
raw := s.Raw()
for i := 0; i < raw.ObjectLen(); i++ {
if !raw.IsObject(i) {
@ -796,6 +803,9 @@ func (f *federator) handleDelete(c context.Context) func(s *streams.Delete) erro
return func(s *streams.Delete) error {
// TODO: Verify ownership. I think the spec unintentionally suggests to
// just assume it is owned, so we will actually verify.
if s.LenObject() == 0 {
return ErrObjectRequired
}
ids, err := getObjectIds(s.Raw())
if err != nil {
return err
@ -824,6 +834,9 @@ func (f *federator) handleFollow(c context.Context) func(s *streams.Follow) erro
return func(s *streams.Follow) error {
// Permit either human-triggered or automatically triggering
// 'Accept'/'Reject'.
if s.LenObject() == 0 {
return ErrObjectRequired
}
todo := f.FederateApp.OnFollow(c, s)
if todo != DoNothing {
var activity vocab.ActivityType
@ -940,8 +953,8 @@ func (f *federator) handleAdd(c context.Context) func(s *streams.Add) error {
// 'object' to a specific 'target' collection.
if s.LenObject() == 0 {
return ErrObjectRequired
} else if s.LenType() == 0 {
return ErrTypeRequired
} else if s.LenTarget() == 0 {
return ErrTargetRequired
}
raw := s.Raw()
ids, err := getTargetIds(raw)
@ -1005,8 +1018,8 @@ func (f *federator) handleRemove(c context.Context) func(s *streams.Remove) erro
// an 'object' from a specific 'target' collection.
if s.LenObject() == 0 {
return ErrObjectRequired
} else if s.LenType() == 0 {
return ErrTypeRequired
} else if s.LenTarget() == 0 {
return ErrTargetRequired
}
raw := s.Raw()
ids, err := getTargetIds(raw)
@ -1066,6 +1079,9 @@ func (f *federator) handleRemove(c context.Context) func(s *streams.Remove) erro
func (f *federator) handleLike(c context.Context) func(s *streams.Like) error {
return func(s *streams.Like) error {
if s.LenObject() == 0 {
return ErrObjectRequired
}
getter := func(object vocab.ObjectType, lc *vocab.CollectionType, loc *vocab.OrderedCollectionType) error {
if object.IsLikesAnyURI() {
pObj, err := f.App.Get(c, object.GetLikesAnyURI())
@ -1102,6 +1118,21 @@ func (f *federator) handleUndo(c context.Context) func(s *streams.Undo) error {
// application is responsible for enforcing this. Note that 'Undo'-ing
// is not a deletion of a previous Activity, but the addition of its
// opposite.
if s.LenObject() == 0 {
return ErrObjectRequired
}
return f.ServerCallbacker.Undo(c, s)
}
}
func (f *federator) handleBlock(c context.Context) func(s *streams.Block) error {
// Servers SHOULD NOT deliver Block Activities to their object. So in
// this case we will explicitly ignore it, but validate it as if we
// were to accept it.
return func(s *streams.Block) error {
if s.LenObject() == 0 {
return ErrObjectRequired
}
return nil
}
}

ファイルの表示

@ -1112,6 +1112,166 @@ func TestPostInbox_HandlesBlocked(t *testing.T) {
}
}
func TestPostInbox_RequiresObject(t *testing.T) {
tests := []struct {
name string
input func() vocab.Serializer
}{
{
name: "create",
input: func() vocab.Serializer {
v := &vocab.Create{}
v.SetId(*noteActivityIRI)
v.AddSummaryString("Sally created a note")
v.AddActorObject(sallyActor)
v.AddToObject(samActor)
return v
},
},
{
name: "update",
input: func() vocab.Serializer {
v := &vocab.Update{}
v.SetId(*noteActivityIRI)
v.AddSummaryString("Sally updated a note")
v.AddActorObject(sallyActor)
v.AddToObject(samActor)
return v
},
},
{
name: "delete",
input: func() vocab.Serializer {
v := &vocab.Delete{}
v.SetId(*noteActivityIRI)
v.AddActorObject(sallyActor)
v.AddToObject(samActor)
return v
},
},
{
name: "follow",
input: func() vocab.Serializer {
v := &vocab.Follow{}
v.SetId(*noteActivityIRI)
v.AddActorObject(sallyActor)
v.AddToObject(samActor)
return v
},
},
{
name: "add",
input: func() vocab.Serializer {
v := &vocab.Add{}
v.SetId(*noteActivityIRI)
v.AddActorObject(sallyActor)
v.AddToObject(samActor)
v.AddTargetObject(testNote)
return v
},
},
{
name: "remove",
input: func() vocab.Serializer {
v := &vocab.Remove{}
v.SetId(*noteActivityIRI)
v.AddActorObject(sallyActor)
v.AddToObject(samActor)
v.AddTargetObject(testNote)
return v
},
},
{
name: "like",
input: func() vocab.Serializer {
v := &vocab.Like{}
v.SetId(*noteActivityIRI)
v.AddActorObject(sallyActor)
v.AddToObject(samActor)
return v
},
},
{
name: "block",
input: func() vocab.Serializer {
v := &vocab.Block{}
v.SetId(*noteActivityIRI)
v.AddActorObject(sallyActor)
v.AddToObject(samActor)
return v
},
},
{
name: "undo",
input: func() vocab.Serializer {
v := &vocab.Undo{}
v.SetId(*noteActivityIRI)
v.AddActorObject(sallyActor)
v.AddToObject(samActor)
return v
},
},
}
_, _, fedApp, _, _, _, _, p := NewPubberTest(t)
fedApp.unblocked = func(c context.Context, actorIRIs []url.URL) error {
return nil
}
for _, test := range tests {
resp := httptest.NewRecorder()
req := ActivityPubRequest(httptest.NewRequest("POST", testInboxURI, bytes.NewBuffer(MustSerialize(test.input()))))
handled, err := p.PostInbox(context.Background(), resp, req)
if err != ErrObjectRequired {
t.Fatalf("(%s) expected %s, got %s", test.name, ErrObjectRequired, err)
} else if !handled {
t.Fatalf("(%s) expected handled, got !handled", test.name)
}
}
}
func TestPostInbox_RequiresTarget(t *testing.T) {
tests := []struct {
name string
input func() vocab.Serializer
}{
{
name: "add",
input: func() vocab.Serializer {
v := &vocab.Add{}
v.SetId(*noteActivityIRI)
v.AddActorObject(sallyActor)
v.AddToObject(samActor)
v.AddObject(testNote)
return v
},
},
{
name: "remove",
input: func() vocab.Serializer {
v := &vocab.Remove{}
v.SetId(*noteActivityIRI)
v.AddActorObject(sallyActor)
v.AddToObject(samActor)
v.AddObject(testNote)
return v
},
},
}
_, _, fedApp, _, _, _, _, p := NewPubberTest(t)
fedApp.unblocked = func(c context.Context, actorIRIs []url.URL) error {
return nil
}
for _, test := range tests {
resp := httptest.NewRecorder()
req := ActivityPubRequest(httptest.NewRequest("POST", testInboxURI, bytes.NewBuffer(MustSerialize(test.input()))))
handled, err := p.PostInbox(context.Background(), resp, req)
if err != ErrTargetRequired {
t.Fatalf("(%s) expected %s, got %s", test.name, ErrTargetRequired, err)
} else if !handled {
t.Fatalf("(%s) expected handled, got !handled", test.name)
}
}
}
func TestPostInbox_Create_SetsObject(t *testing.T) {
app, _, fedApp, _, fedCb, _, _, p := NewPubberTest(t)
resp := httptest.NewRecorder()
@ -1831,14 +1991,6 @@ func TestPostInbox_Reject_CallsCallback(t *testing.T) {
}
}
func TestPostInbox_Add_RequireObject(t *testing.T) {
// TODO: Implement
}
func TestPostInbox_Add_RequireType(t *testing.T) {
// TODO: Implement
}
func TestPostInbox_Add_DoesNotAddIfTargetNotOwned(t *testing.T) {
// TODO: Implement
}
@ -1863,14 +2015,6 @@ func TestPostInbox_Add_CallsCallback(t *testing.T) {
// TODO: Implement
}
func TestPostInbox_Remove_RequireObject(t *testing.T) {
// TODO: Implement
}
func TestPostInbox_Remove_RequireType(t *testing.T) {
// TODO: Implement
}
func TestPostInbox_Remove_DoesNotAddIfTargetNotOwned(t *testing.T) {
// TODO: Implement
}
@ -2023,7 +2167,7 @@ func TestPostOutbox_Add_RequireObject(t *testing.T) {
// TODO: Implement
}
func TestPostOutbox_Add_RequireType(t *testing.T) {
func TestPostOutbox_Add_RequireTarget(t *testing.T) {
// TODO: Implement
}
@ -2059,7 +2203,7 @@ func TestPostOutbox_Remove_RequireObject(t *testing.T) {
// TODO: Implement
}
func TestPostOutbox_Remove_RequireType(t *testing.T) {
func TestPostOutbox_Remove_RequireTarget(t *testing.T) {
// TODO: Implement
}
@ -2095,7 +2239,7 @@ func TestPostOutbox_Like_RequireObject(t *testing.T) {
// TODO: Implement
}
func TestPostOutbox_Like_RequireType(t *testing.T) {
func TestPostOutbox_Like_RequireTarget(t *testing.T) {
// TODO: Implement
}