adminapitoken

package
v4.2.0 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Oct 11, 2026 License: MIT Imports: 17 Imported by: 0

Documentation

Overview

Package adminapitoken issues and verifies administrators' personal access tokens: long-lived credentials for automation on the administrator APIs (docs/reference/admin-api-tokens.md).

A token is "anixadm_" and 43 characters of URL-safe base64 for 32 random bytes. The kernel stores only its SHA-256 (v4_kernel_admin_api_token); the token is shown once, when it is created. A request names it in "Authorization: Bearer <token>" and nowhere else, and the lookup is by hash: an attacker cannot learn anything from the comparison without the token's preimage.

A token carries no rights of its own beyond a scope (read or admin). The owner's rights are read from v2_user on every use, so banning, demoting or deleting an administrator ends their tokens at once, and a token used while its owner is not an administrator is revoked for good (it does not come back when the owner is restored).

Index

Constants

View Source
const (
	// Prefix marks an admin API token. It lets secret scanners and humans
	// recognise one, and the middleware tell it from a JWT.
	Prefix = "anixadm_"

	// TokenLength is the length of every token.
	TokenLength = len(Prefix) + encodedLength
	// MaxNameLength bounds a token's name, counted in characters (runes), as the
	// web form counts them and as the column (varchar 100) does.
	MaxNameLength = 100
	// MaxActivePerUser bounds the unrevoked, unexpired tokens of one owner.
	MaxActivePerUser = 25
	// MaxLifetime bounds an expiry that is set; no expiry stays allowed.
	MaxLifetime = 2 * 365 * 24 * time.Hour
	// ManagementPath is the prefix of the routes that manage tokens. They
	// need a signed-in session: no API token reaches them.
	ManagementPath = "/api/v4/kernel/api-tokens"
)
View Source
const (
	ContextKeyAuthMethod = "auth_method"
	ContextKeyTokenID    = "api_token_id"
	ContextKeyTokenScope = "api_token_scope"
	// AuthMethodAPIToken is the value of ContextKeyAuthMethod for a request
	// an admin API token authenticated.
	AuthMethodAPIToken = "api_token"
)

Request context keys the authentication middleware sets. They are set on the server side only; a client cannot send them.

View Source
const (
	AuditModule       = "admin_api_token"
	AuditActionCreate = "admin_api_token_create"
	// AuditActionCreateDenied is a creation refused for its re-authentication.
	AuditActionCreateDenied = "admin_api_token_create_denied"
	AuditActionRevoke       = "admin_api_token_revoke"
	AuditActionUse          = "admin_api_token_use"
	AuditActionUseDenied    = "admin_api_token_use_denied"
)

Audit entries (v2_operation_log). Entries carry a token's id, name, scope, hint and expiry, never the token or its hash.

Variables

View Source
var (
	// ErrInvalid covers a malformed, unknown or unusable token.
	ErrInvalid = errors.New("invalid admin API token")
	// ErrRevoked is a revoked token.
	ErrRevoked = errors.New("admin API token revoked")
	// ErrExpired is a token past its expiry.
	ErrExpired = errors.New("admin API token expired")
	// ErrOwnerDenied is a token whose owner is banned, demoted or deleted.
	ErrOwnerDenied = errors.New("admin API token owner is not an active administrator")
	// ErrScopeDenied is a request the token's scope does not allow.
	ErrScopeDenied = errors.New("admin API token scope does not allow this request")
	// ErrInteractiveOnly is a request that needs a signed-in session.
	ErrInteractiveOnly = errors.New("this request needs a signed-in session, not an API token")

	// ErrNotFound is a token that does not exist or is not the caller's.
	ErrNotFound = errors.New("admin API token not found")
	// ErrInvalidRequest wraps every refused creation.
	ErrInvalidRequest = errors.New("invalid admin API token request")
	// ErrTooMany is an owner at MaxActivePerUser.
	ErrTooMany = errors.New("too many active admin API tokens")
	// ErrOwnerNotAdmin is a creation for an account that is not an active
	// administrator.
	ErrOwnerNotAdmin = errors.New("only an active administrator may hold admin API tokens")
)

Why an authentication or a use was refused.

Functions

func Generate

func Generate() (token, hash, hint string, err error)

Generate returns a new token, its stored hash and its hint.

func Hash

