| 1 | package docs |
| 2 | |
| 3 | import ( |
| 4 | "context" |
| 5 | "net/http" |
| 6 | "time" |
| 7 | |
| 8 | "amadan.net/oficina/home/roster" |
| 9 | "amadan.net/rastrillo/rastrillo/sessions" |
| 10 | ) |
| 11 | |
| 12 | type teamStreamGuard struct { |
| 13 | store *Store |
| 14 | access *requestAccess |
| 15 | request *http.Request |
| 16 | docID int64 |
| 17 | deadline time.Time |
| 18 | } |
| 19 | |
| 20 | func (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. |
| 35 | func (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 | |