forked from tangled.org/core
Monorepo for Tangled

appview,rbac: ingest sh.tangled.knot{,member} records

The knot.member is only ingested for create/update ops but we can't
delete it yet. See comment.

Signed-off-by: Anirudh Oppiliappan <anirudh@tangled.sh>

anirudh.fi fe10e193 9db30f0a

verified
Changed files
+165 -3
appview
rbac
+160 -3
appview/ingester.go
··· 14 14 "tangled.sh/tangled.sh/core/api/tangled" 15 15 "tangled.sh/tangled.sh/core/appview/config" 16 16 "tangled.sh/tangled.sh/core/appview/db" 17 - "tangled.sh/tangled.sh/core/appview/spindleverify" 17 + "tangled.sh/tangled.sh/core/appview/serververify" 18 18 "tangled.sh/tangled.sh/core/idresolver" 19 19 "tangled.sh/tangled.sh/core/rbac" 20 20 ) ··· 64 64 err = i.ingestSpindleMember(e) 65 65 case tangled.SpindleNSID: 66 66 err = i.ingestSpindle(e) 67 + case tangled.KnotMemberNSID: 68 + err = i.ingestKnotMember(e) 69 + case tangled.KnotNSID: 70 + err = i.ingestKnot(e) 67 71 case tangled.StringNSID: 68 72 err = i.ingestString(e) 69 73 } ··· 475 479 return err 476 480 } 477 481 478 - err = spindleverify.RunVerification(context.Background(), instance, did, i.Config.Core.Dev) 482 + err = serververify.RunVerification(context.Background(), instance, did, i.Config.Core.Dev) 479 483 if err != nil { 480 484 l.Error("failed to add spindle to db", "err", err, "instance", instance) 481 485 return err 482 486 } 483 487 484 - _, err = spindleverify.MarkVerified(ddb, i.Enforcer, instance, did) 488 + _, err = serververify.MarkSpindleVerified(ddb, i.Enforcer, instance, did) 485 489 if err != nil { 486 490 return fmt.Errorf("failed to mark verified: %w", err) 487 491 } ··· 609 613 610 614 return nil 611 615 } 616 + 617 + func (i *Ingester) ingestKnotMember(e *models.Event) error { 618 + did := e.Did 619 + var err error 620 + 621 + l := i.Logger.With("handler", "ingestKnotMember") 622 + l = l.With("nsid", e.Commit.Collection) 623 + 624 + switch e.Commit.Operation { 625 + case models.CommitOperationCreate: 626 + raw := json.RawMessage(e.Commit.Record) 627 + record := tangled.KnotMember{} 628 + err = json.Unmarshal(raw, &record) 629 + if err != nil { 630 + l.Error("invalid record", "err", err) 631 + return err 632 + } 633 + 634 + // only knot owner can invite to knots 635 + ok, err := i.Enforcer.IsKnotInviteAllowed(did, record.Domain) 636 + if err != nil || !ok { 637 + return fmt.Errorf("failed to enforce permissions: %w", err) 638 + } 639 + 640 + memberId, err := i.IdResolver.ResolveIdent(context.Background(), record.Subject) 641 + if err != nil { 642 + return err 643 + } 644 + 645 + if memberId.Handle.IsInvalidHandle() { 646 + return err 647 + } 648 + 649 + err = i.Enforcer.AddKnotMember(record.Domain, memberId.DID.String()) 650 + if err != nil { 651 + return fmt.Errorf("failed to update ACLs: %w", err) 652 + } 653 + 654 + l.Info("added knot member") 655 + case models.CommitOperationDelete: 656 + // we don't store knot members in a table (like we do for spindle) 657 + // and we can't remove this just yet. possibly fixed if we switch 658 + // to either: 659 + // 1. a knot_members table like with spindle and store the rkey 660 + // 2. use the knot host as the rkey 661 + // 662 + // TODO: implement member deletion 663 + l.Info("skipping knot member delete", "did", did, "rkey", e.Commit.RKey) 664 + } 665 + 666 + return nil 667 + } 668 + 669 + func (i *Ingester) ingestKnot(e *models.Event) error { 670 + did := e.Did 671 + var err error 672 + 673 + l := i.Logger.With("handler", "ingestKnot") 674 + l = l.With("nsid", e.Commit.Collection) 675 + 676 + switch e.Commit.Operation { 677 + case models.CommitOperationCreate: 678 + raw := json.RawMessage(e.Commit.Record) 679 + record := tangled.Knot{} 680 + err = json.Unmarshal(raw, &record) 681 + if err != nil { 682 + l.Error("invalid record", "err", err) 683 + return err 684 + } 685 + 686 + domain := e.Commit.RKey 687 + 688 + ddb, ok := i.Db.Execer.(*db.DB) 689 + if !ok { 690 + return fmt.Errorf("failed to index profile record, invalid db cast") 691 + } 692 + 693 + err := db.AddKnot(ddb, domain, did) 694 + if err != nil { 695 + l.Error("failed to add knot to db", "err", err, "domain", domain) 696 + return err 697 + } 698 + 699 + err = serververify.RunVerification(context.Background(), domain, did, i.Config.Core.Dev) 700 + if err != nil { 701 + l.Error("failed to verify knot", "err", err, "domain", domain) 702 + return err 703 + } 704 + 705 + err = serververify.MarkKnotVerified(ddb, i.Enforcer, domain, did) 706 + if err != nil { 707 + return fmt.Errorf("failed to mark verified: %w", err) 708 + } 709 + 710 + return nil 711 + 712 + case models.CommitOperationDelete: 713 + domain := e.Commit.RKey 714 + 715 + ddb, ok := i.Db.Execer.(*db.DB) 716 + if !ok { 717 + return fmt.Errorf("failed to index profile record, invalid db cast") 718 + } 719 + 720 + // get record from db first 721 + registration, err := db.RegistrationByDomain(ddb, domain) 722 + if err != nil { 723 + return fmt.Errorf("failed to get registration: %w", err) 724 + } 725 + 726 + // only allow deletion by the owner 727 + if registration.ByDid != did { 728 + return fmt.Errorf("unauthorized deletion attempt") 729 + } 730 + 731 + tx, err := ddb.Begin() 732 + if err != nil { 733 + return err 734 + } 735 + defer func() { 736 + tx.Rollback() 737 + i.Enforcer.E.LoadPolicy() 738 + }() 739 + 740 + err = db.DeleteKnot( 741 + tx, 742 + db.FilterEq("did", did), 743 + db.FilterEq("domain", domain), 744 + ) 745 + if err != nil { 746 + return err 747 + } 748 + 749 + if registration.Registered != nil { 750 + err = i.Enforcer.RemoveKnot(domain) 751 + if err != nil { 752 + return err 753 + } 754 + } 755 + 756 + err = tx.Commit() 757 + if err != nil { 758 + return err 759 + } 760 + 761 + err = i.Enforcer.E.SavePolicy() 762 + if err != nil { 763 + return err 764 + } 765 + } 766 + 767 + return nil 768 + }
+5
rbac/rbac.go
··· 100 100 return err 101 101 } 102 102 103 + func (e *Enforcer) RemoveKnot(knot string) error { 104 + _, err := e.E.DeleteDomains(knot) 105 + return err 106 + } 107 + 103 108 func (e *Enforcer) GetKnotsForUser(did string) ([]string, error) { 104 109 keepFunc := isNotSpindle 105 110 stripFunc := unSpindle