Skip to main content

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.

source

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.

source

type Access

type Access string

Access is the memory authority stored with a project credential.

source

const ReadOnly, ReadWrite

const (
ReadOnly Access = "read_only"
ReadWrite Access = "read_write"
)

source

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.

source

const KindProject, KindOperator

const (
KindProject Kind = "project"
KindOperator Kind = "operator"
)

source

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
}

source

type Store

type Store struct {
registry *pgxpool.Pool
schema string
}

Store resolves and issues credentials against the registry schema.

source

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.

source

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.

source

method Store.Resolve

func (s *Store) Resolve(ctx context.Context, token string) (Grant, error)

Resolve turns a presented token into a grant.

source

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.

source

method Store.resolve

func (s *Store) resolve(ctx context.Context, token string) (Grant, Kind, error)

source

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.

source

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.

source

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.

source

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.

source

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.

source

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.

source