- pact.MCPSubjectChanged and pier.Event gain TokenID and ClientID; a narrowed revoked event closes only matching sessions and stateless servers - cabana fires the admin token revoke with the token row id - towel.WithClientIP/ClientIP; pier puts the address (surf applies http.trusted_proxies) on the resolve context and ResolveBackendToken logs it
68 lines
2.8 KiB
Go
68 lines
2.8 KiB
Go
package pier
|
|
|
|
import (
|
|
"context"
|
|
"encoding/json"
|
|
"time"
|
|
)
|
|
|
|
// Record is one stored session: the binding key of the identity that opened
|
|
// it, the JSON of the SDK's ServerSessionState once initialised (nil until
|
|
// then) and the creation time.
|
|
type Record struct {
|
|
Binding string `json:"binding"`
|
|
State json.RawMessage `json:"state,omitempty"`
|
|
Created time.Time `json:"created"`
|
|
}
|
|
|
|
// Event is what instances publish to each other: a session ended by DELETE
|
|
// (EventEnded, with SessionID) or a subject change (the pact
|
|
// MCPSubjectChanged kinds, with Guard and UserID). Instance names the
|
|
// publisher so it can skip its own echo when it has already acted.
|
|
type Event struct {
|
|
Kind string `json:"kind"`
|
|
Guard string `json:"guard,omitempty"`
|
|
UserID uint `json:"user_id,omitempty"`
|
|
SessionID string `json:"session_id,omitempty"`
|
|
Instance string `json:"instance,omitempty"`
|
|
TokenID uint `json:"token_id,omitempty"`
|
|
ClientID string `json:"client_id,omitempty"`
|
|
}
|
|
|
|
// EventEnded is the kind of a session-ended event.
|
|
const EventEnded = "ended"
|
|
|
|
// Store is the session registry and the event channel shared by every
|
|
// instance of the application: memory for one instance (and tests), Redis
|
|
// for several. Put writes a record with a TTL, Touch slides it, Count
|
|
// answers the live sessions of one binding, Publish and Subscribe carry
|
|
// Events.
|
|
type Store interface {
|
|
Put(ctx context.Context, id string, rec Record, ttl time.Duration) error
|
|
Get(ctx context.Context, id string) (Record, bool, error)
|
|
Touch(ctx context.Context, id string, ttl time.Duration) error
|
|
Delete(ctx context.Context, id string) error
|
|
Count(ctx context.Context, binding string) (int, error)
|
|
Publish(ctx context.Context, ev Event) error
|
|
Subscribe(ctx context.Context, fn func(Event)) error
|
|
}
|
|
|
|
// updater is implemented by the stores pier ships: Update rewrites an
|
|
// existing record and never creates one (Redis SET XX), answering false when
|
|
// the record is gone, so a request that finishes after its session ended
|
|
// (DELETE, revocation, deletion, expiry) cannot write the record back.
|
|
type updater interface {
|
|
Update(ctx context.Context, id string, rec Record, ttl time.Duration) (bool, error)
|
|
}
|
|
|
|
// reserver is implemented by the stores pier ships: Reserve is the atomic
|
|
// form of Count then Put (A-WR-03). It writes rec for id with the TTL and
|
|
// adds id to the binding's sessions only when the binding holds fewer than
|
|
// max live sessions, answering false (nothing written) at the cap, so
|
|
// concurrent initializes of one binding cannot exceed it. Release drops a
|
|
// reservation whose session never opened (a failed connect or initialize).
|
|
type reserver interface {
|
|
Reserve(ctx context.Context, binding, id string, rec Record, max int, ttl time.Duration) (bool, error)
|
|
Release(ctx context.Context, binding, id string) error
|
|
}
|