Documentation
¶
Overview ¶
Package startgrant evaluates the independent, least-privilege mayStart audience of ADR-0516. It is not a route guard: callers must also validate human authentication, publication, version and start form on the server.
Index ¶
- Constants
- Variables
- func Allowed(grants []Grant, applicationID, processID string, p Principal) bool
- type Account
- type Change
- type ChangeRequest
- type ChangeView
- type Directory
- type Grant
- type GrantView
- type MutationView
- type PreviewView
- type Principal
- type Service
- func (s *Service) HandleDelete(w http.ResponseWriter, r *http.Request)
- func (s *Service) HandleGet(w http.ResponseWriter, r *http.Request)
- func (s *Service) HandleHistory(w http.ResponseWriter, r *http.Request)
- func (s *Service) HandleList(w http.ResponseWriter, r *http.Request)
- func (s *Service) HandlePreview(w http.ResponseWriter, r *http.Request)
- func (s *Service) HandlePut(w http.ResponseWriter, r *http.Request)
- type Store
- func (s *Store) Allowed(applicationID, processID string, principal Principal) bool
- func (s *Store) Current(applicationID, processID string) (Change, bool)
- func (s *Store) ForApplication(applicationID string) []Change
- func (s *Store) GrantedTo(p Principal) []Grant
- func (s *Store) History(applicationID, processID string) []Change
- func (s *Store) Set(req ChangeRequest) (Change, error)
- func (s *Store) Uncertain() bool
- type Target
Constants ¶
const ( BlockApplicationMissing = "application-missing" BlockApplicationProtected = "application-protected" BlockNotDeployed = "not-deployed" BlockOtherApplication = "newest-version-in-other-application" BlockNotExecutable = "not-executable" BlockInactive = "inactive" BlockTriggerOnly = "trigger-only" BlockConstantsMissing = "constants-missing" )
Blocker codes say why a granted process would still not be offered for a start. They are stable strings so the Console and tests can read them.
const ( ActionGrant = "grant" ActionRevoke = "revoke" )
ActionGrant and ActionRevoke describe immutable audited audience changes.
const SchemaVersion = 1
SchemaVersion identifies the durable grant-document format.
Variables ¶
var ErrStaleRevision = errors.New("start grant revision changed")
ErrStaleRevision means another admin change won before this request.
var ErrUncertainWrite = errors.New("start grant store requires recovery after write error")
ErrUncertainWrite requires restart/recovery before any positive authorization.
Functions ¶
func Allowed ¶
Allowed evaluates an explicit grant for exactly one application/process pair. The caller supplies current group membership and a current durable grant snapshot; no cached positive decision may survive a persisted revocation. It deliberately does not infer permission from viewer/editor or operator.
Types ¶
type Account ¶
type Account struct {
// Disabled accounts cannot sign in, so a grant to one starts nothing.
Disabled bool
// SelfService reports whether the account holds a role the self-service start
// routes are declared for (user, or admin, which reaches every route). A grant
// is a second key beside that role, never a replacement for it.
SelfService bool
}
Account is what the directory says about a person a grant or a preview names.
type Change ¶
type Change struct {
SchemaVersion int `json:"schemaVersion"`
Revision uint64 `json:"revision"`
At int64 `json:"at"`
ActorID string `json:"actorId"`
Action string `json:"action"`
Grant Grant `json:"grant"`
}
Change is one immutable audit entry in a process-scoped grant document. Revision increases per application/process, not globally.
type ChangeRequest ¶
ChangeRequest is prepared by a future admin-only HTTP service. The actor and timestamp must come from the authenticated server context, not the client.
type ChangeView ¶
type ChangeView struct {
Revision uint64 `json:"revision"`
At int64 `json:"at"`
ActorID string `json:"actorId"`
Action string `json:"action"`
UserIDs []string `json:"userIds"`
GroupIDs []string `json:"groupIds"`
}
ChangeView is one audited change as the history route answers it.
type Directory ¶
type Directory struct {
User func(id string) (Account, bool, error)
Group func(id string) (bool, error)
GroupsOf func(userID string) ([]string, error)
}
Directory resolves the identities a grant names. Every function is called on the run loop only. Identities are looked up by id, never by a display or user name: a name can be changed or reused, an id cannot.
type Grant ¶
type Grant struct {
ApplicationID string `json:"applicationId"`
ProcessID string `json:"processId"`
UserIDs []string `json:"userIds,omitempty"`
GroupIDs []string `json:"groupIds,omitempty"`
}
Grant is a process-scoped audience configured independently of app sharing. Persistence and admin audit belong to the design-time sidecar, not the WAL.
type GrantView ¶
type GrantView struct {
ApplicationID string `json:"applicationId"`
ProcessID string `json:"processId"`
// Revision is the per-scope revision a change must name as expectedRevision;
// zero when nothing was ever granted for this scope.
Revision uint64 `json:"revision"`
Granted bool `json:"granted"`
UserIDs []string `json:"userIds"`
GroupIDs []string `json:"groupIds"`
ChangedAt int64 `json:"changedAt,omitempty"`
ChangedBy string `json:"changedBy,omitempty"`
}
GrantView is one scope's current state as the administration routes answer it.
type MutationView ¶
MutationView is a write's answer: the scope as it now stands, and whether this request changed it. Changed is false for a revoke of what is already revoked and for a grant of exactly the audience already granted — neither writes an audit entry, so a retried request does not pile up history.
type PreviewView ¶
type PreviewView struct {
ApplicationID string `json:"applicationId"`
ProcessID string `json:"processId"`
SubjectKind string `json:"subjectKind"`
SubjectID string `json:"subjectId"`
Revision uint64 `json:"revision"`
Granted bool `json:"granted"`
MatchedBy []string `json:"matchedBy"`
AccountIssues []string `json:"accountIssues"`
Startable bool `json:"startable"`
Blockers []string `json:"blockers"`
DefinitionKey uint64 `json:"definitionKey,omitempty"`
Version int32 `json:"version,omitempty"`
// WouldBeOffered is the conjunction the self-service list applies: granted, an
// account that can sign in and holds the role, and a process that is startable.
// A machine credential never qualifies, whatever it carries (ADR-0516).
WouldBeOffered bool `json:"wouldBeOffered"`
}
PreviewView answers whether one person or group would be offered the process.
type Principal ¶
Principal is the authenticated user's current identity and group membership. A zero UserID cannot authorize a start even when groups match.
type Service ¶
type Service struct {
// Limits are the installation's resource budgets. NewService sets them to
// [limits.Default]; the server overwrites them with its own once it has read the
// environment (ADR-0291). A grant body is one ordinary JSON request.
Limits limits.Limits
// contains filtered or unexported fields
}
Service serves the start-grant administration routes. Build it with NewService.
func NewService ¶
func NewService(loop *runloop.Loop, store *Store, dir Directory, resolve func(applicationID, processID string) (Target, error), authEnabled func() bool, now func() int64) *Service
NewService builds the administration service over the store. resolve and every function of dir read state the loop owns and are only called from inside its turn. authEnabled reports whether the server enforces authentication; it is read per request because the server settles it after constructing its services. now stamps an audit entry, in Unix seconds; the actor and the time are the server's, never the client's.
func (*Service) HandleDelete ¶
func (s *Service) HandleDelete(w http.ResponseWriter, r *http.Request)
HandleDelete revokes one scope's grant. A revocation takes effect for every authorization check after it is durable; a start authorized before it may still finish (ADR-0516's prospective ordering). Revoking needs no application or process to exist any more — taking a right away must always be possible — and revoking what is already revoked writes nothing.
func (*Service) HandleGet ¶
func (s *Service) HandleGet(w http.ResponseWriter, r *http.Request)
HandleGet answers one scope's current grant. A scope never granted answers revision 0 and an empty audience: deny is the default, not a missing record.
func (*Service) HandleHistory ¶
func (s *Service) HandleHistory(w http.ResponseWriter, r *http.Request)
HandleHistory answers every audited change of one scope, newest first.
func (*Service) HandleList ¶
func (s *Service) HandleList(w http.ResponseWriter, r *http.Request)
HandleList answers every scope of one application that was ever granted, revoked ones included, ordered by process id.
func (*Service) HandlePreview ¶
func (s *Service) HandlePreview(w http.ResponseWriter, r *http.Request)
HandlePreview answers whether one account (?userId=) or one group (?groupId=) would be offered the process now, and why not. It reads the account's group membership from the directory — the same source a session's groups are kept current from (ADR-0185) — and writes nothing.
func (*Service) HandlePut ¶
func (s *Service) HandlePut(w http.ResponseWriter, r *http.Request)
HandlePut issues or wholly replaces one scope's grant. The application must exist and not be a protected system application, the newest deployed version of the process must be filed under it, and every user and group id must name an account or group that exists now. All of it is checked in the same loop turn as the write, which is durable — written and fsynced — before the answer says so.
type Store ¶
type Store struct {
// contains filtered or unexported fields
}
Store owns durable process-start grants, with single-writer run-loop access. Never call its mutable methods concurrently or from the processor hot path.
func NewStore ¶
NewStore loads all scoped grant documents and validates their audit chains. A corrupt record fails startup instead of granting access from partial data.
func (*Store) Allowed ¶
Allowed checks only the exact last durable audience, never project viewer, editor, task assignment or operator privileges. Call it on the run-loop.
func (*Store) Current ¶
Current returns a defensive copy of the latest audited mutation. It is unavailable after an uncertain write until restart/recovery.
func (*Store) ForApplication ¶
ForApplication returns the latest audited change of every process scope filed under one application, revoked scopes included, ordered by process id. It is the administrator's overview; it grants nothing and is unavailable while the store is uncertain, for the reason Store.Current is.
func (*Store) GrantedTo ¶
GrantedTo returns every scope whose latest durable change grants the principal, directly or through one of the groups it carries now, ordered by application and process id. It is the self-service list's starting point and nothing more: the caller must still check the scope's application and process before offering it. Like every positive answer, it is empty while the store is uncertain.
func (*Store) Set ¶
func (s *Store) Set(req ChangeRequest) (Change, error)
Set atomically replaces the effective policy and its complete audit trail. Callers MUST first enforce admin authorization and validate the target application, BPMN process and audience against current server-side records. The store is not itself a route guard. A failed write poisons authorization until recovery because a failed fsync can have an unknown outcome.
type Target ¶
type Target struct {
ApplicationFound bool
ApplicationProtected bool
// ProcessInApplication reports that the newest deployed version of the process
// id is filed under this application. A grant is issued only then: a process id
// another application deployed last is not this application's to hand out.
ProcessInApplication bool
// DefinitionKey and Version name that newest deployed version, zero when the
// process id is not deployed.
DefinitionKey uint64
Version int32
// Blockers say why the newest version would not be offered for a self-service
// start; empty means it would be.
Blockers []string
}
Target is what the server knows, now, about one application/process pair.