util.go (28634B)
1 package pub 2 3 import ( 4 "bytes" 5 "context" 6 "crypto/sha256" 7 "encoding/base64" 8 "encoding/json" 9 "errors" 10 "fmt" 11 "net/http" 12 "net/url" 13 "strings" 14 "time" 15 16 "github.com/superseriousbusiness/activity/streams" 17 "github.com/superseriousbusiness/activity/streams/vocab" 18 ) 19 20 var ( 21 // ErrObjectRequired indicates the activity needs its object property 22 // set. Can be returned by DelegateActor's PostInbox or PostOutbox so a 23 // Bad Request response is set. 24 ErrObjectRequired = errors.New("object property required on the provided activity") 25 // ErrTargetRequired indicates the activity needs its target property 26 // set. Can be returned by DelegateActor's PostInbox or PostOutbox so a 27 // Bad Request response is set. 28 ErrTargetRequired = errors.New("target property required on the provided activity") 29 ) 30 31 // activityStreamsMediaTypes contains all of the accepted ActivityStreams media 32 // types. Generated at init time. 33 var activityStreamsMediaTypes []string 34 35 func init() { 36 activityStreamsMediaTypes = []string{ 37 "application/activity+json", 38 } 39 jsonLdType := "application/ld+json" 40 for _, semi := range []string{";", " ;", " ; ", "; "} { 41 for _, profile := range []string{ 42 "profile=https://www.w3.org/ns/activitystreams", 43 "profile=\"https://www.w3.org/ns/activitystreams\"", 44 } { 45 activityStreamsMediaTypes = append( 46 activityStreamsMediaTypes, 47 fmt.Sprintf("%s%s%s", jsonLdType, semi, profile)) 48 } 49 } 50 } 51 52 // headerIsActivityPubMediaType returns true if the header string contains one 53 // of the accepted ActivityStreams media types. 54 // 55 // Note we don't try to build a comprehensive parser and instead accept a 56 // tolerable amount of whitespace since the HTTP specification is ambiguous 57 // about the format and significance of whitespace. 58 func headerIsActivityPubMediaType(header string) bool { 59 for _, mediaType := range activityStreamsMediaTypes { 60 if strings.Contains(header, mediaType) { 61 return true 62 } 63 } 64 return false 65 } 66 67 const ( 68 // The Content-Type header. 69 contentTypeHeader = "Content-Type" 70 // The Accept header. 71 acceptHeader = "Accept" 72 ) 73 74 // isActivityPubPost returns true if the request is a POST request that has the 75 // ActivityStreams content type header 76 func isActivityPubPost(r *http.Request) bool { 77 return r.Method == "POST" && headerIsActivityPubMediaType(r.Header.Get(contentTypeHeader)) 78 } 79 80 // isActivityPubGet returns true if the request is a GET request that has the 81 // ActivityStreams content type header 82 func isActivityPubGet(r *http.Request) bool { 83 return r.Method == "GET" && headerIsActivityPubMediaType(r.Header.Get(acceptHeader)) 84 } 85 86 // dedupeOrderedItems deduplicates the 'orderedItems' within an ordered 87 // collection type. Deduplication happens by the 'id' property. 88 func dedupeOrderedItems(oc orderedItemser) error { 89 oi := oc.GetActivityStreamsOrderedItems() 90 if oi == nil { 91 return nil 92 } 93 seen := make(map[string]bool, oi.Len()) 94 for i := 0; i < oi.Len(); { 95 var id *url.URL 96 97 iter := oi.At(i) 98 asType := iter.GetType() 99 if asType != nil { 100 var err error 101 id, err = GetId(asType) 102 if err != nil { 103 return err 104 } 105 } else if iter.IsIRI() { 106 id = iter.GetIRI() 107 } else { 108 return fmt.Errorf("element %d in OrderedCollection does not have an ID nor is an IRI", i) 109 } 110 if seen[id.String()] { 111 oi.Remove(i) 112 } else { 113 seen[id.String()] = true 114 i++ 115 } 116 } 117 return nil 118 } 119 120 const ( 121 // The Location header 122 locationHeader = "Location" 123 // Contains the ActivityStreams Content-Type value. 124 contentTypeHeaderValue = "application/ld+json; profile=\"https://www.w3.org/ns/activitystreams\"" 125 // The Date header. 126 dateHeader = "Date" 127 // The Digest header. 128 digestHeader = "Digest" 129 // The delimiter used in the Digest header. 130 digestDelimiter = "=" 131 // SHA-256 string for the Digest header. 132 sha256Digest = "SHA-256" 133 ) 134 135 // addResponseHeaders sets headers needed in the HTTP response, such but not 136 // limited to the Content-Type, Date, and Digest headers. 137 func addResponseHeaders(h http.Header, c Clock, responseContent []byte) { 138 h.Set(contentTypeHeader, contentTypeHeaderValue) 139 // RFC 7231 ยง7.1.1.2 140 h.Set(dateHeader, c.Now().UTC().Format("Mon, 02 Jan 2006 15:04:05")+" GMT") 141 // RFC 3230 and RFC 5843 142 var b bytes.Buffer 143 b.WriteString(sha256Digest) 144 b.WriteString(digestDelimiter) 145 hashed := sha256.Sum256(responseContent) 146 b.WriteString(base64.StdEncoding.EncodeToString(hashed[:])) 147 h.Set(digestHeader, b.String()) 148 } 149 150 // IdProperty is a property that can readily have its id obtained 151 type IdProperty interface { 152 // GetIRI returns the IRI of this property. When IsIRI returns false, 153 // GetIRI will return an arbitrary value. 154 GetIRI() *url.URL 155 // GetType returns the value in this property as a Type. Returns nil if 156 // the value is not an ActivityStreams type, such as an IRI or another 157 // value. 158 GetType() vocab.Type 159 // IsIRI returns true if this property is an IRI. 160 IsIRI() bool 161 } 162 163 // ToId returns an IdProperty's id. 164 func ToId(i IdProperty) (*url.URL, error) { 165 if i.GetType() != nil { 166 return GetId(i.GetType()) 167 } else if i.IsIRI() { 168 return i.GetIRI(), nil 169 } 170 return nil, fmt.Errorf("cannot determine id of activitystreams property") 171 } 172 173 // GetId will attempt to find the 'id' property or, if it happens to be a 174 // Link or derived from Link type, the 'href' property instead. 175 // 176 // Returns an error if the id is not set and either the 'href' property is not 177 // valid on this type, or it is also not set. 178 func GetId(t vocab.Type) (*url.URL, error) { 179 if id := t.GetJSONLDId(); id != nil { 180 return id.Get(), nil 181 } else if h, ok := t.(hrefer); ok { 182 if href := h.GetActivityStreamsHref(); href != nil { 183 return href.Get(), nil 184 } 185 } 186 return nil, fmt.Errorf("cannot determine id of activitystreams value") 187 } 188 189 // getInboxForwardingValues obtains the 'inReplyTo', 'object', 'target', and 190 // 'tag' values on an ActivityStreams value. 191 func getInboxForwardingValues(o vocab.Type) (t []vocab.Type, iri []*url.URL) { 192 // 'inReplyTo' 193 if i, ok := o.(inReplyToer); ok { 194 if irt := i.GetActivityStreamsInReplyTo(); irt != nil { 195 for iter := irt.Begin(); iter != irt.End(); iter = iter.Next() { 196 if tv := iter.GetType(); tv != nil { 197 t = append(t, tv) 198 } else { 199 iri = append(iri, iter.GetIRI()) 200 } 201 } 202 } 203 } 204 // 'tag' 205 if i, ok := o.(tagger); ok { 206 if tag := i.GetActivityStreamsTag(); tag != nil { 207 for iter := tag.Begin(); iter != tag.End(); iter = iter.Next() { 208 if tv := iter.GetType(); tv != nil { 209 t = append(t, tv) 210 } else { 211 iri = append(iri, iter.GetIRI()) 212 } 213 } 214 } 215 } 216 // 'object' 217 if i, ok := o.(objecter); ok { 218 if obj := i.GetActivityStreamsObject(); obj != nil { 219 for iter := obj.Begin(); iter != obj.End(); iter = iter.Next() { 220 if tv := iter.GetType(); tv != nil { 221 t = append(t, tv) 222 } else { 223 iri = append(iri, iter.GetIRI()) 224 } 225 } 226 } 227 } 228 // 'target' 229 if i, ok := o.(targeter); ok { 230 if tar := i.GetActivityStreamsTarget(); tar != nil { 231 for iter := tar.Begin(); iter != tar.End(); iter = iter.Next() { 232 if tv := iter.GetType(); tv != nil { 233 t = append(t, tv) 234 } else { 235 iri = append(iri, iter.GetIRI()) 236 } 237 } 238 } 239 } 240 return 241 } 242 243 // wrapInCreate will automatically wrap the provided object in a Create 244 // activity. This will copy over the 'to', 'bto', 'cc', 'bcc', and 'audience' 245 // properties. It will also copy over the published time if present. 246 func wrapInCreate(ctx context.Context, o vocab.Type, actor *url.URL) (c vocab.ActivityStreamsCreate, err error) { 247 c = streams.NewActivityStreamsCreate() 248 // Object property 249 oProp := streams.NewActivityStreamsObjectProperty() 250 oProp.AppendType(o) 251 c.SetActivityStreamsObject(oProp) 252 // Actor Property 253 actorProp := streams.NewActivityStreamsActorProperty() 254 actorProp.AppendIRI(actor) 255 c.SetActivityStreamsActor(actorProp) 256 // Published Property 257 if v, ok := o.(publisheder); ok { 258 c.SetActivityStreamsPublished(v.GetActivityStreamsPublished()) 259 } 260 // Copying over properties. 261 if v, ok := o.(toer); ok { 262 if to := v.GetActivityStreamsTo(); to != nil { 263 activityTo := streams.NewActivityStreamsToProperty() 264 for iter := to.Begin(); iter != to.End(); iter = iter.Next() { 265 var id *url.URL 266 id, err = ToId(iter) 267 if err != nil { 268 return 269 } 270 activityTo.AppendIRI(id) 271 } 272 c.SetActivityStreamsTo(activityTo) 273 } 274 } 275 if v, ok := o.(btoer); ok { 276 if bto := v.GetActivityStreamsBto(); bto != nil { 277 activityBto := streams.NewActivityStreamsBtoProperty() 278 for iter := bto.Begin(); iter != bto.End(); iter = iter.Next() { 279 var id *url.URL 280 id, err = ToId(iter) 281 if err != nil { 282 return 283 } 284 activityBto.AppendIRI(id) 285 } 286 c.SetActivityStreamsBto(activityBto) 287 } 288 } 289 if v, ok := o.(ccer); ok { 290 if cc := v.GetActivityStreamsCc(); cc != nil { 291 activityCc := streams.NewActivityStreamsCcProperty() 292 for iter := cc.Begin(); iter != cc.End(); iter = iter.Next() { 293 var id *url.URL 294 id, err = ToId(iter) 295 if err != nil { 296 return 297 } 298 activityCc.AppendIRI(id) 299 } 300 c.SetActivityStreamsCc(activityCc) 301 } 302 } 303 if v, ok := o.(bccer); ok { 304 if bcc := v.GetActivityStreamsBcc(); bcc != nil { 305 activityBcc := streams.NewActivityStreamsBccProperty() 306 for iter := bcc.Begin(); iter != bcc.End(); iter = iter.Next() { 307 var id *url.URL 308 id, err = ToId(iter) 309 if err != nil { 310 return 311 } 312 activityBcc.AppendIRI(id) 313 } 314 c.SetActivityStreamsBcc(activityBcc) 315 } 316 } 317 if v, ok := o.(audiencer); ok { 318 if aud := v.GetActivityStreamsAudience(); aud != nil { 319 activityAudience := streams.NewActivityStreamsAudienceProperty() 320 for iter := aud.Begin(); iter != aud.End(); iter = iter.Next() { 321 var id *url.URL 322 id, err = ToId(iter) 323 if err != nil { 324 return 325 } 326 activityAudience.AppendIRI(id) 327 } 328 c.SetActivityStreamsAudience(activityAudience) 329 } 330 } 331 return 332 } 333 334 // filterURLs removes urls whose strings match the provided filter 335 func filterURLs(u []*url.URL, fn func(s string) bool) []*url.URL { 336 i := 0 337 for i < len(u) { 338 if fn(u[i].String()) { 339 u = append(u[:i], u[i+1:]...) 340 } else { 341 i++ 342 } 343 } 344 return u 345 } 346 347 const ( 348 // PublicActivityPubIRI is the IRI that indicates an Activity is meant 349 // to be visible for general public consumption. 350 PublicActivityPubIRI = "https://www.w3.org/ns/activitystreams#Public" 351 publicJsonLD = "Public" 352 publicJsonLDAS = "as:Public" 353 ) 354 355 // IsPublic determines if an IRI string is the Public collection as defined in 356 // the spec, including JSON-LD compliant collections. 357 func IsPublic(s string) bool { 358 return s == PublicActivityPubIRI || s == publicJsonLD || s == publicJsonLDAS 359 } 360 361 // getInboxes extracts the 'inbox' IRIs from actor types. 362 func getInboxes(t []vocab.Type) (u []*url.URL, err error) { 363 for _, elem := range t { 364 var iri *url.URL 365 iri, err = getInbox(elem) 366 if err != nil { 367 return 368 } 369 u = append(u, iri) 370 } 371 return 372 } 373 374 // getInbox extracts the 'inbox' IRI from an actor type. 375 func getInbox(t vocab.Type) (u *url.URL, err error) { 376 ib, ok := t.(inboxer) 377 if !ok { 378 err = fmt.Errorf("actor type %T has no inbox", t) 379 return 380 } 381 inbox := ib.GetActivityStreamsInbox() 382 return ToId(inbox) 383 } 384 385 // dedupeIRIs will deduplicate final inbox IRIs. The ignore list is applied to 386 // the final list. 387 func dedupeIRIs(recipients, ignored []*url.URL) (out []*url.URL) { 388 ignoredMap := make(map[string]bool, len(ignored)) 389 for _, elem := range ignored { 390 ignoredMap[elem.String()] = true 391 } 392 outMap := make(map[string]bool, len(recipients)) 393 for _, k := range recipients { 394 kStr := k.String() 395 if !ignoredMap[kStr] && !outMap[kStr] { 396 out = append(out, k) 397 outMap[kStr] = true 398 } 399 } 400 return 401 } 402 403 // removeOne removes any occurrences of entry from a slice of entries. 404 func removeOne(entries []*url.URL, entry *url.URL) (out []*url.URL) { 405 for _, e := range entries { 406 if e.String() != entry.String() { 407 out = append(out, e) 408 } 409 } 410 return out 411 } 412 413 // stripHiddenRecipients removes "bto" and "bcc" from the activity. 414 // 415 // Note that this requirement of the specification is under "Section 6: Client 416 // to Server Interactions", the Social API, and not the Federative API. 417 func stripHiddenRecipients(activity Activity) { 418 activity.SetActivityStreamsBto(nil) 419 activity.SetActivityStreamsBcc(nil) 420 op := activity.GetActivityStreamsObject() 421 if op != nil { 422 for iter := op.Begin(); iter != op.End(); iter = iter.Next() { 423 if v, ok := iter.GetType().(btoer); ok { 424 v.SetActivityStreamsBto(nil) 425 } 426 if v, ok := iter.GetType().(bccer); ok { 427 v.SetActivityStreamsBcc(nil) 428 } 429 } 430 } 431 } 432 433 // mustHaveActivityOriginMatchObjects ensures that the Host in the activity id 434 // IRI matches all of the Hosts in the object id IRIs. 435 func mustHaveActivityOriginMatchObjects(a Activity) error { 436 originIRI, err := GetId(a) 437 if err != nil { 438 return err 439 } 440 originHost := originIRI.Host 441 op := a.GetActivityStreamsObject() 442 if op == nil || op.Len() == 0 { 443 return nil 444 } 445 for iter := op.Begin(); iter != op.End(); iter = iter.Next() { 446 iri, err := ToId(iter) 447 if err != nil { 448 return err 449 } 450 if originHost != iri.Host { 451 return fmt.Errorf("object %q: not in activity origin", iri) 452 } 453 } 454 return nil 455 } 456 457 // normalizeRecipients ensures the activity and object have the same 'to', 458 // 'bto', 'cc', 'bcc', and 'audience' properties. Copy the Activity's recipients 459 // to objects, and the objects to the activity, but does NOT copy objects' 460 // recipients to each other. 461 func normalizeRecipients(a vocab.ActivityStreamsCreate) error { 462 // Phase 0: Acquire all recipients on the activity. 463 // 464 // Obtain the actorTo map 465 actorToMap := make(map[string]*url.URL) 466 actorTo := a.GetActivityStreamsTo() 467 if actorTo == nil { 468 actorTo = streams.NewActivityStreamsToProperty() 469 a.SetActivityStreamsTo(actorTo) 470 } 471 for iter := actorTo.Begin(); iter != actorTo.End(); iter = iter.Next() { 472 id, err := ToId(iter) 473 if err != nil { 474 return err 475 } 476 actorToMap[id.String()] = id 477 } 478 // Obtain the actorBto map 479 actorBtoMap := make(map[string]*url.URL) 480 actorBto := a.GetActivityStreamsBto() 481 if actorBto == nil { 482 actorBto = streams.NewActivityStreamsBtoProperty() 483 a.SetActivityStreamsBto(actorBto) 484 } 485 for iter := actorBto.Begin(); iter != actorBto.End(); iter = iter.Next() { 486 id, err := ToId(iter) 487 if err != nil { 488 return err 489 } 490 actorBtoMap[id.String()] = id 491 } 492 // Obtain the actorCc map 493 actorCcMap := make(map[string]*url.URL) 494 actorCc := a.GetActivityStreamsCc() 495 if actorCc == nil { 496 actorCc = streams.NewActivityStreamsCcProperty() 497 a.SetActivityStreamsCc(actorCc) 498 } 499 for iter := actorCc.Begin(); iter != actorCc.End(); iter = iter.Next() { 500 id, err := ToId(iter) 501 if err != nil { 502 return err 503 } 504 actorCcMap[id.String()] = id 505 } 506 // Obtain the actorBcc map 507 actorBccMap := make(map[string]*url.URL) 508 actorBcc := a.GetActivityStreamsBcc() 509 if actorBcc == nil { 510 actorBcc = streams.NewActivityStreamsBccProperty() 511 a.SetActivityStreamsBcc(actorBcc) 512 } 513 for iter := actorBcc.Begin(); iter != actorBcc.End(); iter = iter.Next() { 514 id, err := ToId(iter) 515 if err != nil { 516 return err 517 } 518 actorBccMap[id.String()] = id 519 } 520 // Obtain the actorAudience map 521 actorAudienceMap := make(map[string]*url.URL) 522 actorAudience := a.GetActivityStreamsAudience() 523 if actorAudience == nil { 524 actorAudience = streams.NewActivityStreamsAudienceProperty() 525 a.SetActivityStreamsAudience(actorAudience) 526 } 527 for iter := actorAudience.Begin(); iter != actorAudience.End(); iter = iter.Next() { 528 id, err := ToId(iter) 529 if err != nil { 530 return err 531 } 532 actorAudienceMap[id.String()] = id 533 } 534 // Obtain the objects maps for each recipient type. 535 o := a.GetActivityStreamsObject() 536 objsTo := make([]map[string]*url.URL, o.Len()) 537 objsBto := make([]map[string]*url.URL, o.Len()) 538 objsCc := make([]map[string]*url.URL, o.Len()) 539 objsBcc := make([]map[string]*url.URL, o.Len()) 540 objsAudience := make([]map[string]*url.URL, o.Len()) 541 for i := 0; i < o.Len(); i++ { 542 iter := o.At(i) 543 // Phase 1: Acquire all existing recipients on the object. 544 // 545 // Object to 546 objsTo[i] = make(map[string]*url.URL) 547 var oTo vocab.ActivityStreamsToProperty 548 if tr, ok := iter.GetType().(toer); !ok { 549 return fmt.Errorf("the Create object at %d has no 'to' property", i) 550 } else { 551 oTo = tr.GetActivityStreamsTo() 552 if oTo == nil { 553 oTo = streams.NewActivityStreamsToProperty() 554 tr.SetActivityStreamsTo(oTo) 555 } 556 } 557 for iter := oTo.Begin(); iter != oTo.End(); iter = iter.Next() { 558 id, err := ToId(iter) 559 if err != nil { 560 return err 561 } 562 objsTo[i][id.String()] = id 563 } 564 // Object bto 565 objsBto[i] = make(map[string]*url.URL) 566 var oBto vocab.ActivityStreamsBtoProperty 567 if tr, ok := iter.GetType().(btoer); !ok { 568 return fmt.Errorf("the Create object at %d has no 'bto' property", i) 569 } else { 570 oBto = tr.GetActivityStreamsBto() 571 if oBto == nil { 572 oBto = streams.NewActivityStreamsBtoProperty() 573 tr.SetActivityStreamsBto(oBto) 574 } 575 } 576 for iter := oBto.Begin(); iter != oBto.End(); iter = iter.Next() { 577 id, err := ToId(iter) 578 if err != nil { 579 return err 580 } 581 objsBto[i][id.String()] = id 582 } 583 // Object cc 584 objsCc[i] = make(map[string]*url.URL) 585 var oCc vocab.ActivityStreamsCcProperty 586 if tr, ok := iter.GetType().(ccer); !ok { 587 return fmt.Errorf("the Create object at %d has no 'cc' property", i) 588 } else { 589 oCc = tr.GetActivityStreamsCc() 590 if oCc == nil { 591 oCc = streams.NewActivityStreamsCcProperty() 592 tr.SetActivityStreamsCc(oCc) 593 } 594 } 595 for iter := oCc.Begin(); iter != oCc.End(); iter = iter.Next() { 596 id, err := ToId(iter) 597 if err != nil { 598 return err 599 } 600 objsCc[i][id.String()] = id 601 } 602 // Object bcc 603 objsBcc[i] = make(map[string]*url.URL) 604 var oBcc vocab.ActivityStreamsBccProperty 605 if tr, ok := iter.GetType().(bccer); !ok { 606 return fmt.Errorf("the Create object at %d has no 'bcc' property", i) 607 } else { 608 oBcc = tr.GetActivityStreamsBcc() 609 if oBcc == nil { 610 oBcc = streams.NewActivityStreamsBccProperty() 611 tr.SetActivityStreamsBcc(oBcc) 612 } 613 } 614 for iter := oBcc.Begin(); iter != oBcc.End(); iter = iter.Next() { 615 id, err := ToId(iter) 616 if err != nil { 617 return err 618 } 619 objsBcc[i][id.String()] = id 620 } 621 // Object audience 622 objsAudience[i] = make(map[string]*url.URL) 623 var oAudience vocab.ActivityStreamsAudienceProperty 624 if tr, ok := iter.GetType().(audiencer); !ok { 625 return fmt.Errorf("the Create object at %d has no 'audience' property", i) 626 } else { 627 oAudience = tr.GetActivityStreamsAudience() 628 if oAudience == nil { 629 oAudience = streams.NewActivityStreamsAudienceProperty() 630 tr.SetActivityStreamsAudience(oAudience) 631 } 632 } 633 for iter := oAudience.Begin(); iter != oAudience.End(); iter = iter.Next() { 634 id, err := ToId(iter) 635 if err != nil { 636 return err 637 } 638 objsAudience[i][id.String()] = id 639 } 640 // Phase 2: Apply missing recipients to the object from the 641 // activity. 642 // 643 // Activity to -> Object to 644 for k, v := range actorToMap { 645 if _, ok := objsTo[i][k]; !ok { 646 oTo.AppendIRI(v) 647 } 648 } 649 // Activity bto -> Object bto 650 for k, v := range actorBtoMap { 651 if _, ok := objsBto[i][k]; !ok { 652 oBto.AppendIRI(v) 653 } 654 } 655 // Activity cc -> Object cc 656 for k, v := range actorCcMap { 657 if _, ok := objsCc[i][k]; !ok { 658 oCc.AppendIRI(v) 659 } 660 } 661 // Activity bcc -> Object bcc 662 for k, v := range actorBccMap { 663 if _, ok := objsBcc[i][k]; !ok { 664 oBcc.AppendIRI(v) 665 } 666 } 667 // Activity audience -> Object audience 668 for k, v := range actorAudienceMap { 669 if _, ok := objsAudience[i][k]; !ok { 670 oAudience.AppendIRI(v) 671 } 672 } 673 } 674 // Phase 3: Apply missing recipients to the activity from the objects. 675 // 676 // Object to -> Activity to 677 for i := 0; i < len(objsTo); i++ { 678 for k, v := range objsTo[i] { 679 if _, ok := actorToMap[k]; !ok { 680 actorTo.AppendIRI(v) 681 } 682 } 683 } 684 // Object bto -> Activity bto 685 for i := 0; i < len(objsBto); i++ { 686 for k, v := range objsBto[i] { 687 if _, ok := actorBtoMap[k]; !ok { 688 actorBto.AppendIRI(v) 689 } 690 } 691 } 692 // Object cc -> Activity cc 693 for i := 0; i < len(objsCc); i++ { 694 for k, v := range objsCc[i] { 695 if _, ok := actorCcMap[k]; !ok { 696 actorCc.AppendIRI(v) 697 } 698 } 699 } 700 // Object bcc -> Activity bcc 701 for i := 0; i < len(objsBcc); i++ { 702 for k, v := range objsBcc[i] { 703 if _, ok := actorBccMap[k]; !ok { 704 actorBcc.AppendIRI(v) 705 } 706 } 707 } 708 // Object audience -> Activity audience 709 for i := 0; i < len(objsAudience); i++ { 710 for k, v := range objsAudience[i] { 711 if _, ok := actorAudienceMap[k]; !ok { 712 actorAudience.AppendIRI(v) 713 } 714 } 715 } 716 return nil 717 } 718 719 // toTombstone creates a Tombstone object for the given ActivityStreams value. 720 func toTombstone(obj vocab.Type, id *url.URL, now time.Time) vocab.ActivityStreamsTombstone { 721 tomb := streams.NewActivityStreamsTombstone() 722 // id property 723 idProp := streams.NewJSONLDIdProperty() 724 idProp.Set(id) 725 tomb.SetJSONLDId(idProp) 726 // formerType property 727 former := streams.NewActivityStreamsFormerTypeProperty() 728 tomb.SetActivityStreamsFormerType(former) 729 // Populate Former Type 730 former.AppendXMLSchemaString(obj.GetTypeName()) 731 // Copy over the published property if it existed 732 if pubber, ok := obj.(publisheder); ok { 733 if pub := pubber.GetActivityStreamsPublished(); pub != nil { 734 tomb.SetActivityStreamsPublished(pub) 735 } 736 } 737 // Copy over the updated property if it existed 738 if upder, ok := obj.(updateder); ok { 739 if upd := upder.GetActivityStreamsUpdated(); upd != nil { 740 tomb.SetActivityStreamsUpdated(upd) 741 } 742 } 743 // Set deleted time to now. 744 deleted := streams.NewActivityStreamsDeletedProperty() 745 deleted.Set(now) 746 tomb.SetActivityStreamsDeleted(deleted) 747 return tomb 748 } 749 750 // mustHaveActivityActorsMatchObjectActors ensures that the actors on types in 751 // the 'object' property are all listed in the 'actor' property. 752 func mustHaveActivityActorsMatchObjectActors(c context.Context, 753 actors vocab.ActivityStreamsActorProperty, 754 op vocab.ActivityStreamsObjectProperty, 755 newTransport func(c context.Context, actorBoxIRI *url.URL, gofedAgent string) (t Transport, err error), 756 boxIRI *url.URL, 757 ) error { 758 activityActorMap := make(map[string]bool, actors.Len()) 759 for iter := actors.Begin(); iter != actors.End(); iter = iter.Next() { 760 id, err := ToId(iter) 761 if err != nil { 762 return err 763 } 764 activityActorMap[id.String()] = true 765 } 766 for iter := op.Begin(); iter != op.End(); iter = iter.Next() { 767 iri, err := ToId(iter) 768 if err != nil { 769 return err 770 } 771 // Attempt to dereference the IRI, regardless whether it is a 772 // type or IRI 773 tport, err := newTransport(c, boxIRI, goFedUserAgent()) 774 if err != nil { 775 return err 776 } 777 b, err := tport.Dereference(c, iri) 778 if err != nil { 779 return err 780 } 781 var m map[string]interface{} 782 if err = json.Unmarshal(b, &m); err != nil { 783 return err 784 } 785 t, err := streams.ToType(c, m) 786 if err != nil { 787 return err 788 } 789 ac, ok := t.(actorer) 790 if !ok { 791 return fmt.Errorf("cannot verify actors: object value has no 'actor' property") 792 } 793 objActors := ac.GetActivityStreamsActor() 794 for iter := objActors.Begin(); iter != objActors.End(); iter = iter.Next() { 795 id, err := ToId(iter) 796 if err != nil { 797 return err 798 } 799 if !activityActorMap[id.String()] { 800 return fmt.Errorf("activity does not have all actors from its object's actors") 801 } 802 } 803 } 804 return nil 805 } 806 807 // add implements the logic of adding object ids to a target Collection or 808 // OrderedCollection. This logic is shared by both the C2S and S2S protocols. 809 func add(c context.Context, 810 op vocab.ActivityStreamsObjectProperty, 811 target vocab.ActivityStreamsTargetProperty, 812 db Database, 813 ) error { 814 opIds := make([]*url.URL, 0, op.Len()) 815 for iter := op.Begin(); iter != op.End(); iter = iter.Next() { 816 id, err := ToId(iter) 817 if err != nil { 818 return err 819 } 820 opIds = append(opIds, id) 821 } 822 targetIds := make([]*url.URL, 0, op.Len()) 823 for iter := target.Begin(); iter != target.End(); iter = iter.Next() { 824 id, err := ToId(iter) 825 if err != nil { 826 return err 827 } 828 targetIds = append(targetIds, id) 829 } 830 // Create anonymous loop function to be able to properly scope the defer 831 // for the database lock at each iteration. 832 loopFn := func(t *url.URL) error { 833 unlock, err := db.Lock(c, t) 834 if err != nil { 835 return err 836 } 837 defer unlock() 838 if owns, err := db.Owns(c, t); err != nil { 839 return err 840 } else if !owns { 841 return nil 842 } 843 tp, err := db.Get(c, t) 844 if err != nil { 845 return err 846 } 847 if streams.IsOrExtendsActivityStreamsOrderedCollection(tp) { 848 oi, ok := tp.(orderedItemser) 849 if !ok { 850 return fmt.Errorf("type extending from OrderedCollection cannot convert to orderedItemser interface") 851 } 852 oiProp := oi.GetActivityStreamsOrderedItems() 853 if oiProp == nil { 854 oiProp = streams.NewActivityStreamsOrderedItemsProperty() 855 oi.SetActivityStreamsOrderedItems(oiProp) 856 } 857 for _, objId := range opIds { 858 oiProp.AppendIRI(objId) 859 } 860 } else if streams.IsOrExtendsActivityStreamsCollection(tp) { 861 i, ok := tp.(itemser) 862 if !ok { 863 return fmt.Errorf("type extending from Collection cannot convert to itemser interface") 864 } 865 iProp := i.GetActivityStreamsItems() 866 if iProp == nil { 867 iProp = streams.NewActivityStreamsItemsProperty() 868 i.SetActivityStreamsItems(iProp) 869 } 870 for _, objId := range opIds { 871 iProp.AppendIRI(objId) 872 } 873 } else { 874 return fmt.Errorf("target in Add is neither a Collection nor an OrderedCollection") 875 } 876 err = db.Update(c, tp) 877 if err != nil { 878 return err 879 } 880 return nil 881 } 882 for _, t := range targetIds { 883 if err := loopFn(t); err != nil { 884 return err 885 } 886 } 887 return nil 888 } 889 890 // remove implements the logic of removing object ids to a target Collection or 891 // OrderedCollection. This logic is shared by both the C2S and S2S protocols. 892 func remove(c context.Context, 893 op vocab.ActivityStreamsObjectProperty, 894 target vocab.ActivityStreamsTargetProperty, 895 db Database, 896 ) error { 897 opIds := make(map[string]bool, op.Len()) 898 for iter := op.Begin(); iter != op.End(); iter = iter.Next() { 899 id, err := ToId(iter) 900 if err != nil { 901 return err 902 } 903 opIds[id.String()] = true 904 } 905 targetIds := make([]*url.URL, 0, op.Len()) 906 for iter := target.Begin(); iter != target.End(); iter = iter.Next() { 907 id, err := ToId(iter) 908 if err != nil { 909 return err 910 } 911 targetIds = append(targetIds, id) 912 } 913 // Create anonymous loop function to be able to properly scope the defer 914 // for the database lock at each iteration. 915 loopFn := func(t *url.URL) error { 916 unlock, err := db.Lock(c, t) 917 if err != nil { 918 return err 919 } 920 defer unlock() 921 if owns, err := db.Owns(c, t); err != nil { 922 return err 923 } else if !owns { 924 return nil 925 } 926 tp, err := db.Get(c, t) 927 if err != nil { 928 return err 929 } 930 if streams.IsOrExtendsActivityStreamsOrderedCollection(tp) { 931 oi, ok := tp.(orderedItemser) 932 if !ok { 933 return fmt.Errorf("type extending from OrderedCollection cannot convert to orderedItemser interface") 934 } 935 oiProp := oi.GetActivityStreamsOrderedItems() 936 if oiProp != nil { 937 for i := 0; i < oiProp.Len(); /*Conditional*/ { 938 id, err := ToId(oiProp.At(i)) 939 if err != nil { 940 return err 941 } 942 if opIds[id.String()] { 943 oiProp.Remove(i) 944 } else { 945 i++ 946 } 947 } 948 } 949 } else if streams.IsOrExtendsActivityStreamsCollection(tp) { 950 i, ok := tp.(itemser) 951 if !ok { 952 return fmt.Errorf("type extending from Collection cannot convert to itemser interface") 953 } 954 iProp := i.GetActivityStreamsItems() 955 if iProp != nil { 956 for i := 0; i < iProp.Len(); /*Conditional*/ { 957 id, err := ToId(iProp.At(i)) 958 if err != nil { 959 return err 960 } 961 if opIds[id.String()] { 962 iProp.Remove(i) 963 } else { 964 i++ 965 } 966 } 967 } 968 } else { 969 return fmt.Errorf("target in Remove is neither a Collection nor an OrderedCollection") 970 } 971 err = db.Update(c, tp) 972 if err != nil { 973 return err 974 } 975 return nil 976 } 977 for _, t := range targetIds { 978 if err := loopFn(t); err != nil { 979 return err 980 } 981 } 982 return nil 983 } 984 985 // clearSensitiveFields removes the 'bto' and 'bcc' entries on the given value 986 // and recursively on every 'object' property value. 987 func clearSensitiveFields(obj vocab.Type) { 988 if t, ok := obj.(btoer); ok { 989 t.SetActivityStreamsBto(nil) 990 } 991 if t, ok := obj.(bccer); ok { 992 t.SetActivityStreamsBcc(nil) 993 } 994 if t, ok := obj.(objecter); ok { 995 op := t.GetActivityStreamsObject() 996 if op != nil { 997 for iter := op.Begin(); iter != op.End(); iter = iter.Next() { 998 clearSensitiveFields(iter.GetType()) 999 } 1000 } 1001 } 1002 } 1003 1004 // requestId forms an ActivityPub id based on the HTTP request. Always assumes 1005 // that the id is HTTPS. 1006 func requestId(r *http.Request, scheme string) *url.URL { 1007 id := r.URL 1008 id.Host = r.Host 1009 id.Scheme = scheme 1010 return id 1011 }