this repo has no description
1package state 2 3import ( 4 "context" 5 "log" 6 "net/http" 7 "strings" 8 "time" 9 10 comatproto "github.com/bluesky-social/indigo/api/atproto" 11 "github.com/bluesky-social/indigo/atproto/identity" 12 "github.com/bluesky-social/indigo/xrpc" 13 "github.com/go-chi/chi/v5" 14 "github.com/sotangled/tangled/appview" 15 "github.com/sotangled/tangled/appview/auth" 16) 17 18type Middleware func(http.Handler) http.Handler 19 20func AuthMiddleware(s *State) Middleware { 21 return func(next http.Handler) http.Handler { 22 return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { 23 session, _ := s.auth.Store.Get(r, appview.SessionName) 24 authorized, ok := session.Values[appview.SessionAuthenticated].(bool) 25 if !ok || !authorized { 26 log.Printf("not logged in, redirecting") 27 http.Redirect(w, r, "/login", http.StatusTemporaryRedirect) 28 return 29 } 30 31 // refresh if nearing expiry 32 // TODO: dedup with /login 33 expiryStr := session.Values[appview.SessionExpiry].(string) 34 expiry, err := time.Parse(time.RFC3339, expiryStr) 35 if err != nil { 36 log.Println("invalid expiry time", err) 37 return 38 } 39 pdsUrl := session.Values[appview.SessionPds].(string) 40 did := session.Values[appview.SessionDid].(string) 41 refreshJwt := session.Values[appview.SessionRefreshJwt].(string) 42 43 if time.Now().After(expiry) { 44 log.Println("token expired, refreshing ...") 45 46 client := xrpc.Client{ 47 Host: pdsUrl, 48 Auth: &xrpc.AuthInfo{ 49 Did: did, 50 AccessJwt: refreshJwt, 51 RefreshJwt: refreshJwt, 52 }, 53 } 54 atSession, err := comatproto.ServerRefreshSession(r.Context(), &client) 55 if err != nil { 56 log.Println(err) 57 return 58 } 59 60 sessionish := auth.RefreshSessionWrapper{atSession} 61 62 err = s.auth.StoreSession(r, w, &sessionish, pdsUrl) 63 if err != nil { 64 log.Printf("failed to store session for did: %s\n: %s", atSession.Did, err) 65 return 66 } 67 68 log.Println("successfully refreshed token") 69 } 70 71 next.ServeHTTP(w, r) 72 }) 73 } 74} 75 76func RoleMiddleware(s *State, group string) Middleware { 77 return func(next http.Handler) http.Handler { 78 return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { 79 // requires auth also 80 actor := s.auth.GetUser(r) 81 if actor == nil { 82 // we need a logged in user 83 log.Printf("not logged in, redirecting") 84 http.Error(w, "Forbiden", http.StatusUnauthorized) 85 return 86 } 87 domain := chi.URLParam(r, "domain") 88 if domain == "" { 89 http.Error(w, "malformed url", http.StatusBadRequest) 90 return 91 } 92 93 ok, err := s.enforcer.E.HasGroupingPolicy(actor.Did, group, domain) 94 if err != nil || !ok { 95 // we need a logged in user 96 log.Printf("%s does not have perms of a %s in domain %s", actor.Did, group, domain) 97 http.Error(w, "Forbiden", http.StatusUnauthorized) 98 return 99 } 100 101 next.ServeHTTP(w, r) 102 }) 103 } 104} 105 106func StripLeadingAt(next http.Handler) http.Handler { 107 return http.HandlerFunc(func(w http.ResponseWriter, req *http.Request) { 108 path := req.URL.Path 109 if strings.HasPrefix(path, "/@") { 110 req.URL.Path = "/" + strings.TrimPrefix(path, "/@") 111 } 112 next.ServeHTTP(w, req) 113 }) 114} 115 116func ResolveIdent(next http.Handler) http.Handler { 117 return http.HandlerFunc(func(w http.ResponseWriter, req *http.Request) { 118 didOrHandle := chi.URLParam(req, "user") 119 120 log.Println(didOrHandle) 121 id, err := auth.ResolveIdent(req.Context(), didOrHandle) 122 if err != nil { 123 // invalid did or handle 124 log.Println("failed to resolve did/handle") 125 w.WriteHeader(http.StatusNotFound) 126 return 127 } 128 129 ctx := context.WithValue(req.Context(), "resolvedId", *id) 130 next.ServeHTTP(w, req.WithContext(ctx)) 131 }) 132} 133 134func ResolveRepoKnot(s *State) Middleware { 135 return func(next http.Handler) http.Handler { 136 return http.HandlerFunc(func(w http.ResponseWriter, req *http.Request) { 137 repoName := chi.URLParam(req, "repo") 138 id, ok := req.Context().Value("resolvedId").(identity.Identity) 139 if !ok { 140 log.Println("malformed middleware") 141 w.WriteHeader(http.StatusInternalServerError) 142 return 143 } 144 145 repo, err := s.db.GetRepo(id.DID.String(), repoName) 146 if err != nil { 147 // invalid did or handle 148 log.Println("failed to resolve repo") 149 w.WriteHeader(http.StatusNotFound) 150 return 151 } 152 153 ctx := context.WithValue(req.Context(), "knot", repo.Knot) 154 next.ServeHTTP(w, req.WithContext(ctx)) 155 }) 156 } 157}