oficina / docs Public

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

Plain git — no account needed to clone.

Download

Download this file

1package docs
2
3import (
4 "context"
5 "net/http"
6 "time"
7
8 "amadan.net/oficina/home/roster"
9 "amadan.net/rastrillo/rastrillo/sessions"
10)
11
12type teamStreamGuard struct {
13 store *Store
14 access *requestAccess
15 request *http.Request
16 docID int64
17 deadline time.Time
18}
19
20func (g *teamStreamGuard) remaining() time.Duration {
21 if g.access.uncertain {
22 return 0
23 }
24 if g.access.decision.State == roster.Member {
25 if g.deadline.IsZero() {
26 return 0
27 }
28 return min(g.access.decision.Lease.Remaining(), time.Until(g.deadline))
29 }
30 return defaultStreamWriteDeadline
31}
32
33// A stream never renews its lease. Reconnection obtains fresh admission, so a
34// blocked cache publisher cannot hold an expired stream open during refresh.
35func (g *teamStreamGuard) check() (time.Duration, bool) {
36 t := g.store.team
37 remaining := g.remaining()
38 if t == nil || t.bridge == nil || t.readSession == nil || remaining <= 0 {
39 return 0, false
40 }
41 limit := min(remaining, defaultStreamWriteDeadline)
42 ctx, cancel := context.WithTimeout(g.request.Context(), limit)
43 defer cancel()
44 if _, present, ok := homeCookie(g.request, t.bridge.sessionCookie); !ok || !present {
45 return 0, false
46 }
47 session, ok := t.readSession(ctx, g.request)
48 if !ok || ctx.Err() != nil {
49 return 0, false
50 }
51 request := sessions.WithSession(g.request.WithContext(ctx), session)
52 id, e := t.bridge.resolver.resolve(request)
53 if e != nil || !sameActor(id.Actor, g.access.actor) || id.HomeSubject != g.access.subject {
54 return 0, false
55 }
56 if e = g.store.checkPermission(ctx, g.store.db.WithContext(ctx), id.Actor, g.docID, permissionRead); e != nil {
57 return 0, false
58 }
59 remaining = g.remaining()
60 if ctx.Err() != nil || remaining <= 0 {
61 return 0, false
62 }
63 return min(remaining, defaultStreamWriteDeadline), true
64}
65