forked from tangled.org/core
this repo has no description
1package state 2 3import ( 4 "context" 5 "log" 6 "net/http" 7 "strconv" 8 "strings" 9 "time" 10 11 "slices" 12 13 "github.com/bluesky-social/indigo/atproto/identity" 14 "github.com/go-chi/chi/v5" 15 "tangled.sh/tangled.sh/core/appview/db" 16 "tangled.sh/tangled.sh/core/appview/middleware" 17) 18 19func knotRoleMiddleware(s *State, group string) middleware.Middleware { 20 return func(next http.Handler) http.Handler { 21 return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { 22 // requires auth also 23 actor := s.auth.GetUser(r) 24 if actor == nil { 25 // we need a logged in user 26 log.Printf("not logged in, redirecting") 27 http.Error(w, "Forbiden", http.StatusUnauthorized) 28 return 29 } 30 domain := chi.URLParam(r, "domain") 31 if domain == "" { 32 http.Error(w, "malformed url", http.StatusBadRequest) 33 return 34 } 35 36 ok, err := s.enforcer.E.HasGroupingPolicy(actor.Did, group, domain) 37 if err != nil || !ok { 38 // we need a logged in user 39 log.Printf("%s does not have perms of a %s in domain %s", actor.Did, group, domain) 40 http.Error(w, "Forbiden", http.StatusUnauthorized) 41 return 42 } 43 44 next.ServeHTTP(w, r) 45 }) 46 } 47} 48 49func KnotOwner(s *State) middleware.Middleware { 50 return knotRoleMiddleware(s, "server:owner") 51} 52 53func RepoPermissionMiddleware(s *State, requiredPerm string) middleware.Middleware { 54 return func(next http.Handler) http.Handler { 55 return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { 56 // requires auth also 57 actor := s.auth.GetUser(r) 58 if actor == nil { 59 // we need a logged in user 60 log.Printf("not logged in, redirecting") 61 http.Error(w, "Forbiden", http.StatusUnauthorized) 62 return 63 } 64 f, err := fullyResolvedRepo(r) 65 if err != nil { 66 http.Error(w, "malformed url", http.StatusBadRequest) 67 return 68 } 69 70 ok, err := s.enforcer.E.Enforce(actor.Did, f.Knot, f.DidSlashRepo(), requiredPerm) 71 if err != nil || !ok { 72 // we need a logged in user 73 log.Printf("%s does not have perms of a %s in repo %s", actor.Did, requiredPerm, f.OwnerSlashRepo()) 74 http.Error(w, "Forbiden", http.StatusUnauthorized) 75 return 76 } 77 78 next.ServeHTTP(w, r) 79 }) 80 } 81} 82 83func StripLeadingAt(next http.Handler) http.Handler { 84 return http.HandlerFunc(func(w http.ResponseWriter, req *http.Request) { 85 path := req.URL.EscapedPath() 86 if strings.HasPrefix(path, "/@") { 87 req.URL.RawPath = "/" + strings.TrimPrefix(path, "/@") 88 } 89 next.ServeHTTP(w, req) 90 }) 91} 92 93func ResolveIdent(s *State) middleware.Middleware { 94 excluded := []string{"favicon.ico"} 95 96 return func(next http.Handler) http.Handler { 97 return http.HandlerFunc(func(w http.ResponseWriter, req *http.Request) { 98 didOrHandle := chi.URLParam(req, "user") 99 if slices.Contains(excluded, didOrHandle) { 100 next.ServeHTTP(w, req) 101 return 102 } 103 104 id, err := s.resolver.ResolveIdent(req.Context(), didOrHandle) 105 if err != nil { 106 // invalid did or handle 107 log.Println("failed to resolve did/handle:", err) 108 w.WriteHeader(http.StatusNotFound) 109 return 110 } 111 112 ctx := context.WithValue(req.Context(), "resolvedId", *id) 113 114 next.ServeHTTP(w, req.WithContext(ctx)) 115 }) 116 } 117} 118 119func ResolveRepo(s *State) middleware.Middleware { 120 return func(next http.Handler) http.Handler { 121 return http.HandlerFunc(func(w http.ResponseWriter, req *http.Request) { 122 repoName := chi.URLParam(req, "repo") 123 id, ok := req.Context().Value("resolvedId").(identity.Identity) 124 if !ok { 125 log.Println("malformed middleware") 126 w.WriteHeader(http.StatusInternalServerError) 127 return 128 } 129 130 repo, err := db.GetRepo(s.db, id.DID.String(), repoName) 131 if err != nil { 132 // invalid did or handle 133 log.Println("failed to resolve repo") 134 w.WriteHeader(http.StatusNotFound) 135 return 136 } 137 138 ctx := context.WithValue(req.Context(), "knot", repo.Knot) 139 ctx = context.WithValue(ctx, "repoAt", repo.AtUri) 140 ctx = context.WithValue(ctx, "repoDescription", repo.Description) 141 ctx = context.WithValue(ctx, "repoAddedAt", repo.Created.Format(time.RFC3339)) 142 next.ServeHTTP(w, req.WithContext(ctx)) 143 }) 144 } 145} 146 147// middleware that is tacked on top of /{user}/{repo}/pulls/{pull} 148func ResolvePull(s *State) middleware.Middleware { 149 return func(next http.Handler) http.Handler { 150 return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { 151 f, err := fullyResolvedRepo(r) 152 if err != nil { 153 log.Println("failed to fully resolve repo", err) 154 http.Error(w, "invalid repo url", http.StatusNotFound) 155 return 156 } 157 158 prId := chi.URLParam(r, "pull") 159 prIdInt, err := strconv.Atoi(prId) 160 if err != nil { 161 http.Error(w, "bad pr id", http.StatusBadRequest) 162 log.Println("failed to parse pr id", err) 163 return 164 } 165 166 pr, err := db.GetPull(s.db, f.RepoAt, prIdInt) 167 if err != nil { 168 log.Println("failed to get pull and comments", err) 169 return 170 } 171 172 ctx := context.WithValue(r.Context(), "pull", pr) 173 174 next.ServeHTTP(w, r.WithContext(ctx)) 175 }) 176 } 177}