add token type with permission checks and composable matchers
This commit is contained in:
parent
5288df0b9e
commit
8b5fcc87c3
4 changed files with 499 additions and 0 deletions
114
token.go
Normal file
114
token.go
Normal file
|
|
@ -0,0 +1,114 @@
|
|||
package ficha
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"time"
|
||||
)
|
||||
|
||||
// Token is a validated, decrypted token. It exposes the consumer-defined
|
||||
// permissions and data, and provides methods for permission checks.
|
||||
//
|
||||
// Tokens are returned by Issuer.Validate. Consumers do not construct them
|
||||
// directly. A Token is read-only — its methods do not mutate state.
|
||||
type Token struct {
|
||||
id string
|
||||
issuedAt time.Time
|
||||
expiresAt time.Time
|
||||
permissions []string
|
||||
data json.RawMessage
|
||||
}
|
||||
|
||||
// newToken builds a Token from a decoded payload. Package-private —
|
||||
// only the Issuer constructs Tokens, after successful validation.
|
||||
func newToken(p payload) *Token {
|
||||
return &Token{
|
||||
id: p.ID,
|
||||
issuedAt: time.Unix(p.Iat, 0),
|
||||
expiresAt: time.Unix(p.Exp, 0),
|
||||
permissions: p.Permissions,
|
||||
data: p.Data,
|
||||
}
|
||||
}
|
||||
|
||||
// ID returns the unique token identifier (used by the revocation store).
|
||||
func (t *Token) ID() string { return t.id }
|
||||
|
||||
// IssuedAt returns when the token was issued.
|
||||
func (t *Token) IssuedAt() time.Time { return t.issuedAt }
|
||||
|
||||
// ExpiresAt returns when the token expires.
|
||||
func (t *Token) ExpiresAt() time.Time { return t.expiresAt }
|
||||
|
||||
// Permissions returns a copy of the token's permission strings.
|
||||
// The returned slice is safe to retain and modify.
|
||||
func (t *Token) Permissions() []string {
|
||||
out := make([]string, len(t.permissions))
|
||||
copy(out, t.permissions)
|
||||
return out
|
||||
}
|
||||
|
||||
// UnmarshalData decodes the consumer-defined data blob into v.
|
||||
// Returns nil with v unchanged if the token has no data.
|
||||
func (t *Token) UnmarshalData(v any) error {
|
||||
if len(t.data) == 0 {
|
||||
return nil
|
||||
}
|
||||
return json.Unmarshal(t.data, v)
|
||||
}
|
||||
|
||||
// Has reports whether the token holds the given permission (exact match).
|
||||
func (t *Token) Has(perm string) bool {
|
||||
for _, p := range t.permissions {
|
||||
if p == perm {
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
// HasAll reports whether the token holds every given permission.
|
||||
// Returns true when called with no arguments (vacuous truth). For a
|
||||
// fail-closed variant that returns false on empty input, see RequiresAll.
|
||||
func (t *Token) HasAll(perms ...string) bool {
|
||||
for _, p := range perms {
|
||||
if !t.Has(p) {
|
||||
return false
|
||||
}
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
// RequiresAll reports whether the token holds every given permission,
|
||||
// and requires that at least one permission be specified. Returns false
|
||||
// on empty input — use this when an empty permission list indicates a
|
||||
// programmer error rather than a public/no-auth route.
|
||||
//
|
||||
// For the vacuous-truth variant (empty input → true), use HasAll.
|
||||
func (t *Token) RequiresAll(perms ...string) bool {
|
||||
if len(perms) == 0 {
|
||||
return false
|
||||
}
|
||||
return t.HasAll(perms...)
|
||||
}
|
||||
|
||||
// HasAny reports whether the token holds at least one of the given permissions.
|
||||
// Returns false when called with no arguments.
|
||||
func (t *Token) HasAny(perms ...string) bool {
|
||||
for _, p := range perms {
|
||||
if t.Has(p) {
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
// HasNone reports whether the token holds none of the given permissions.
|
||||
// Returns true when called with no arguments.
|
||||
func (t *Token) HasNone(perms ...string) bool {
|
||||
for _, p := range perms {
|
||||
if t.Has(p) {
|
||||
return false
|
||||
}
|
||||
}
|
||||
return true
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue