Documentation
¶
Overview ¶
Package apikeyissue is the one place a database-managed API key comes into being (#1759).
Two surfaces issue keys: the admin route, where an operator names a key and may bind it to somebody's account, and the portal route, where a person issues one for themselves. What they share is everything that decides whether the key will work -- that a bound account resolves to a person the platform can speak for, that the name is free, that the key exists in the store before it authenticates anywhere, and that peer replicas are told. Forking that would mean one surface could hand out a key the other's rules would have refused.
What the surfaces keep for themselves is what they present: their own request shapes, their own refusal text and status codes, and their own naming (the portal composes a name scoped to its owner). This package raises typed refusals and never writes a response.
Index ¶
Constants ¶
const SelfIssuedPrefix = "user:"
SelfIssuedPrefix opens the name every key a person issued for themselves is stored under. Key names are unique across a deployment, so a person's chosen name is scoped to them; this is the one spelling of that scope, so the surface that composes such a name and the surface that must refuse to impersonate one cannot disagree about what it looks like.
Variables ¶
var ( // ErrNameTaken means a key already carries the name asked for. ErrNameTaken = errors.New("a key with that name already exists") // ErrUnknownPerson means the account a key was to be bound to is not one // the platform can speak for: no directory row, or nobody it has seen sign // in, so there is no subject to authenticate as and no roles to carry. ErrUnknownPerson = errors.New("the platform has no record of that person signing in") // ErrNoStore means the deployment has nowhere to put a key, so none can be // issued. A database-less deployment reaches this rather than minting a // value that would authenticate nowhere. ErrNoStore = errors.New("this deployment stores no api keys") // ErrReservedName means the name asked for is one the platform composes // for a key somebody issued for themselves. ErrReservedName = errors.New("that name belongs to the platform") )
Refusals an issuer raises. Each surface maps these to its own status code and wording, so the rule lives here and the phrasing lives with the reader.
Functions ¶
This section is empty.
Types ¶
type Issued ¶
type Issued struct {
Key string
Email string
Roles []string
// Person is the account the key resolved to, or nil for a service key. Its
// roles are what a bound key with no roles of its own will carry.
Person *auth.BoundPrincipal
}
Issued is the key handed back. Key is the only time the value exists outside the holder's keeping: the store has its hash and nothing else.
type Issuer ¶
type Issuer struct {
// Manager mints the value and holds the copy in memory.
Manager Manager
// Store is where a key comes into being: it authenticates once it is here.
Store Store
// Principals resolves the account a bound key names. Nil means this
// deployment cannot bind a key to anybody, and every bound request is
// refused with ErrUnknownPerson rather than issued unresolvable.
Principals auth.PrincipalSource
// Announce tells peer replicas a key was written, so their in-memory copy
// is refreshed. Optional: a key is live the moment it is stored, so this
// only shortens how long a peer's listing is stale.
Announce func()
}
Issuer issues keys against one deployment's key store.
type Manager ¶
type Manager interface {
GenerateKey(def auth.APIKey) (string, error)
SyncHashedKeys(ctx context.Context) error
}
Manager mints key values and refreshes the keys held in memory. Implemented by *auth.APIKeyAuthenticator.
type Request ¶
type Request struct {
// Name is the key's identity in the store, unique across the deployment.
Name string
// Description is what the key is for, as its holder wrote it.
Description string
// Email is the address a service key carries, already defaulted by the
// caller. It is ignored for a bound key, which always carries its
// person's address.
Email string
// UserEmail binds the key to a person's account. Empty issues the
// standalone service key a key has always been.
UserEmail string
// Roles is the key's own role set. A service key must carry one. A bound
// key with none follows the person it is bound to, on every request; a
// bound key with one carries that set and nothing else, which is not
// checked against what that person holds.
Roles []string
// ExpiresAt ends the key, or nil for a key that never expires.
ExpiresAt *time.Time
// CreatedBy records who issued it.
CreatedBy string
}
Request is a key to issue.
type Store ¶
type Store interface {
Create(ctx context.Context, def apikeystore.Definition) error
}
Store persists the key. Implemented by the platform's API key store.