this repo has no description
1package state
2
3import (
4 "log"
5 "net/http"
6 "strings"
7 "time"
8
9 comatproto "github.com/bluesky-social/indigo/api/atproto"
10 "github.com/bluesky-social/indigo/xrpc"
11 "github.com/go-chi/chi/v5"
12 "github.com/sotangled/tangled/appview"
13 "github.com/sotangled/tangled/appview/auth"
14)
15
16type Middleware func(http.Handler) http.Handler
17
18func AuthMiddleware(s *State) Middleware {
19 return func(next http.Handler) http.Handler {
20 return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
21 session, _ := s.auth.Store.Get(r, appview.SessionName)
22 authorized, ok := session.Values[appview.SessionAuthenticated].(bool)
23 if !ok || !authorized {
24 log.Printf("not logged in, redirecting")
25 http.Redirect(w, r, "/login", http.StatusTemporaryRedirect)
26 return
27 }
28
29 // refresh if nearing expiry
30 // TODO: dedup with /login
31 expiryStr := session.Values[appview.SessionExpiry].(string)
32 expiry, err := time.Parse(time.RFC3339, expiryStr)
33 if err != nil {
34 log.Println("invalid expiry time", err)
35 return
36 }
37 pdsUrl := session.Values[appview.SessionPds].(string)
38 did := session.Values[appview.SessionDid].(string)
39 refreshJwt := session.Values[appview.SessionRefreshJwt].(string)
40
41 if time.Now().After(expiry) {
42 log.Println("token expired, refreshing ...")
43
44 client := xrpc.Client{
45 Host: pdsUrl,
46 Auth: &xrpc.AuthInfo{
47 Did: did,
48 AccessJwt: refreshJwt,
49 RefreshJwt: refreshJwt,
50 },
51 }
52 atSession, err := comatproto.ServerRefreshSession(r.Context(), &client)
53 if err != nil {
54 log.Println(err)
55 return
56 }
57
58 sessionish := auth.RefreshSessionWrapper{atSession}
59
60 err = s.auth.StoreSession(r, w, &sessionish, pdsUrl)
61 if err != nil {
62 log.Printf("failed to store session for did: %s\n: %s", atSession.Did, err)
63 return
64 }
65
66 log.Println("successfully refreshed token")
67 }
68
69 next.ServeHTTP(w, r)
70 })
71 }
72}
73
74func RoleMiddleware(s *State, group string) Middleware {
75 return func(next http.Handler) http.Handler {
76 return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
77 // requires auth also
78 actor := s.auth.GetUser(r)
79 if actor == nil {
80 // we need a logged in user
81 log.Printf("not logged in, redirecting")
82 http.Error(w, "Forbiden", http.StatusUnauthorized)
83 return
84 }
85 domain := chi.URLParam(r, "domain")
86 if domain == "" {
87 http.Error(w, "malformed url", http.StatusBadRequest)
88 return
89 }
90
91 ok, err := s.enforcer.E.HasGroupingPolicy(actor.Did, group, domain)
92 if err != nil || !ok {
93 // we need a logged in user
94 log.Printf("%s does not have perms of a %s in domain %s", actor.Did, group, domain)
95 http.Error(w, "Forbiden", http.StatusUnauthorized)
96 return
97 }
98
99 next.ServeHTTP(w, r)
100 })
101 }
102}
103
104func StripLeadingAt(next http.Handler) http.Handler {
105 return http.HandlerFunc(func(w http.ResponseWriter, req *http.Request) {
106 path := req.URL.Path
107 if strings.HasPrefix(path, "/@") {
108 req.URL.Path = "/" + strings.TrimPrefix(path, "/@")
109 }
110 next.ServeHTTP(w, req)
111 })
112}