oficina / memoria Public

Clone
git clone https://amadan.net/oficina/memoria

Plain git — no account needed to clone.

Download

Download this file

1package memoria
2
3import (
4 "context"
5 "net/http"
6 "time"
7
8 "amadan.net/oficina/home/connect"
9 "amadan.net/oficina/home/connect/linkstore"
10 "amadan.net/oficina/home/handoff"
11 "amadan.net/rastrillo/rastrillo/csrf"
12 "amadan.net/rastrillo/rastrillo/migrate"
13 "amadan.net/rastrillo/rastrillo/sessions"
14)
15
16type homeIntegration struct {
17 bridge *connect.Bridge
18 routes *connect.Routes
19 sessions *sessions.Sessions
20}
21
22// WireHome keeps the local account's address as the subject used for existing
23// owner and sibling authorization. Home subjects never stand in for addresses.
24func (a *app) WireHome(mux *http.ServeMux, cfg Config) error {
25 if cfg.Home == nil {
26 return nil
27 }
28 if a.home != nil || a.auth == nil || mux == nil {
29 return errHomeIdentity
30 }
31 h := cfg.Home
32 schema := migrate.Merge(homeIdentitySchema, migrate.MustFromFS(handoff.Migrations, "oficina_handoff"), migrate.MustFromFS(linkstore.Migrations, linkstore.SchemaNamespace))
33 if _, err := migrate.Apply(context.Background(), a.db, schema); err != nil {
34 return errHomeIdentity
35 }
36 sess, err := sessions.New(sessions.Config{DB: a.db.Writer(), Origin: cfg.Origin, SigninPath: "/signin", Logger: a.logger})
37 if err != nil || sess.CookieName() != a.auth.SessionCookie() {
38 return errHomeIdentity
39 }
40 read := func(r *http.Request) (*connect.Session, error) {
41 s, ok := sess.From(r)
42 if !ok {
43 return nil, nil
44 }
45 return &connect.Session{Subject: s.Subject, Method: s.Method, AuthTime: s.AuthTime, At: s.At}, nil
46 }
47 b, err := connect.New(connect.Config{DB: a.db.Writer(), Origin: cfg.Origin, Issuer: h.Issuer, ConsumerID: h.ConsumerID, Namespace: "oficina/memoria", SessionCookie: sess.CookieName(), Keys: h.Keys, DEK: h.DEK, ReadSession: read,
48 MintHomeSession: func(w http.ResponseWriter, r *http.Request, s connect.Session) error {
49 return sess.SignIn(w, r, sessions.Session{Subject: s.Subject, Method: s.Method, AuthTime: s.AuthTime})
50 },
51 LookupLocalSession: func(ctx context.Context, s connect.Session) (connect.Person, error) {
52 id, label, err := a.localHomeIdentity(ctx, sessions.Session{Subject: s.Subject, Method: s.Method, AuthTime: s.AuthTime, At: s.At})
53 return connect.Person{ID: id, Label: label}, err
54 },
55 LookupPerson: func(ctx context.Context, q linkstore.Queryer, id int64) (string, error) {
56 return a.lookupHomeLocalIdentity(ctx, q, id)
57 }, Render: a.renderHome,
58 })
59 if err != nil {
60 return errHomeIdentity
61 }
62 routes, err := connect.NewRoutes(b, connect.RoutesConfig{Namespace: "oficina/memoria", Origin: cfg.Origin, Issuer: h.Issuer, SessionCookie: sess.CookieName(), DEK: h.DEK, ReadSession: read, SignOut: sess.SignOut, LocalSignin: a.localSignin, Protect: csrf.Protect(cfg.Origin), Render: a.renderHome})
63 if err != nil {
64 return errHomeIdentity
65 }
66 a.home = &homeIntegration{bridge: b, routes: routes, sessions: sess}
67 a.directory = h.Directory
68 return nil
69}
70
71func (a *app) homeMiddleware(origin string) func(http.Handler) http.Handler {
72 return func(next http.Handler) http.Handler {
73 local := csrf.Protect(origin)(next)
74 return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
75 if a.home != nil {
76 a.home.routes.Middleware(next).ServeHTTP(w, r)
77 return
78 }
79 local.ServeHTTP(w, r)
80 })
81 }
82}
83
84// signin is GET /signin. With Home configured it is connect's handler,
85// which remembers return_to for the Home exchange and, for a live session,
86// skips straight to the destination. That skip reads the session cookie
87// and nothing else, so the ADMISSION CHECK RUNS HERE FIRST: somebody who
88// has lost access is signed out and told so, rather than bounced between
89// /signin and a page that refuses them (Astra on Docs #65 and Sheets #39).
90func (a *app) signin(w http.ResponseWriter, r *http.Request) {
91 if a.home != nil {
92 if _, st := a.admissionOf(r); st == removed {
93 a.offboard(w, r)
94 return
95 }
96 a.home.sessions.Middleware(http.HandlerFunc(a.home.routes.Signin)).ServeHTTP(w, r)
97 return
98 }
99 a.localSignin(w, r)
100}
101
102func (a *app) homeAccount(w http.ResponseWriter, r *http.Request) {
103 s, ok := a.home.sessions.From(r)
104 if ok {
105 if _, fresh := connect.FreshLocal(connect.Session{Subject: s.Subject, Method: s.Method, AuthTime: s.AuthTime, At: s.At}, time.Now()); fresh {
106 if _, _, err := a.ensureHomeLocalIdentity(r.Context(), s); err != nil {
107 a.renderHome(w, r, "failed", "", http.StatusForbidden)
108 return
109 }
110 }
111 }
112 a.home.routes.Account(w, r)
113}
114