package models import ( "slices" "time" "git.golem15.com/golem15/summercms/modules/lagoon" ) // APIToken is a personal or OAuth access token owned by golem15.user. The // clear-text secret is returned once and never persisted. type APIToken struct { ID uint `gorm:"column:id;primaryKey"` UserID uint `gorm:"column:user_id"` Name *string `gorm:"column:name"` TokenHash string `gorm:"column:token_hash" json:"-"` Scopes lagoon.Jsonable[[]string] `gorm:"column:scopes"` CollectionIDs lagoon.Jsonable[[]uint] `gorm:"column:collection_ids"` ExpiresAt *time.Time `gorm:"column:expires_at"` RevokedAt *time.Time `gorm:"column:revoked_at"` LastUsedAt *time.Time `gorm:"column:last_used_at"` LastUsedIP *string `gorm:"column:last_used_ip"` OAuthClientID *string `gorm:"column:oauth_client_id"` CreatedAt time.Time `gorm:"column:created_at"` UpdatedAt time.Time `gorm:"column:updated_at"` } func (APIToken) TableName() string { return "user_api_tokens" } func (APIToken) Fillable() []string { return []string{ "user_id", "name", "token_hash", "scopes", "collection_ids", "expires_at", "revoked_at", "last_used_at", "last_used_ip", "oauth_client_id", } } func (APIToken) Hidden() []string { return []string{"token_hash"} } // HasScope reports whether this token explicitly carries scope. func (t APIToken) HasScope(scope string) bool { return slices.Contains(t.Scopes.Get(), scope) } // IsUsableAt is false after revocation or expiry. func (t APIToken) IsUsableAt(now time.Time) bool { return t.RevokedAt == nil && (t.ExpiresAt == nil || t.ExpiresAt.After(now)) } // IsUsable checks usability at the current time. func (t APIToken) IsUsable() bool { return t.IsUsableAt(time.Now()) } func init() { Register(APIToken{}) }