Monorepo for Tangled tangled.org
6

Configure Feed

Select the types of activity you want to include in your feed.

1package xrpc 2 3import ( 4 "database/sql" 5 "encoding/json" 6 "net/http" 7 8 "github.com/bluesky-social/indigo/atproto/syntax" 9 "tangled.org/core/api/tangled" 10 "tangled.org/core/knotserver/db" 11 "tangled.org/core/rbac" 12 xrpcerr "tangled.org/core/xrpc/errors" 13) 14 15type collabTarget struct { 16 repoDid syntax.DID 17 subject syntax.DID 18 ownerNoop bool 19} 20 21func (h *Xrpc) resolveCollabTarget(actor syntax.DID, repo, subject string) (collabTarget, int, *xrpcerr.XrpcError) { 22 fail := func(status int, e xrpcerr.XrpcError) (collabTarget, int, *xrpcerr.XrpcError) { 23 return collabTarget{}, status, &e 24 } 25 26 repoDid, err := syntax.ParseDID(repo) 27 if err != nil { 28 return fail(http.StatusBadRequest, xrpcerr.InvalidRepoError(repo)) 29 } 30 subjectDid, err := syntax.ParseDID(subject) 31 if err != nil { 32 return fail(http.StatusBadRequest, xrpcerr.GenericError(err)) 33 } 34 35 exists, err := h.Db.RepoDidExists(repoDid.String()) 36 if err != nil { 37 return fail(http.StatusInternalServerError, xrpcerr.GenericError(err)) 38 } 39 if !exists { 40 return fail(http.StatusNotFound, xrpcerr.RepoNotFoundError) 41 } 42 43 allowed, err := h.Enforcer.IsCollaboratorInviteAllowed(actor.String(), rbac.ThisServer, repoDid.String()) 44 if err != nil { 45 return fail(http.StatusInternalServerError, xrpcerr.GenericError(err)) 46 } 47 if !allowed { 48 return fail(http.StatusForbidden, xrpcerr.AccessControlError(actor.String())) 49 } 50 51 isOwner, err := h.Enforcer.IsRepoOwner(subjectDid.String(), rbac.ThisServer, repoDid.String()) 52 if err != nil { 53 return fail(http.StatusInternalServerError, xrpcerr.GenericError(err)) 54 } 55 56 return collabTarget{repoDid: repoDid, subject: subjectDid, ownerNoop: isOwner}, 0, nil 57} 58 59func (h *Xrpc) AddCollaborator(w http.ResponseWriter, r *http.Request) { 60 l := h.Logger.With("handler", "AddCollaborator") 61 fail := func(e xrpcerr.XrpcError, status int) { 62 l.Error("failed", "kind", e.Tag, "error", e.Message) 63 writeError(w, e, status) 64 } 65 66 actorDid, ok := r.Context().Value(ActorDid).(syntax.DID) 67 if !ok { 68 fail(xrpcerr.MissingActorDidError, http.StatusForbidden) 69 return 70 } 71 72 var data tangled.RepoAddCollaborator_Input 73 if err := json.NewDecoder(r.Body).Decode(&data); err != nil { 74 fail(xrpcerr.GenericError(err), http.StatusBadRequest) 75 return 76 } 77 78 t, status, xerr := h.resolveCollabTarget(actorDid, data.Repo, data.Subject) 79 if xerr != nil { 80 fail(*xerr, status) 81 return 82 } 83 if t.ownerNoop { 84 l.Info("subject is the repo owner, no-op", "repoDid", t.repoDid, "subject", t.subject) 85 w.WriteHeader(http.StatusOK) 86 return 87 } 88 89 status, xerr = h.applyAclGrant(r.Context(), l.With("repoDid", t.repoDid), aclGrant{ 90 role: "collaborator", 91 subject: t.subject, 92 inAcl: func() (bool, error) { 93 return h.Enforcer.IsRepoCollaborator(t.subject.String(), rbac.ThisServer, t.repoDid.String()) 94 }, 95 inTable: func() (bool, error) { 96 return db.IsCollaborator(h.Db, t.repoDid, t.subject) 97 }, 98 insertRow: func(tx *sql.Tx) error { 99 return db.AddCollaborator(tx, db.Collaborator{ 100 RepoDid: t.repoDid, 101 Subject: t.subject, 102 AddedBy: actorDid, 103 }) 104 }, 105 deleteRow: func() error { 106 return db.RemoveCollaborator(h.Db, t.repoDid, t.subject) 107 }, 108 grantAcl: func() error { 109 return h.Enforcer.AddCollaborator(t.subject.String(), rbac.ThisServer, t.repoDid.String()) 110 }, 111 emit: func() error { 112 return h.Db.EmitCollaboratorUpdate(h.Notifier, db.AclOpAdd, t.subject, t.repoDid) 113 }, 114 }) 115 if xerr != nil { 116 fail(*xerr, status) 117 return 118 } 119 w.WriteHeader(status) 120}