Documentation
¶
Overview ¶
Package plans is a lock's store of plans awaiting a decision: one file per plan in the lock's volume (0600 in a 0700 directory), surviving restarts. A plan is bound to the peer UID that made it, the requesting agent's token, the policy fingerprint and the plan hash; it runs once.
The store keeps the product's plan as an opaque body: the product recomputes the plan hash from it before running (Check), and runs nothing whose binding moved.
One Store owns a store directory: Open locks it (a second Open fails with ErrLocked until Close), so that a decision nonce is accepted once whatever the number of consoles on a volume. A Store is safe for concurrent use.
Index ¶
- Constants
- Variables
- func HashToken(token string) string
- type Binding
- type Caller
- type Option
- type Plan
- type State
- type Store
- func (s *Store) Begin(id string, c Caller, nonce string) error
- func (s *Store) ByTicket(ticketID string, c Caller) (*Plan, error)
- func (s *Store) Check(id string, c Caller, policyFP, planHash string) error
- func (s *Store) Close() error
- func (s *Store) Create(b Binding, body json.RawMessage, ttl time.Duration) (*Plan, error)
- func (s *Store) End(id string, c Caller, st State, reason string) error
- func (s *Store) FindPending(c Caller, planHash string) (*Plan, bool)
- func (s *Store) Finish(id string, c Caller, ok bool, reason string) error
- func (s *Store) Get(id string, c Caller) (*Plan, error)
- func (s *Store) MarkPending(id string, c Caller, t *ticket.Ticket) error
- func (s *Store) Pending() []*Plan
- func (s *Store) Prune() (int, error)
- func (s *Store) WithdrawAll(reason string) ([]*Plan, error)
Constants ¶
const ( // MaxPlans bounds the live (not final) plans of a store. MaxPlans = 1000 // MaxBody bounds a plan's body. MaxBody = 1 << 20 // Retention keeps a final plan this long before Prune removes it. Retention = 24 * time.Hour )
Defaults.
Variables ¶
var ( // ErrNoSuchPlan: unknown, pruned or expired plan id. ErrNoSuchPlan = errors.New("plans: no such plan") // ErrWrongPeer: the plan was made by another peer UID. ErrWrongPeer = errors.New("plans: plan made by another peer") // ErrWrongRequester: the plan was made by another agent (token). ErrWrongRequester = errors.New("plans: plan made by another agent") // ErrStale: the plan hash or policy fingerprint moved since planning. ErrStale = errors.New("plans: plan is stale") // ErrUsed: the plan already ran, or is in a final state. ErrUsed = errors.New("plans: plan already used") // ErrReplay: the decision nonce was consumed before. ErrReplay = errors.New("plans: decision nonce already consumed") // ErrState: the transition is not allowed from the plan's state. ErrState = errors.New("plans: transition not allowed") // ErrFull: the store holds MaxPlans live plans. ErrFull = errors.New("plans: too many plans") // ErrUnsafe: the store directory or a file in it has unsafe // permissions; nothing is loaded. ErrUnsafe = errors.New("plans: unsafe permissions") // ErrLocked: another Store (another console on the same volume) has // the directory open. ErrLocked = errors.New("plans: the store is open in another process") )
Functions ¶
Types ¶
type Binding ¶
type Binding struct {
UID int `json:"uid"`
// Agent is the requesting agent's label (display only).
Agent string `json:"agent"`
// TokenHash is HashToken of the agent's token ("" without one).
TokenHash string `json:"token_hash"`
PolicyFP string `json:"policy_fp"`
PlanHash string `json:"plan_hash"`
}
Binding is what a plan is bound to.
type Caller ¶
type Caller struct {
UID int
// Token is the agent's lea token (LEA_AGENT_TOKEN), "" without lea.
Token string
}
Caller is who presents a plan id.
type Plan ¶
type Plan struct {
ID string `json:"id"`
State State `json:"state"`
Binding Binding `json:"binding"`
// Ticket is the hub ticket id, once pending, and SignedTicket the
// ticket as the lock signed and pushed it: a decision is matched
// against it (ticket.Decision.Match), never against one rebuilt.
Ticket string `json:"ticket,omitempty"`
SignedTicket json.RawMessage `json:"signed_ticket,omitempty"`
// Nonce is the decision nonce consumed when the plan began executing.
Nonce string `json:"nonce,omitempty"`
// Reason says why the plan reached its final state (denial reason,
// failure, what went stale).
Reason string `json:"reason,omitempty"`
Created time.Time `json:"created"`
Updated time.Time `json:"updated"`
Expires *time.Time `json:"expires,omitempty"`
// Body is the product's plan, opaque to the store.
Body json.RawMessage `json:"body"`
}
Plan is one stored plan.
type State ¶
type State string
State of a plan.
const ( // Planned: made, never run. Planned State = "planned" // Pending: run asked, a ticket awaits the human. Pending State = "pending" // Executing: a decision was accepted and consumed; the plan is running. Executing State = "executing" Executed State = "executed" Failed State = "failed" // Stale: the binding moved (plan hash, policy) before it ran. Stale State = "stale" Denied State = "denied" Withdrawn State = "withdrawn" Expired State = "expired" )
type Store ¶
type Store struct {
// contains filtered or unexported fields
}
Store is a directory of plans.
func Open ¶
Open opens or creates the store in dir (created 0700). It refuses a directory, plan or nonce file readable or writable by group or others, and loads every plan.
func (*Store) Begin ¶
Begin starts executing plan id under the decision nonce: single use. The nonce is recorded durably before the plan changes state; a nonce seen before is ErrReplay, a plan not Planned or Pending is ErrUsed. Without a hub (standalone), nonce is "".
func (*Store) Check ¶
Check compares a plan's binding with what the product recomputed just before running it: a different policy fingerprint or plan hash marks the plan Stale and returns ErrStale.
func (*Store) Close ¶
Close releases the store directory for another Open. The Store must not be used after it.
func (*Store) Create ¶
Create stores a new Planned plan bound to b, with the product's body, expiring after ttl unless run (0: never).
func (*Store) End ¶
End moves a plan that has not begun to a final state: Denied, Withdrawn, Stale or Expired, with reason. It is how the lock records a decision other than an approval, or a ticket withdrawn.
func (*Store) FindPending ¶
FindPending returns the pending plan of caller c (peer UID and agent token) with planHash, if any: a second run of the same plan reuses its ticket instead of opening a new one.
func (*Store) Get ¶
Get returns plan id as caller c may see it: same peer UID, same agent token. An expired plan is ErrNoSuchPlan.
func (*Store) MarkPending ¶
MarkPending records that plan id waits on t, the ticket the lock signed for it, until t.Expires (nil: no expiry). t must carry the plan's hash and policy fingerprint.