A vibe coded tangled fork which supports pijul.
at master 110 lines 2.9 kB view raw
1package xrpc 2 3import ( 4 "net/http" 5 "os" 6 "slices" 7 "strings" 8 9 "github.com/bluesky-social/indigo/atproto/syntax" 10 securejoin "github.com/cyphar/filepath-securejoin" 11 "tangled.org/core/api/tangled" 12 "tangled.org/core/rbac" 13 xrpcerr "tangled.org/core/xrpc/errors" 14) 15 16const ( 17 permPush int64 = 1 << iota 18 permSettings 19 permCreateDiscussion 20 permEditDiscussion 21 permTagDiscussion 22 permOwner 23 permInvite 24 permDelete 25) 26 27// RepoPermissions returns the permissions the authenticated user has on a repository. 28// This endpoint is VCS-agnostic — it works for both git and pijul repos. 29func (x *Xrpc) RepoPermissions(w http.ResponseWriter, r *http.Request) { 30 l := x.Logger.With("handler", "RepoPermissions") 31 fail := func(e xrpcerr.XrpcError, status int) { 32 l.Error("failed", "kind", e.Tag, "error", e.Message) 33 writeError(w, e, status) 34 } 35 36 actorDid, ok := r.Context().Value(ActorDid).(syntax.DID) 37 if !ok { 38 fail(xrpcerr.MissingActorDidError, http.StatusBadRequest) 39 return 40 } 41 42 repo := r.URL.Query().Get("repo") 43 if repo == "" { 44 fail(xrpcerr.NewXrpcError( 45 xrpcerr.WithTag("InvalidRequest"), 46 xrpcerr.WithMessage("missing repo parameter"), 47 ), http.StatusBadRequest) 48 return 49 } 50 51 repoPath, err := x.parseRepoParam(repo) 52 if err != nil { 53 fail(err.(xrpcerr.XrpcError), http.StatusBadRequest) 54 return 55 } 56 57 if _, err := os.Stat(repoPath); err != nil { 58 if os.IsNotExist(err) { 59 writeError(w, xrpcerr.RepoNotFoundError, http.StatusNoContent) 60 return 61 } 62 fail(xrpcerr.GenericError(err), http.StatusInternalServerError) 63 return 64 } 65 66 repoParts := strings.SplitN(repo, "/", 2) 67 if len(repoParts) != 2 { 68 fail(xrpcerr.NewXrpcError( 69 xrpcerr.WithTag("InvalidRequest"), 70 xrpcerr.WithMessage("invalid repo format, expected 'did/repoName'"), 71 ), http.StatusBadRequest) 72 return 73 } 74 75 didSlashRepo, err := securejoin.SecureJoin(repoParts[0], repoParts[1]) 76 if err != nil { 77 fail(xrpcerr.InvalidRepoError(repo), http.StatusBadRequest) 78 return 79 } 80 81 roles := x.Enforcer.GetPermissionsInRepo(actorDid.String(), rbac.ThisServer, didSlashRepo) 82 83 var mask int64 84 var permissions []string 85 add := func(name string, bit int64, ok bool) { 86 if !ok { 87 return 88 } 89 mask |= bit 90 permissions = append(permissions, name) 91 } 92 93 has := func(perm string) bool { 94 return slices.Contains(roles, perm) 95 } 96 97 add("push", permPush, has("repo:push")) 98 add("settings", permSettings, has("repo:settings")) 99 add("create_discussion", permCreateDiscussion, has("repo:create_discussion")) 100 add("edit_discussion", permEditDiscussion, has("repo:edit_discussion")) 101 add("tag_discussion", permTagDiscussion, has("repo:tag_discussion")) 102 add("owner", permOwner, has("repo:owner")) 103 add("invite", permInvite, has("repo:invite")) 104 add("delete", permDelete, has("repo:delete")) 105 106 writeJson(w, tangled.RepoPermissions_Output{ 107 Mask: mask, 108 Permissions: permissions, 109 }) 110}