Documentation
¶
Overview ¶
Package postgres provides the PostgreSQL-backed ulinzi.Store implementation (wave-1: opaque-session validation + permission resolution) over the "security" schema, using pgx.
Index ¶
- type Store
- func (s *Store) AddPasskey(ctx context.Context, p *ulinzi.AppUserPasskey) error
- func (s *Store) AppendChained(ctx context.Context, e ulinzi.AuditEvent, hashFn func(prevHash string) string) error
- func (s *Store) BumpSecurityStamp(ctx context.Context, userID uuid.UUID, newStamp string) error
- func (s *Store) CountRecentFailures(ctx context.Context, email string, since time.Time) (int, error)
- func (s *Store) CreateSession(ctx context.Context, rec *ulinzi.SessionRecord, loginCompletedAt time.Time) error
- func (s *Store) CreateUser(ctx context.Context, id uuid.UUID, ...) error
- func (s *Store) DeleteExpiredMfaCodes(ctx context.Context) (int64, error)
- func (s *Store) EnforceSessionCap(ctx context.Context, userID uuid.UUID, max int) error
- func (s *Store) GetActiveLockout(ctx context.Context, email string) (*ulinzi.ActiveLockout, error)
- func (s *Store) GetActiveSessionPolicy(ctx context.Context) (*ulinzi.SessionPolicy, error)
- func (s *Store) GetIdpConfigByProvider(ctx context.Context, provider string) (*ulinzi.IdpConfiguration, error)
- func (s *Store) GetLockoutPolicy(ctx context.Context) (*ulinzi.LockoutPolicy, error)
- func (s *Store) GetMfa(ctx context.Context, userID uuid.UUID) (*ulinzi.AppUserMfa, error)
- func (s *Store) GetPasskeyByCredentialID(ctx context.Context, credentialID string) (*ulinzi.AppUserPasskey, error)
- func (s *Store) GetPasswordPolicy(ctx context.Context) (*ulinzi.PasswordPolicy, error)
- func (s *Store) GetSessionByID(ctx context.Context, sessionID string) (*ulinzi.SessionRecord, error)
- func (s *Store) GetUserAuthByEmail(ctx context.Context, normalizedEmail string) (*ulinzi.UserAuth, error)
- func (s *Store) GetUserAuthByID(ctx context.Context, id uuid.UUID) (*ulinzi.UserAuth, error)
- func (s *Store) GetUserByID(ctx context.Context, id uuid.UUID) (*ulinzi.User, error)
- func (s *Store) GetUserIDByExternalLogin(ctx context.Context, provider ulinzi.IdentityProvider, providerKey string) (uuid.UUID, bool, error)
- func (s *Store) GetUserPermissions(ctx context.Context, userID uuid.UUID) ([]string, error)
- func (s *Store) GetUserRoles(ctx context.Context, userID uuid.UUID) ([]string, error)
- func (s *Store) InsertLockout(ctx context.Context, email string, userID *string, ...) error
- func (s *Store) LinkExternalLogin(ctx context.Context, login ulinzi.UserLogin) error
- func (s *Store) ListExternalLogins(ctx context.Context, userID uuid.UUID) ([]ulinzi.UserLogin, error)
- func (s *Store) ListIdpConfigs(ctx context.Context) ([]ulinzi.IdpConfiguration, error)
- func (s *Store) ListPasskeysByUserID(ctx context.Context, userID uuid.UUID) ([]ulinzi.AppUserPasskey, error)
- func (s *Store) RecordLoginAttempt(ctx context.Context, a ulinzi.LoginAttempt) error
- func (s *Store) RemovePasskeyByUserAndCredentialID(ctx context.Context, userID uuid.UUID, credentialID string) (bool, error)
- func (s *Store) RevokeSession(ctx context.Context, sessionID, reason string, at time.Time) error
- func (s *Store) TouchSession(ctx context.Context, id uuid.UUID, at time.Time) error
- func (s *Store) TryClaimMfaCode(ctx context.Context, userID uuid.UUID, codeHash string, ttl time.Duration) (bool, error)
- func (s *Store) UnlinkExternalLogin(ctx context.Context, provider ulinzi.IdentityProvider, providerKey string) (bool, error)
- func (s *Store) UnlockAccount(ctx context.Context, email, reason string, byUserID *string, at time.Time) error
- func (s *Store) UpdatePasskeySignCount(ctx context.Context, id uuid.UUID, signCount uint32) error
- func (s *Store) UpdatePasswordHash(ctx context.Context, id uuid.UUID, passwordHash string) error
- func (s *Store) UpsertIdpConfig(ctx context.Context, c *ulinzi.IdpConfiguration) error
- func (s *Store) UpsertMfa(ctx context.Context, m *ulinzi.AppUserMfa) error
- func (s *Store) VerifyAuditChain(ctx context.Context, hashFn func(prevHash string, e ulinzi.AuditEvent) string) (bool, int64, error)
Constants ¶
This section is empty.
Variables ¶
This section is empty.
Functions ¶
This section is empty.
Types ¶
type Store ¶
type Store struct {
// contains filtered or unexported fields
}
Store implements ulinzi.Store against a pgx connection pool.
func (*Store) AddPasskey ¶ added in v0.4.0
AddPasskey inserts a new passkey. The unique credential_id index rejects duplicates.
func (*Store) AppendChained ¶
func (s *Store) AppendChained(ctx context.Context, e ulinzi.AuditEvent, hashFn func(prevHash string) string) error
AppendChained appends a hash-chained audit row. It serializes concurrent appends with a transaction-scoped advisory lock, reads the previous row's hash, computes this row's hash via hashFn(prevHash), and inserts atomically.
func (*Store) BumpSecurityStamp ¶
BumpSecurityStamp rotates a user's security stamp (forces global logout).
func (*Store) CountRecentFailures ¶
func (s *Store) CountRecentFailures(ctx context.Context, email string, since time.Time) (int, error)
CountRecentFailures counts failed attempts for email at or after since.
func (*Store) CreateSession ¶
func (*Store) CreateUser ¶
func (*Store) DeleteExpiredMfaCodes ¶
DeleteExpiredMfaCodes prunes stale replay-ledger rows. Run on a schedule.
func (*Store) EnforceSessionCap ¶
EnforceSessionCap deletes a user's active sessions beyond the newest max (MaxSessionsPerUser). A max <= 0 is a no-op.
func (*Store) GetActiveLockout ¶
GetActiveLockout returns the most recent lockout row not yet admin-unlocked (even if expired), or nil.
func (*Store) GetActiveSessionPolicy ¶
func (*Store) GetIdpConfigByProvider ¶ added in v0.4.0
func (s *Store) GetIdpConfigByProvider(ctx context.Context, provider string) (*ulinzi.IdpConfiguration, error)
GetIdpConfigByProvider returns the config row for a provider, or nil if none.
func (*Store) GetLockoutPolicy ¶
GetLockoutPolicy returns the most recently updated lockout policy, or nil.
func (*Store) GetPasskeyByCredentialID ¶ added in v0.4.0
func (s *Store) GetPasskeyByCredentialID(ctx context.Context, credentialID string) (*ulinzi.AppUserPasskey, error)
GetPasskeyByCredentialID looks up a passkey by its globally-unique credential id, or nil if none.
func (*Store) GetPasswordPolicy ¶
GetPasswordPolicy returns the most recently updated password policy, or nil if none is configured (callers fall back to ulinzi.DefaultPasswordPolicy).
func (*Store) GetSessionByID ¶
func (*Store) GetUserAuthByEmail ¶
func (*Store) GetUserAuthByID ¶
func (*Store) GetUserByID ¶
func (*Store) GetUserIDByExternalLogin ¶ added in v0.4.0
func (s *Store) GetUserIDByExternalLogin(ctx context.Context, provider ulinzi.IdentityProvider, providerKey string) (uuid.UUID, bool, error)
GetUserIDByExternalLogin resolves the local user id for (provider, providerKey), or ok=false when no link exists.
func (*Store) GetUserPermissions ¶
func (*Store) GetUserRoles ¶
func (*Store) InsertLockout ¶
func (s *Store) InsertLockout(ctx context.Context, email string, userID *string, lockoutCount, failedCount int, lockedAt time.Time, expiresAt *time.Time) error
InsertLockout appends a new account_lockouts row (expiresAt nil => permanent).
func (*Store) LinkExternalLogin ¶ added in v0.4.0
LinkExternalLogin idempotently upserts an external login on its composite key.
func (*Store) ListExternalLogins ¶ added in v0.4.0
func (s *Store) ListExternalLogins(ctx context.Context, userID uuid.UUID) ([]ulinzi.UserLogin, error)
ListExternalLogins returns all external links for a user (ordered by provider).
func (*Store) ListIdpConfigs ¶ added in v0.4.0
ListIdpConfigs returns every configured provider, ordered by provider.
func (*Store) ListPasskeysByUserID ¶ added in v0.4.0
func (s *Store) ListPasskeysByUserID(ctx context.Context, userID uuid.UUID) ([]ulinzi.AppUserPasskey, error)
ListPasskeysByUserID returns a user's passkeys, oldest first.
func (*Store) RecordLoginAttempt ¶
RecordLoginAttempt appends one row to security.login_attempts.
func (*Store) RemovePasskeyByUserAndCredentialID ¶ added in v0.4.0
func (s *Store) RemovePasskeyByUserAndCredentialID(ctx context.Context, userID uuid.UUID, credentialID string) (bool, error)
RemovePasskeyByUserAndCredentialID deletes a user's passkey, reporting whether a row was removed.
func (*Store) RevokeSession ¶
func (*Store) TouchSession ¶
func (*Store) TryClaimMfaCode ¶
func (s *Store) TryClaimMfaCode(ctx context.Context, userID uuid.UUID, codeHash string, ttl time.Duration) (bool, error)
TryClaimMfaCode atomically records a one-time use of codeHash for userID. It returns true on first use, false when a live (non-expired) claim already exists (a replay). An expired collision row is reclaimed. It fails closed on error.
The UNIQUE (user_id, code_hash) index makes the claim atomic: the INSERT ... ON CONFLICT DO UPDATE ... WHERE (expired) updates + RETURNs only when the prior claim has expired; a live claim leaves the WHERE false, so RETURNING yields no row (ErrNoRows) => replay.
func (*Store) UnlinkExternalLogin ¶ added in v0.4.0
func (s *Store) UnlinkExternalLogin(ctx context.Context, provider ulinzi.IdentityProvider, providerKey string) (bool, error)
UnlinkExternalLogin removes an external login, reporting whether a row existed.
func (*Store) UnlockAccount ¶
func (s *Store) UnlockAccount(ctx context.Context, email, reason string, byUserID *string, at time.Time) error
UnlockAccount closes all open lockouts for the email (admin unlock).
func (*Store) UpdatePasskeySignCount ¶ added in v0.4.0
UpdatePasskeySignCount persists the monotonic signature counter after a successful assertion.
func (*Store) UpdatePasswordHash ¶
func (*Store) UpsertIdpConfig ¶ added in v0.4.0
UpsertIdpConfig inserts or fully replaces the row for c.Provider. The caller (ulinzi.IdpConfigService) has already merged partial updates and encrypted any secrets, so this is a straight full-row write.
func (*Store) VerifyAuditChain ¶
func (s *Store) VerifyAuditChain(ctx context.Context, hashFn func(prevHash string, e ulinzi.AuditEvent) string) (bool, int64, error)
VerifyAuditChain walks the audit log in seq order and recomputes each row's hash. It returns ok=false and the seq of the first row whose prev-linkage or stored hash does not match.