Fixes for the first c2s implementation report.

Non Interface Behavior Changes:

- Robust header detection for ActivityPub requests
- No more panic because of a Bto access after a Bcc condition
- PostOutbox Undo activities now required to have matching actors

Interface Behavior Changes:

- PostOutbox activities themselves are now passed to App.Set
- Authentication/Authorization in a SocialAPIVerifier no longer
    shadowed and ignored.
- Add activities can now fetch remote objects (not permanent)

The behavior changes are justified as having been broken bugs that would
have not met a developer's expectations. So including them as part of
the next release maintains major version 0 behavior compatibility.
このコミットが含まれているのは:
Cory Slep 2018-06-06 00:49:02 +02:00
コミット 33c5320c8f
4個のファイルの変更220行の追加79行の削除

ファイルの表示

@ -266,8 +266,9 @@ func (f *federator) PostOutbox(c context.Context, w http.ResponseWriter, r *http
authenticated := false
authorized := true
if verifier := f.SocialAPI.GetSocialAPIVerifier(c); verifier != nil {
var err error
// Use custom Social API method to authenticate and authorize.
authenticated, authorized, err := verifier.VerifyForOutbox(r, r.URL)
authenticated, authorized, err = verifier.VerifyForOutbox(r, r.URL)
if err != nil {
return true, err
} else if authenticated && !authorized {
@ -318,20 +319,23 @@ func (f *federator) PostOutbox(c context.Context, w http.ResponseWriter, r *http
}
typer = f.wrapInCreate(obj, actorIri)
}
newId := f.App.NewId(c, typer)
typer.SetId(newId)
if m, err = typer.Serialize(); err != nil {
activity, ok := typer.(vocab.ActivityType)
if !ok {
return true, fmt.Errorf("assigning new ids: cannot convert to vocab.ActivityType: %T", typer)
}
f.addNewIds(c, activity)
if m, err = activity.Serialize(); err != nil {
return true, err
}
deliverable := false
if err = f.getPostOutboxResolver(c, m, &deliverable, &m).Deserialize(m); err != nil {
if err = f.getPostOutboxResolver(c, m, &deliverable, &m, r.URL).Deserialize(m); err != nil {
if err == errObjectRequired || err == errTargetRequired {
w.WriteHeader(http.StatusBadRequest)
return true, nil
}
return true, err
}
if err := f.addToOutbox(c, r, m); err != nil {
if err = f.addToOutbox(c, r, m); err != nil {
return true, err
}
if f.EnableServer && deliverable {
@ -343,7 +347,7 @@ func (f *federator) PostOutbox(c context.Context, w http.ResponseWriter, r *http
return true, err
}
}
w.Header().Set("Location", newId.String())
w.Header().Set("Location", activity.GetId().String())
w.WriteHeader(http.StatusCreated)
return true, nil
}
@ -376,7 +380,7 @@ func (f *federator) GetOutbox(c context.Context, w http.ResponseWriter, r *http.
return true, nil
}
func (f *federator) getPostOutboxResolver(c context.Context, rawJson map[string]interface{}, deliverable *bool, toAddToOutbox *map[string]interface{}) *streams.Resolver {
func (f *federator) getPostOutboxResolver(c context.Context, rawJson map[string]interface{}, deliverable *bool, toAddToOutbox *map[string]interface{}, outboxURL *url.URL) *streams.Resolver {
return &streams.Resolver{
CreateCallback: f.handleClientCreate(c, deliverable, toAddToOutbox),
UpdateCallback: f.handleClientUpdate(c, rawJson, deliverable),
@ -384,7 +388,7 @@ func (f *federator) getPostOutboxResolver(c context.Context, rawJson map[string]
FollowCallback: f.handleClientFollow(c, deliverable),
AcceptCallback: f.handleClientAccept(c, deliverable),
RejectCallback: f.handleClientReject(c, deliverable),
AddCallback: f.handleClientAdd(c, deliverable),
AddCallback: f.handleClientAdd(c, deliverable, outboxURL),
RemoveCallback: f.handleClientRemove(c, deliverable),
LikeCallback: f.handleClientLike(c, deliverable),
UndoCallback: f.handleClientUndo(c, deliverable),
@ -600,7 +604,7 @@ func (f *federator) handleClientReject(c context.Context, deliverable *bool) fun
}
}
func (f *federator) handleClientAdd(c context.Context, deliverable *bool) func(s *streams.Add) error {
func (f *federator) handleClientAdd(c context.Context, deliverable *bool, outboxURL *url.URL) func(s *streams.Add) error {
return func(s *streams.Add) error {
*deliverable = true
if s.LenObject() == 0 {
@ -641,11 +645,29 @@ func (f *federator) handleClientAdd(c context.Context, deliverable *bool) func(s
}
}
for i := 0; i < raw.ObjectLen(); i++ {
if !raw.IsObject(i) {
// TODO: Fetch IRIs as well
return fmt.Errorf("add object must be object type: %v", raw)
var obj vocab.ObjectType
if raw.IsObjectIRI(i) {
objId := raw.GetObjectIRI(i)
if f.App.Owns(c, objId) {
pObj, err := f.App.Get(c, objId, Read)
var ok bool
if obj, ok = pObj.(vocab.ObjectType); !ok {
return fmt.Errorf("add object must be an activitypub object: %v", raw)
}
if err != nil {
return err
}
} else {
obj, err = f.dereferenceAsUser(outboxURL, objId)
if err != nil {
return err
}
}
} else if raw.IsObject(i) {
obj = raw.GetObject(i)
} else {
return fmt.Errorf("add object must be of object or iri type: %v", raw)
}
obj := raw.GetObject(i)
for _, target := range targets {
if !f.App.CanAdd(c, obj, target) {
continue
@ -769,6 +791,10 @@ func (f *federator) handleClientUndo(c context.Context, deliverable *bool) func(
if s.LenObject() == 0 {
return errObjectRequired
}
raw := s.Raw()
if err := f.ensureActivityActorsMatchObjectActors(raw); err != nil {
return err
}
// TODO: Determine if we can support common forms of undo natively.
return f.ClientCallbacker.Undo(c, s)
}

ファイルの表示

@ -28,7 +28,8 @@ const (
testAgent = "test agent string"
testInboxURI = "https://example.com/sally/inbox"
testOutboxURI = "https://example.com/sally/outbox"
testNewIRIString = "https://example.com/test/new/iri"
testNewIRIString = "https://example.com/test/new/iri/1"
testNewIRIString2 = "https://example.com/test/new/iri/2"
sallyIRIString = "https://example.com/sally"
sallyFollowingIRIString = "https://example.com/sally/following"
samIRIString = "https://example.com/sam"
@ -48,6 +49,7 @@ var (
noteActivityIRI *url.URL
updateActivityIRI *url.URL
testNewIRI *url.URL
testNewIRI2 *url.URL
sallyIRI *url.URL
sallyIRIInbox *url.URL
sallyFollowingIRI *url.URL
@ -118,6 +120,10 @@ func init() {
if err != nil {
panic(err)
}
testNewIRI2, err = url.Parse(testNewIRIString2)
if err != nil {
panic(err)
}
sallyIRI, err = url.Parse(sallyIRIString)
if err != nil {
panic(err)
@ -258,7 +264,7 @@ func init() {
testBlock.AppendObject(samActor)
testClientExpectedNote = &vocab.Note{}
testClientExpectedNote.SetId(noteIRI)
testClientExpectedNote.SetId(testNewIRI2)
testClientExpectedNote.AppendNameString(noteName)
testClientExpectedNote.AppendContentString("This is a simple note")
testClientExpectedNote.AppendAttributedToObject(sallyActor)
@ -1018,8 +1024,14 @@ func PreparePostOutboxTest(t *testing.T, app *MockApplication, socialApp *MockSo
fedApp.privateKey = func(boxIRI *url.URL) (crypto.PrivateKey, string, error) {
return testPrivateKey, testPublicKeyId, nil
}
gotNewId := 0
app.newId = func(c context.Context, t Typer) *url.URL {
return testNewIRI
gotNewId++
if gotNewId == 1 {
return testNewIRI
} else {
return testNewIRI2
}
}
app.getOutbox = func(c context.Context, r *http.Request, rw RWType) (vocab.OrderedCollectionType, error) {
if rw != ReadWrite {
@ -1129,7 +1141,10 @@ func TestSocialPubber_PostOutbox(t *testing.T) {
gotNewId := 0
app.newId = func(c context.Context, t Typer) *url.URL {
gotNewId++
return testNewIRI
if gotNewId == 1 {
return testNewIRI
}
return testNewIRI2
}
gotOutbox := 0
app.getOutbox = func(c context.Context, r *http.Request, rw RWType) (vocab.OrderedCollectionType, error) {
@ -1143,12 +1158,15 @@ func TestSocialPubber_PostOutbox(t *testing.T) {
}
gotSet := 0
var gotSetOutbox PubObject
var gotSetActivity PubObject
var gotSetCreateObject PubObject
app.set = func(c context.Context, o PubObject) error {
gotSet++
if gotSet == 1 {
gotSetCreateObject = o
} else if gotSet == 2 {
gotSetActivity = o
} else if gotSet == 3 {
gotSetOutbox = o
}
return nil
@ -1175,16 +1193,18 @@ func TestSocialPubber_PostOutbox(t *testing.T) {
t.Fatalf("expected %s, got %s", testOutboxURI, s)
} else if gotSocialAPIVerifier != 1 {
t.Fatalf("expected %d, got %d", 1, gotSocialAPIVerifier)
} else if gotNewId != 1 {
t.Fatalf("expected %d, got %d", 1, gotNewId)
} else if gotNewId != 2 {
t.Fatalf("expected %d, got %d", 2, gotNewId)
} else if gotOutbox != 1 {
t.Fatalf("expected %d, got %d", 1, gotOutbox)
} else if gotSet != 2 {
t.Fatalf("expected %d, got %d", 2, gotSet)
} else if gotSet != 3 {
t.Fatalf("expected %d, got %d", 3, gotSet)
} else if l := gotSetOutbox.GetType(0).(string); l != "OrderedCollection" {
t.Fatalf("expected %s, got %s", "OrderedCollection", l)
} else if l := gotSetCreateObject.GetType(0).(string); l != "Note" {
t.Fatalf("expected %s, got %s", "Note", l)
} else if l := gotSetActivity.GetType(0).(string); l != "Create" {
t.Fatalf("expected %s, got %s", "Create", l)
} else if gotCreate != 1 {
t.Fatalf("expected %d, got %d", 1, gotCreate)
} else if iri := gotCreateCallback.Raw().GetActorObject(0).GetId(); *iri != *sallyIRI {
@ -1202,15 +1222,6 @@ func TestSocialPubber_PostOutbox_SocialAPIVerified(t *testing.T) {
app, socialApp, cb, p := NewSocialPubberTest(t)
resp := httptest.NewRecorder()
req := Sign(ActivityPubRequest(httptest.NewRequest("POST", testOutboxURI, bytes.NewBuffer(MustSerialize(testCreateNote)))))
gotPublicKeyForOutbox := 0
var gotPublicKeyId string
var gotBoxIRI *url.URL
socialApp.getPublicKeyForOutbox = func(c context.Context, publicKeyId string, boxIRI *url.URL) (crypto.PublicKey, httpsig.Algorithm, error) {
gotPublicKeyForOutbox++
gotPublicKeyId = publicKeyId
gotBoxIRI = boxIRI
return testPrivateKey.Public(), httpsig.RSA_SHA256, nil
}
gotVerifyForOutbox := 0
var gotVerifiedOutbox *url.URL
socialApp.getSocialAPIVerifier = func(c context.Context) SocialAPIVerifier {
@ -1226,7 +1237,11 @@ func TestSocialPubber_PostOutbox_SocialAPIVerified(t *testing.T) {
gotNewId := 0
app.newId = func(c context.Context, t Typer) *url.URL {
gotNewId++
return testNewIRI
if gotNewId == 1 {
return testNewIRI
} else {
return testNewIRI2
}
}
gotOutbox := 0
app.getOutbox = func(c context.Context, r *http.Request, rw RWType) (vocab.OrderedCollectionType, error) {
@ -1240,12 +1255,15 @@ func TestSocialPubber_PostOutbox_SocialAPIVerified(t *testing.T) {
}
gotSet := 0
var gotSetOutbox PubObject
var gotSetActivity PubObject
var gotSetCreateObject PubObject
app.set = func(c context.Context, o PubObject) error {
gotSet++
if gotSet == 1 {
gotSetCreateObject = o
} else if gotSet == 2 {
gotSetActivity = o
} else if gotSet == 3 {
gotSetOutbox = o
}
return nil
@ -1264,26 +1282,22 @@ func TestSocialPubber_PostOutbox_SocialAPIVerified(t *testing.T) {
t.Fatalf("expected handled, got !handled")
} else if resp.Code != http.StatusCreated {
t.Fatalf("expected %d, got %d", http.StatusCreated, resp.Code)
} else if gotPublicKeyForOutbox != 1 {
t.Fatalf("expected %d, got %d", 1, gotPublicKeyForOutbox)
} else if gotPublicKeyId != testPublicKeyId {
t.Fatalf("expected %s, got %s", testPublicKeyId, gotPublicKeyId)
} else if s := gotBoxIRI.String(); s != testOutboxURI {
t.Fatalf("expected %s, got %s", testOutboxURI, s)
} else if gotVerifyForOutbox != 1 {
t.Fatalf("expected %d, got %d", 1, gotVerifyForOutbox)
} else if o := gotVerifiedOutbox.String(); o != testOutboxURI {
t.Fatalf("expected %s, got %s", testOutboxURI, o)
} else if gotNewId != 1 {
t.Fatalf("expected %d, got %d", 1, gotNewId)
} else if gotNewId != 2 {
t.Fatalf("expected %d, got %d", 2, gotNewId)
} else if gotOutbox != 1 {
t.Fatalf("expected %d, got %d", 1, gotOutbox)
} else if gotSet != 2 {
t.Fatalf("expected %d, got %d", 2, gotSet)
} else if gotSet != 3 {
t.Fatalf("expected %d, got %d", 3, gotSet)
} else if l := gotSetOutbox.GetType(0).(string); l != "OrderedCollection" {
t.Fatalf("expected %s, got %s", "OrderedCollection", l)
} else if l := gotSetCreateObject.GetType(0).(string); l != "Note" {
t.Fatalf("expected %s, got %s", "Note", l)
} else if l := gotSetActivity.GetType(0).(string); l != "Create" {
t.Fatalf("expected %s, got %s", "Create", l)
} else if gotCreate != 1 {
t.Fatalf("expected %d, got %d", 1, gotCreate)
} else if iri := gotCreateCallback.Raw().GetActorObject(0).GetId(); *iri != *sallyIRI {
@ -1666,7 +1680,11 @@ func TestPubber_PostOutbox(t *testing.T) {
gotNewId := 0
app.MockFederateApp.newId = func(c context.Context, t Typer) *url.URL {
gotNewId++
return testNewIRI
if gotNewId == 1 {
return testNewIRI
} else {
return testNewIRI2
}
}
gotOutbox := 0
app.MockFederateApp.getOutbox = func(c context.Context, r *http.Request, rw RWType) (vocab.OrderedCollectionType, error) {
@ -1680,12 +1698,15 @@ func TestPubber_PostOutbox(t *testing.T) {
}
gotSet := 0
var gotSetOutbox PubObject
var gotSetActivity PubObject
var gotSetCreateObject PubObject
app.MockFederateApp.set = func(c context.Context, o PubObject) error {
gotSet++
if gotSet == 1 {
gotSetCreateObject = o
} else if gotSet == 2 {
gotSetActivity = o
} else if gotSet == 3 {
gotSetOutbox = o
}
return nil
@ -1749,8 +1770,8 @@ func TestPubber_PostOutbox(t *testing.T) {
t.Fatalf("expected %s, got %s", testOutboxURI, s)
} else if gotSocialAPIVerifier != 1 {
t.Fatalf("expected %d, got %d", 1, gotSocialAPIVerifier)
} else if gotNewId != 1 {
t.Fatalf("expected %d, got %d", 1, gotNewId)
} else if gotNewId != 2 {
t.Fatalf("expected %d, got %d", 2, gotNewId)
} else if gotNewSigner != 1 {
t.Fatalf("expected %d, got %d", 1, gotNewSigner)
} else if gotPrivateKey != 1 {
@ -1759,12 +1780,14 @@ func TestPubber_PostOutbox(t *testing.T) {
t.Fatalf("expected %s, got %s", testOutboxURI, s)
} else if gotOutbox != 1 {
t.Fatalf("expected %d, got %d", 1, gotOutbox)
} else if gotSet != 2 {
t.Fatalf("expected %d, got %d", 2, gotSet)
} else if gotSet != 3 {
t.Fatalf("expected %d, got %d", 3, gotSet)
} else if l := gotSetOutbox.GetType(0).(string); l != "OrderedCollection" {
t.Fatalf("expected %s, got %s", "OrderedCollection", l)
} else if l := gotSetCreateObject.GetType(0).(string); l != "Note" {
t.Fatalf("expected %s, got %s", "Note", l)
} else if l := gotSetActivity.GetType(0).(string); l != "Create" {
t.Fatalf("expected %s, got %s", "Create", l)
} else if gotCreate != 1 {
t.Fatalf("expected %d, got %d", 1, gotCreate)
} else if iri := gotCreateCallback.Raw().GetActorObject(0).GetId(); iri.String() != sallyIRIString {
@ -4224,7 +4247,7 @@ func TestPostOutbox_WrapInCreateActivity(t *testing.T) {
rawNote.AppendToObject(samActor)
// Expected result
expectedNote := &vocab.Note{}
expectedNote.SetId(noteIRI)
expectedNote.SetId(testNewIRI2)
expectedNote.AppendNameString(noteName)
expectedNote.AppendContentString("This is a simple note")
expectedNote.AppendToObject(samActor)
@ -4458,12 +4481,15 @@ func TestPostOutbox_Create_SetCreatedObject(t *testing.T) {
}
gotSet := 0
var gotSetOutbox PubObject
var gotSetActivity PubObject
var gotSetCreate PubObject
app.MockFederateApp.set = func(c context.Context, o PubObject) error {
gotSet++
if gotSet == 1 {
gotSetCreate = o
} else {
} else if gotSet == 2 {
gotSetActivity = o
} else if gotSet == 3 {
gotSetOutbox = o
}
return nil
@ -4476,10 +4502,12 @@ func TestPostOutbox_Create_SetCreatedObject(t *testing.T) {
t.Fatal(err)
} else if !handled {
t.Fatalf("expected handled, got !handled")
} else if gotSet != 2 {
t.Fatalf("expected %d, got %d", 2, gotSet)
} else if gotSet != 3 {
t.Fatalf("expected %d, got %d", 3, gotSet)
} else if err := PubObjectEquals(gotSetCreate, testClientExpectedNote); err != nil {
t.Fatalf("unexpected callback object: %s", err)
} else if err := PubObjectEquals(gotSetActivity, testClientExpectedCreateNote); err != nil {
t.Fatalf("unexpected callback object: %s", err)
} else if err := PubObjectEquals(gotSetOutbox, expectedOutbox); err != nil {
t.Fatalf("unexpected callback object: %s", err)
}
@ -4606,8 +4634,8 @@ func TestPostOutbox_Update_DeleteSubFields(t *testing.T) {
t.Fatalf("expected handled, got !handled")
} else if gotGet != 1 {
t.Fatalf("expected %d, got %d", 1, gotGet)
} else if gotSet != 2 {
t.Fatalf("expected %d, got %d", 2, gotSet)
} else if gotSet != 3 {
t.Fatalf("expected %d, got %d", 3, gotSet)
} else if err := PubObjectEquals(gotSetObject, expectedUpdatedNote); err != nil {
t.Fatalf("unexpected set object: %s", err)
}
@ -4661,8 +4689,8 @@ func TestPostOutbox_Update_DeleteFields(t *testing.T) {
t.Fatalf("expected handled, got !handled")
} else if gotGet != 1 {
t.Fatalf("expected %d, got %d", 1, gotGet)
} else if gotSet != 2 {
t.Fatalf("expected %d, got %d", 2, gotSet)
} else if gotSet != 3 {
t.Fatalf("expected %d, got %d", 3, gotSet)
} else if err := PubObjectEquals(gotSetObject, expectedUpdatedNote); err != nil {
t.Fatalf("unexpected set object: %s", err)
}
@ -4737,8 +4765,8 @@ func TestPostOutbox_Update_DeleteSubFieldsMultipleObjects(t *testing.T) {
t.Fatalf("expected handled, got !handled")
} else if gotGet != 2 {
t.Fatalf("expected %d, got %d", 2, gotGet)
} else if gotSet != 3 {
t.Fatalf("expected %d, got %d", 3, gotSet)
} else if gotSet != 4 {
t.Fatalf("expected %d, got %d", 4, gotSet)
} else if err := PubObjectEquals(gotSetObject, expectedUpdatedNote); err != nil {
t.Fatalf("unexpected set object: %s", err)
} else if err := PubObjectEquals(gotSetObject2, expectedUpdatedNote2); err != nil {
@ -4796,8 +4824,8 @@ func TestPostOutbox_Update_OverwriteUpdatedFields(t *testing.T) {
t.Fatalf("expected handled, got !handled")
} else if gotGet != 1 {
t.Fatalf("expected %d, got %d", 1, gotGet)
} else if gotSet != 2 {
t.Fatalf("expected %d, got %d", 2, gotSet)
} else if gotSet != 3 {
t.Fatalf("expected %d, got %d", 3, gotSet)
} else if err := PubObjectEquals(gotSetObject, expectedUpdatedNote); err != nil {
t.Fatalf("unexpected set object: %s", err)
}
@ -4951,8 +4979,8 @@ func TestPostOutbox_Delete_SetsTombstone(t *testing.T) {
t.Fatalf("expected handled, got !handled")
} else if gotGet != 1 {
t.Fatalf("expected %d, got %d", 1, gotGet)
} else if gotSet != 2 {
t.Fatalf("expected %d, got %d", 2, gotSet)
} else if gotSet != 3 {
t.Fatalf("expected %d, got %d", 3, gotSet)
} else if err := PubObjectEquals(gotSetObject, expectedTombstone); err != nil {
t.Fatalf("unexpected set object: %s", err)
}
@ -5344,8 +5372,8 @@ func TestPostOutbox_Add_AddsIfTargetOwnedAndAppCanAdd(t *testing.T) {
t.Fatal(err)
} else if err := VocabSerializerEquals(canAddObj, testNote); err != nil {
t.Fatal(err)
} else if gotSet != 2 {
t.Fatalf("expected %d, got %d", 2, gotSet)
} else if gotSet != 3 {
t.Fatalf("expected %d, got %d", 3, gotSet)
} else if err := PubObjectEquals(gotSetObj, expectedTarget); err != nil {
t.Fatalf("unexpected set object: %s", err)
}
@ -5561,8 +5589,8 @@ func TestPostOutbox_Remove_RemoveIfTargetOwnedAndCanRemove(t *testing.T) {
t.Fatal(err)
} else if err := VocabSerializerEquals(canRemoveObj, testNote); err != nil {
t.Fatal(err)
} else if gotSet != 2 {
t.Fatalf("expected %d, got %d", 2, gotSet)
} else if gotSet != 3 {
t.Fatalf("expected %d, got %d", 3, gotSet)
} else if err := PubObjectEquals(gotSetObj, expectedTarget); err != nil {
t.Fatalf("unexpected set object: %s", err)
}
@ -5754,8 +5782,8 @@ func TestPostOutbox_Like_AddsToLikedCollection(t *testing.T) {
t.Fatalf("expected %d, got %d", 1, gotGet)
} else if gotGetString := gotGetIri.String(); gotGetString != sallyIRIString {
t.Fatalf("expected %s, got %s", noteURIString, sallyIRIString)
} else if gotSet != 2 {
t.Fatalf("expected %d, got %d", 2, gotSet)
} else if gotSet != 3 {
t.Fatalf("expected %d, got %d", 3, gotSet)
} else if err := PubObjectEquals(gotSetObj, expectedActor); err != nil {
t.Fatalf("set obj: %s", err)
}
@ -5801,8 +5829,8 @@ func TestPostOutbox_Like_DoesNotAddIfAlreadyLiked(t *testing.T) {
t.Fatal(err)
} else if !handled {
t.Fatalf("expected handled, got !handled")
} else if gotSet != 2 {
t.Fatalf("expected %d, got %d", 2, gotSet)
} else if gotSet != 3 {
t.Fatalf("expected %d, got %d", 3, gotSet)
} else if err := PubObjectEquals(gotSetObj, expectedActor); err != nil {
t.Fatalf("set obj: %s", err)
}

ファイルの表示

@ -35,7 +35,10 @@ const (
digestDelimiter = "="
)
var alternatives = []string{"application/activity+json"}
var alternatives = []string{
"application/activity+json",
"application/ld+json; profile=https://www.w3.org/ns/activitystreams",
}
func trimAll(s []string) []string {
var r []string
@ -45,24 +48,36 @@ func trimAll(s []string) []string {
return r
}
func headerEqualsOneOf(header string, acceptable []string) bool {
sanitizedHeader := strings.Join(trimAll(strings.Split(header, ";")), ";")
func headerContainsOneOf(header string, acceptable []string) bool {
sanitizedHeaderValues := trimAll(strings.Split(header, ";"))
sanitizedHeaderMap := make(map[string]bool, len(sanitizedHeaderValues))
for _, s := range sanitizedHeaderValues {
sanitizedHeaderMap[s] = true
}
found := false
for _, v := range acceptable {
if found {
break
}
// Remove any number of whitespace after ;'s
sanitizedV := strings.Join(trimAll(strings.Split(v, ";")), ";")
if sanitizedHeader == sanitizedV {
return true
sanitizedAcceptableValues := trimAll(strings.Split(v, ";"))
found = true
for _, v := range sanitizedAcceptableValues {
if has, ok := sanitizedHeaderMap[v]; !has || !ok {
found = false
break
}
}
}
return false
return found
}
func isActivityPubPost(r *http.Request) bool {
return r.Method == "POST" && headerEqualsOneOf(r.Header.Get(contentTypeHeader), append([]string{postContentTypeHeader}, alternatives...))
return r.Method == "POST" && headerContainsOneOf(r.Header.Get(contentTypeHeader), append([]string{postContentTypeHeader}, alternatives...))
}
func isActivityPubGet(r *http.Request) bool {
return r.Method == "GET" && headerEqualsOneOf(r.Header.Get(acceptHeader), append([]string{getAcceptHeader}, alternatives...))
return r.Method == "GET" && headerContainsOneOf(r.Header.Get(acceptHeader), append([]string{getAcceptHeader}, alternatives...))
}
// isPublic determines if a target is the Public collection as defined in the
@ -126,6 +141,34 @@ type creds struct {
pubKeyId string
}
// dereferenceAsUser is meant to be used by the activity handlers that need to
// handle IRI use cases where objects are expected. Returns an error if not
// federating.
func (f *federator) dereferenceAsUser(boxIRI, fetchIRI *url.URL) (obj vocab.ObjectType, err error) {
if !f.EnableServer {
err = fmt.Errorf("cannot dereference iri as user if not federating: %q", fetchIRI)
return
}
creds := &creds{}
creds.signer, err = f.FederateAPI.NewSigner()
if err != nil {
return
}
creds.privKey, creds.pubKeyId, err = f.FederateAPI.PrivateKey(boxIRI)
if err != nil {
return
}
resp, err := dereference(f.Client, fetchIRI, f.Agent, creds, f.Clock)
if err != nil {
return
}
var m map[string]interface{}
if err = json.Unmarshal(resp, &m); err != nil {
return
}
return toAnyObject(m)
}
// postToOutbox will attempt to send a POST request to the given URL with the
// body set to the provided bytes.
//
@ -160,11 +203,27 @@ func postToOutbox(c HttpClient, b []byte, to *url.URL, agent string, creds *cred
return nil
}
// addNewIds will add new IDs not just for an activity, but all objects
// contained within the activity if it is a Create activity.
func (f *federator) addNewIds(c context.Context, a vocab.ActivityType) {
newId := f.App.NewId(c, a)
a.SetId(newId)
if hasType(a, "Create") {
for i := 0; i < a.ObjectLen(); i++ {
if a.IsObject(i) {
obj := a.GetObject(i)
obj.SetId(f.App.NewId(c, obj))
}
}
}
}
// 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 (f *federator) wrapInCreate(o vocab.ObjectType, actor *url.URL) *vocab.Create {
c := &vocab.Create{}
c.AppendType("Create")
c.AppendObject(o)
c.AppendActorIRI(actor)
if o.IsPublished() {
@ -782,7 +841,7 @@ func stripHiddenRecipients(o deliverableObject) {
o.RemoveBccObject(0)
} else if o.IsBccLink(0) {
o.RemoveBccLink(0)
} else if o.IsBtoIRI(0) {
} else if o.IsBccIRI(0) {
o.RemoveBccIRI(0)
}
}
@ -1290,6 +1349,7 @@ func (f *federator) addAllObjectsToActorCollection(ctx context.Context, getter g
iri = c.GetActorIRI(i)
}
if !f.App.Owns(ctx, iri) {
// TODO: Fetch or just store
continue
}
var actor vocab.ObjectType
@ -1395,6 +1455,7 @@ func (f *federator) addAllActorsToObjectCollection(ctx context.Context, getter g
iri = c.GetObjectIRI(i)
}
if !f.App.Owns(ctx, iri) {
// TODO: Fetch or just store
continue
}
ownsAny = true
@ -1532,6 +1593,9 @@ func (f *federator) addToOutbox(c context.Context, r *http.Request, m map[string
if err != nil {
return err
}
if err := f.App.Set(c, activity); err != nil {
return err
}
outbox.PrependOrderedItemsObject(activity)
return f.App.Set(c, outbox)
}
@ -1957,3 +2021,15 @@ func isActivityType(t Typer) bool {
}
return false
}
func hasType(t Typer, kind string) bool {
for i := 0; i < t.TypeLen(); i++ {
v := t.GetType(i)
if s, ok := v.(string); ok {
if s == kind {
return true
}
}
}
return false
}

ファイルの表示

@ -154,3 +154,14 @@ func toAnyActivity(m map[string]interface{}) (o vocab.ActivityType, err error) {
err = r.Deserialize(m)
return
}
func toAnyObject(m map[string]interface{}) (o vocab.ObjectType, err error) {
r := &streams.Resolver{
AnyObjectCallback: func(i vocab.ObjectType) error {
o = i
return nil
},
}
err = r.Deserialize(m)
return
}