Documentation
¶
Overview ¶
Package auth implements the R5 RBAC identity layer: a pluggable Authenticator interface, an admin-managed users file, bearer-token issuance and verification, and the role/permission model the review service enforces.
The package is pure (no HTTP). Downstream HTTP handlers consume the Authenticator interface and the Permission checks defined here.
Index ¶
- Variables
- func DefaultUsersPath() (string, error)
- func GenerateToken() (string, error)
- func HashToken(plaintext string) (string, error)
- func VerifyToken(plaintext, hash string) (bool, error)
- type Authenticator
- type Identity
- type LocalUsersAuthenticator
- type Permission
- type Role
- type User
- type UsersFile
Constants ¶
This section is empty.
Variables ¶
var ( // ErrUserNotFound is returned when a lookup matches no user. ErrUserNotFound = errors.New("auth: user not found") // ErrUserExists is returned when adding a user whose email already exists. ErrUserExists = errors.New("auth: user already exists") )
var ErrHashMalformed = errors.New("auth: malformed hash")
ErrHashMalformed is returned by VerifyToken when the stored hash is not a well-formed argon2id PHC string.
var ErrTokenMalformed = errors.New("auth: malformed token")
ErrTokenMalformed is returned when a presented token is empty or does not carry the expected prefix.
var ErrUnauthenticated = errors.New("auth: unauthenticated")
ErrUnauthenticated is returned by Authenticate when the token is missing, malformed, or matches no user. Callers map this to a 401.
Functions ¶
func DefaultUsersPath ¶
DefaultUsersPath returns ~/.config/da/review/users.yaml, honoring XDG_CONFIG_HOME first (-> $XDG_CONFIG_HOME/da/review/users.yaml) before falling back to ~/.config/da/review/users.yaml.
Secrets never live in AGENTS_HOME (the git-synced config tree pushed by `da sync`); auth state is per-host local and resolves only into the local-secrets home, alongside ~/.config/da/credentials.json.
func GenerateToken ¶
GenerateToken returns a fresh cryptographically-random plaintext token. The plaintext is shown to the operator exactly once at issuance; only its hash is persisted (design D5.3, OQ1 print-once).
func HashToken ¶
HashToken returns an argon2id PHC-encoded hash of a plaintext token, suitable for storage in the users file. A fresh random salt is generated per call, so hashing the same token twice yields distinct strings.
func VerifyToken ¶
VerifyToken reports whether the plaintext matches the stored argon2id hash. A mismatch (or a malformed token) returns false with no error; an error is returned only when the stored hash itself is unusable.
Types ¶
type Authenticator ¶
type Authenticator interface {
// Authenticate resolves a plaintext bearer token to an Identity. It returns
// ErrUnauthenticated when the token does not correspond to a known user. Any
// other error indicates an operational failure (e.g. unreadable backing
// store) and should not be conflated with a 401.
Authenticate(token string) (Identity, error)
}
Authenticator resolves a presented bearer token to an Identity. It is the pluggable seam that lets an OIDC backend replace the local users file later (design D5.3) without changing handler code.
type Identity ¶
Identity is the resolved principal behind a presented token. It is what an Authenticator returns on success and what downstream handlers consult for authorization decisions.
func (Identity) Can ¶
func (id Identity) Can(p Permission) bool
Can reports whether the identity's role grants the given permission.
type LocalUsersAuthenticator ¶
type LocalUsersAuthenticator struct {
// contains filtered or unexported fields
}
LocalUsersAuthenticator authenticates bearer tokens against a users file on disk. The file is reloaded on each call so out-of-band CLI edits (user add / remove) take effect without a service restart.
func NewLocalUsersAuthenticator ¶
func NewLocalUsersAuthenticator(path string) *LocalUsersAuthenticator
NewLocalUsersAuthenticator constructs an authenticator backed by the users file at path.
func (*LocalUsersAuthenticator) Authenticate ¶
func (a *LocalUsersAuthenticator) Authenticate(token string) (Identity, error)
Authenticate implements Authenticator. It reloads the users file, then scans for the user whose stored hash matches the presented token. Because the plaintext token does not encode the owning user, every candidate hash is compared; a malformed or unmatched token yields ErrUnauthenticated.
type Permission ¶
type Permission string
Permission is a capability the review service gates on. Permissions are derived from roles via RolePermissions; handlers check a permission rather than a role so the role→capability mapping stays in one place.
const ( // PermReadLabels allows fetching labels. PermReadLabels Permission = "labels:read" // PermWriteLabels allows submitting and editing labels. PermWriteLabels Permission = "labels:write" // PermManageUsers allows creating, listing, and removing users. PermManageUsers Permission = "users:manage" // PermAdminLabels allows editing any reviewer's label (admin correction). PermAdminLabels Permission = "labels:admin" // PermReadAudit allows reading and verifying the audit log. PermReadAudit Permission = "audit:read" )
type Role ¶
type Role string
Role is one of the three single-tenant roles defined by R5 design D5.3.
const ( // RoleReviewer can read and submit/edit their own labels. RoleReviewer Role = "reviewer" // RoleAdmin can do everything a reviewer can plus manage users, edit any // label, and administer the audit log. RoleAdmin Role = "admin" // RoleReadonly can read labels but cannot mutate anything. RoleReadonly Role = "readonly" )
func (Role) Can ¶
func (r Role) Can(p Permission) bool
Can reports whether the role grants the given permission.
func (Role) Permissions ¶
func (r Role) Permissions() []Permission
Permissions returns the permission set granted to the role. The returned slice is a copy; callers may not mutate the canonical table.
type User ¶
type User struct {
Email string `yaml:"email"`
Role Role `yaml:"role"`
TokenHash string `yaml:"token_hash"`
CreatedAt string `yaml:"created_at"`
}
User is one entry in the admin-managed users file. The token plaintext is never persisted — only TokenHash (argon2id) is stored.
type UsersFile ¶
UsersFile is the on-disk shape of ~/.config/da/review/users.yaml.
func LoadUsersFile ¶
LoadUsersFile reads and parses a users file. A missing file is not an error: it returns an empty, current-schema UsersFile so first-run callers can add the first user without a separate init step.
func (*UsersFile) AddUser ¶
AddUser issues a fresh token for a new user, appends the user (storing only the token hash), and returns the issued plaintext token. The caller is responsible for persisting via Save and for displaying the plaintext exactly once. ErrUserExists is returned if the email is already present.
func (*UsersFile) Find ¶
Find returns the user with the given email (case-insensitive) and true, or the zero value and false.
func (*UsersFile) RemoveUser ¶
RemoveUser deletes the user with the given email (case-insensitive). It returns ErrUserNotFound if no such user exists.