internal/credential/credential.go
internal/credential · 270 lines · 19 declarations · source
This file carries the package documentation, rendered on the package page.
Declarations
const Prefix
const Prefix = "tsk_"
Prefix marks a Taisce token in a log, a secret scanner or a pasted snippet. It is stored in the clear alongside the digest so a person can tell two credentials apart in a list.
var ErrUnknown
var ErrUnknown = errors.New("credential does not resolve")
ErrUnknown is returned for a credential that does not resolve — absent, malformed, unknown or revoked, deliberately without distinguishing them.
The caller of this package turns it into one status. Telling an unauthenticated stranger which of those it was tells them whether a token they hold is real, which is the one fact they cannot otherwise get.
type Access
type Access string
Access is the memory authority stored with a project credential.
const ReadOnly, ReadWrite
const (
ReadOnly Access = "read_only"
ReadWrite Access = "read_write"
)
type Kind
type Kind string
Grant carries the authenticated project and memory access mode, never caller-supplied authority. Kind is which door a credential opens. A project credential opens one project's memory routes; an operator credential opens the management surface and no project's memory. Read from the registry at every request, never from the token.
const KindProject, KindOperator
const (
KindProject Kind = "project"
KindOperator Kind = "operator"
)
type Grant
type Grant struct {
Access Access
CredentialID string
Name string
// Project is the one project this credential reaches. A caller never names a project — the
// credential decides it — so this is not a set to be checked against a request but the answer
// to which project an operation acts on.
//
// Empty is not a valid resolved grant: Resolve refuses a credential with no project rather than
// returning one that reaches nothing, because a token that authenticates and then does nothing
// is a support case, not a security posture.
Project string
}
type Store
type Store struct {
registry *pgxpool.Pool
schema string
}
Store resolves and issues credentials against the registry schema.
func NewStore
func NewStore(registry *pgxpool.Pool, schema string) *Store
NewStore takes the REGISTRY pool. Handing it the memory pool would silently work — both reach the same database — and would defeat the grant that makes the boundary real, so the parameter is named for the only pool that is correct.
func Digest
func Digest(token string) []byte
Digest is the stored form of a token.
SHA-256 rather than a password hash. A token is 256 bits of random, so the guessing attack a slow hash defends against does not exist here; what a digest defends against is a read of the table that should never have happened, and it does that just as well while costing one hash on a path every request pays.
method Store.Resolve
func (s *Store) Resolve(ctx context.Context, token string) (Grant, error)
Resolve turns a presented token into a grant.
method Store.ResolveOperator
func (s *Store) ResolveOperator(ctx context.Context, token string) (Grant, error)
ResolveOperator is the management door: it opens for an operator credential and refuses every other token, a project credential included, with the one answer a stranger gets. The grant carries no project, because an operator acts on the instance and names a project in the request.
method Store.resolve
func (s *Store) resolve(ctx context.Context, token string) (Grant, Kind, error)
method Store.Issue
func (s *Store) Issue(ctx context.Context, name, project string) (string, Grant, error)
Issue mints a credential for one project and returns the token ONCE.
The token is not recoverable afterwards, by us or by anyone with the database: only its digest is stored. That is the property being bought, and it is why this returns the token rather than writing it anywhere.
method Store.IssueWithAccess
func (s *Store) IssueWithAccess(ctx context.Context, name, project string, access Access) (string, Grant, error)
IssueWithAccess creates an explicitly restricted project credential. Access is registry metadata; it is never taken from an HTTP request using that credential.
method Store.IssueOperator
func (s *Store) IssueOperator(ctx context.Context, name string) (string, Grant, error)
IssueOperator mints an operator credential and returns the token ONCE. It names no project: the management surface acts on the instance, and a request names the project it acts on.
type Listed
type Listed struct {
CredentialID string `json:"id"`
Name string `json:"name"`
Kind Kind `json:"kind"`
Project string `json:"project,omitempty"`
Access Access `json:"access"`
TokenPrefix string `json:"token_prefix"`
CreatedAt time.Time `json:"created_at"`
RevokedAt *time.Time `json:"revoked_at,omitempty"`
}
Listed is one credential as the registry describes it to an operator: never the token, never its digest.
method Store.List
func (s *Store) List(ctx context.Context, project string, limit int) ([]Listed, error)
List describes the credentials of one project, or every operator credential when project is empty, newest first, revoked ones included so a revocation is visible as one.
method Store.Revoke
func (s *Store) Revoke(ctx context.Context, credentialID string) error
Revoke stops a credential resolving, keeping the row so whatever recorded its use can still name it.