func Hash(token string) string

Hash is the stored form of a token: its hex SHA-256.

func LooksLikeToken

func LooksLikeToken(s string) bool

LooksLikeToken reports whether s starts like a token, however it is shaped; the middleware refuses these in a URL.

func ValidScope

func ValidScope(scope string) bool

ValidScope reports whether scope is one the kernel knows.

func WellFormed

func WellFormed(s string) bool

WellFormed reports whether s has the shape of a token, so a request that cannot be one is refused without a lookup.

Types

type Actor

type Actor struct {
	UserID uint
	Name   string
	IP     string
	// Super lets the actor revoke other administrators' tokens.
	Super bool
}

Actor is the administrator who revokes a token.

type CreateInput

type CreateInput struct {
	UserID uint
	Name   string
	Scope  string
	// ExpiresAt ends the token; nil never (allowed, not advised).
	ExpiresAt *time.Time
	// Actor and IP describe the caller in the audit entry.
	Actor string
	IP    string
}

CreateInput describes a new token.

type Denial

type Denial struct {
	Err     error
	TokenID string
	UserID  uint
	// Detail is a short safe reason for the audit entry.
	Detail string
}

Denial is a refused authentication: Err is one of the Err values above, the other fields say which token and owner it concerned when they are known.

func (*Denial) Error

func (d *Denial) Error() string

func (*Denial) Unwrap

func (d *Denial) Unwrap() error

type Event

type Event struct {
	TokenID string
	UserID  uint
	Actor   string
	IP      string
	Method  string
	Path    string
	// Reason is a short safe reason: a Denial's Detail or the error.
	Reason string
	Status int
}

Event is a request an API token made or was refused.

type ListFilter

type ListFilter struct {
	// UserID limits the list to one owner; nil lists every owner's tokens.
	UserID *uint
	// IncludeInactive also lists revoked and expired tokens.
	IncludeInactive bool
}

ListFilter selects tokens.

type Principal

type Principal struct {
	TokenID string
	UserID  uint
	Email   string
	Scope   string
	Name    string
}

Principal is an authenticated token and its owner.

type Service

type Service struct {
	// contains filtered or unexported fields
}

Service issues, lists, revokes and authenticates admin API tokens.

func New

func New(db *gorm.DB) *Service

New returns a service over db.

func (*Service) Authenticate

func (s *Service) Authenticate(ctx context.Context, presented, ip string) (*Principal, error)

Authenticate resolves a presented token to its owner and scope. A refusal is a *Denial (use errors.Is with the Err values). ip is recorded as the token's last use, at most every few minutes.

func (*Service) Create

func (s *Service) Create(ctx context.Context, input CreateInput) (string, model.AdminAPIToken, error)

Create issues a token for an active administrator and returns it: the only time it exists in clear. The audit entry is written with the row.

func (*Service) List

func (s *Service) List(ctx context.Context, filter ListFilter) ([]model.AdminAPIToken, error)

List returns tokens, newest first. A token's secret is never in a row.

func (*Service) RecordCreateDenied

func (s *Service) RecordCreateDenied(ctx context.Context, event Event)

RecordCreateDenied audits a creation refused because the re-authentication failed or was missing, at most once a minute per administrator. The entry carries no password and no code.

func (*Service) RecordDenied

func (s *Service) RecordDenied(ctx context.Context, event Event)

RecordDenied audits a refused use: an unusable token (Reason says why), a scope the request is outside of, or a request that needs a session. The same subject is audited at most once a minute, so a script that keeps failing cannot fill the log; the log line always says which token and owner, never the token.

func (*Service) RecordUse

func (s *Service) RecordUse(ctx context.Context, event Event)

RecordUse audits a write an API token made, so the audit log can tell a token's requests from the owner's own. The audit log of the administrator APIs records the request itself; this entry names the token.

func (*Service) Revoke

func (s *Service) Revoke(ctx context.Context, id string, actor Actor) (model.AdminAPIToken, bool, error)

Revoke ends a token. An administrator revokes their own; a super administrator revokes anyone's. A token that is not the actor's, or does not exist, is ErrNotFound. Revoking a revoked token changes nothing and reports changed false.

func (*Service) WithClock

func (s *Service) WithClock(now func() time.Time) *Service

WithClock replaces the clock; for tests.

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL