store

package
v0.29.0 Latest Latest
Warning

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

Go to latest
Published: May 5, 2026 License: MIT Imports: 20 Imported by: 0

Documentation

Index

Constants

This section is empty.

Variables

View Source
var (
	// ErrUsernameConflict is returned when a username already exists
	ErrUsernameConflict = errors.New("username already exists")

	// ErrExternalUserMissingIdentity is returned by UpsertExternalUser when
	// one of the required identity fields (username, externalID, authSource,
	// or — on create — email) is blank after trimming. Without a stable
	// identity we cannot safely create or link a user.
	ErrExternalUserMissingIdentity = errors.New(
		"external user missing required identity field (username, external_id, auth_source, or email)",
	)

	// ErrAmbiguousEmail is returned by FindUserByNormalizedEmail when more
	// than one row matches the whitespace-normalized email — a signal that
	// legacy data contains duplicates differing only in incidental
	// whitespace, which must be deduped manually before the caller can
	// proceed. GetUserByEmail (exact indexed match) never returns this.
	ErrAmbiguousEmail = errors.New(
		"multiple users match the normalized email; manual deduplication required",
	)

	// ErrAuthCodeAlreadyUsed is returned by MarkAuthorizationCodeUsed when the
	// code was already consumed by a concurrent request (0 rows updated).
	ErrAuthCodeAlreadyUsed = errors.New("authorization code already used")

	// ErrDeviceCodeAlreadyAuthorized is returned by AuthorizeDeviceCode when the
	// device code was already authorized by a concurrent request (0 rows updated).
	ErrDeviceCodeAlreadyAuthorized = errors.New("device code already authorized")
)
View Source
var (
	NewPaginationParams = types.NewPaginationParams
	CalculatePagination = types.CalculatePagination
)

Re-export functions.

Functions

func GetDialector

func GetDialector(driver, dsn string) (gorm.Dialector, error)

GetDialector returns a GORM dialector for the given driver name and DSN

func RegisterDriver

func RegisterDriver(name string, factory DriverFactory)

RegisterDriver allows registering custom database drivers

Types

type AuditLogFilters

type AuditLogFilters = types.AuditLogFilters

Re-export types from store/types for backward compatibility.

type AuditLogStats

type AuditLogStats = types.AuditLogStats

Re-export types from store/types for backward compatibility.

type DashboardCounts added in v0.24.0

type DashboardCounts = types.DashboardCounts

Re-export types from store/types for backward compatibility.

type DriverFactory

type DriverFactory func(dsn string) gorm.Dialector

DriverFactory is a function that creates a gorm.Dialector

type PaginationParams

type PaginationParams = types.PaginationParams

Re-export types from store/types for backward compatibility.

type PaginationResult

type PaginationResult = types.PaginationResult

Re-export types from store/types for backward compatibility.

type Store

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

func New

func New(ctx context.Context, driver, dsn string, cfg *config.Config) (*Store, error)

func (*Store) AuthorizeDeviceCode added in v0.24.0

func (s *Store) AuthorizeDeviceCode(id int64, userID string) error

AuthorizeDeviceCode atomically marks a device code as authorized by a user. It uses a WHERE clause to ensure only one concurrent request wins; the loser receives ErrDeviceCodeAlreadyAuthorized (0 rows updated).

func (*Store) Close

func (s *Store) Close(ctx context.Context) error

Close gracefully closes the database connection with timeout support

func (*Store) CountActiveTokensByCategory

func (s *Store) CountActiveTokensByCategory(category string) (int64, error)

CountActiveTokensByCategory counts active, non-expired tokens by category

func (*Store) CountActiveTokensByClientID

func (s *Store) CountActiveTokensByClientID(clientID string) (int64, error)

CountActiveTokensByClientID counts active tokens for a specific client

func (*Store) CountClientsByStatus added in v0.17.0

func (s *Store) CountClientsByStatus(status string) (int64, error)

CountClientsByStatus returns the number of clients with the given status

func (*Store) CountPendingDeviceCodes

func (s *Store) CountPendingDeviceCodes() (int64, error)

CountPendingDeviceCodes counts pending (not yet authorized) device codes

func (*Store) CountTotalDeviceCodes

func (s *Store) CountTotalDeviceCodes() (int64, error)

CountTotalDeviceCodes counts all non-expired device codes

func (*Store) CountUsersByRole added in v0.24.0

func (s *Store) CountUsersByRole(role string) (int64, error)

CountUsersByRole returns the number of active users with the given role. Only active users are counted so that disabled admins do not inflate the "last admin" guard used by disable/delete operations.

func (*Store) CreateAccessToken

func (s *Store) CreateAccessToken(token *models.AccessToken) error

func (*Store) CreateAuditLog

func (s *Store) CreateAuditLog(log *models.AuditLog) error

CreateAuditLog creates a single audit log entry

func (*Store) CreateAuditLogBatch

func (s *Store) CreateAuditLogBatch(logs []*models.AuditLog) error

CreateAuditLogBatch creates multiple audit log entries in a single transaction

func (*Store) CreateAuthorizationCode

func (s *Store) CreateAuthorizationCode(code *models.AuthorizationCode) error

CreateAuthorizationCode persists a new authorization code

func (*Store) CreateClient

func (s *Store) CreateClient(client *models.OAuthApplication) error

func (*Store) CreateDeviceCode

func (s *Store) CreateDeviceCode(dc *models.DeviceCode) error

CreateDeviceCode creates a new device code

func (*Store) CreateOAuthConnection

func (s *Store) CreateOAuthConnection(conn *models.OAuthConnection) error

CreateOAuthConnection creates a new OAuth connection

func (*Store) CreateUser

func (s *Store) CreateUser(user *models.User) error

CreateUser creates a new user

func (*Store) DB deprecated

func (s *Store) DB() *gorm.DB

DB returns the underlying GORM database connection (for transactions)

Deprecated: use RunInTransaction instead of accessing the raw DB handle.

func (*Store) DeleteClient

func (s *Store) DeleteClient(clientID string) error

func (*Store) DeleteDeviceCodeByID

func (s *Store) DeleteDeviceCodeByID(id int64) error

DeleteDeviceCodeByID deletes device code by ID (primary key)

func (*Store) DeleteExpiredDeviceCodes

func (s *Store) DeleteExpiredDeviceCodes() error

func (*Store) DeleteExpiredTokens

func (s *Store) DeleteExpiredTokens() error

func (*Store) DeleteOAuthConnection

func (s *Store) DeleteOAuthConnection(id string) error

DeleteOAuthConnection deletes an OAuth connection by ID

func (*Store) DeleteOAuthConnectionsByUserID added in v0.24.0

func (s *Store) DeleteOAuthConnectionsByUserID(userID string) error

DeleteOAuthConnectionsByUserID deletes all OAuth connections for a user.

func (*Store) DeleteOldAuditLogs

func (s *Store) DeleteOldAuditLogs(olderThan time.Time) (int64, error)

DeleteOldAuditLogs deletes audit logs older than the specified time

func (*Store) DeleteUser

func (s *Store) DeleteUser(id string) error

DeleteUser deletes a user by ID

func (*Store) FindUserByNormalizedEmail added in v0.27.0

func (s *Store) FindUserByNormalizedEmail(email string) (*models.User, error)

FindUserByNormalizedEmail looks up a user by email with whitespace- tolerant matching on the stored side. When more than one row ties to the normalized value, it returns ErrAmbiguousEmail instead of picking a non-deterministic winner. Intended for the OAuth auto-link flow where binding a verified provider to the wrong local user must be prevented.

Performance: the lookup is a `TRIM(email) = ?` scan bounded by LIMIT 2, which is NOT backed by the UNIQUE email index. Callers that do not need whitespace tolerance (the common case — admin uniqueness checks, etc.) should use GetUserByEmail instead.

Known limitation: Go's strings.TrimSpace strips Unicode whitespace while SQL TRIM() only removes ASCII spaces by default on SQLite and Postgres. The write paths in this package trim on insert/update, so newly stored rows stay free of both kinds; only pre-existing legacy rows containing exotic whitespace (tabs, NBSP, …) would miss this lookup.

func (*Store) GetAccessTokenByHash added in v0.15.0

func (s *Store) GetAccessTokenByHash(hash string) (*models.AccessToken, error)

func (*Store) GetAccessTokenByID

func (s *Store) GetAccessTokenByID(tokenID string) (*models.AccessToken, error)

func (*Store) GetActiveTokenHashesByAuthorizationID added in v0.24.0

func (s *Store) GetActiveTokenHashesByAuthorizationID(authorizationID uint) ([]string, error)

GetActiveTokenHashesByAuthorizationID returns token hashes for all active tokens linked to a specific UserAuthorization. Used for cache invalidation before bulk revocation.

func (*Store) GetActiveTokenHashesByClientID added in v0.24.0

func (s *Store) GetActiveTokenHashesByClientID(clientID string) ([]string, error)

GetActiveTokenHashesByClientID returns token hashes for all active tokens belonging to a specific client. Used for cache invalidation before bulk revocation.

func (*Store) GetActiveTokenHashesByFamilyID added in v0.23.0

func (s *Store) GetActiveTokenHashesByFamilyID(familyID string) ([]string, error)

GetActiveTokenHashesByFamilyID returns token hashes for all active tokens in a family. Used for cache invalidation before bulk revocation.

func (*Store) GetAuditLogStats

func (s *Store) GetAuditLogStats(startTime, endTime time.Time) (AuditLogStats, error)

GetAuditLogStats returns statistics about audit logs in a given time range

func (*Store) GetAuditLogsPaginated

func (s *Store) GetAuditLogsPaginated(
	params PaginationParams,
	filters AuditLogFilters,
) ([]models.AuditLog, PaginationResult, error)

GetAuditLogsPaginated retrieves audit logs with pagination and filtering

func (*Store) GetAuthorizationCodeByHash

func (s *Store) GetAuthorizationCodeByHash(hash string) (*models.AuthorizationCode, error)

GetAuthorizationCodeByHash retrieves an authorization code by its SHA-256 hash

func (*Store) GetClient

func (s *Store) GetClient(clientID string) (*models.OAuthApplication, error)

func (*Store) GetClientAuthorizations

func (s *Store) GetClientAuthorizations(clientID string) ([]models.UserAuthorization, error)

GetClientAuthorizations returns all active consent records for a client, ordered by grant date

func (*Store) GetClientByIntID

func (s *Store) GetClientByIntID(id int64) (*models.OAuthApplication, error)

GetClientByIntID retrieves an OAuth application by its integer primary key

func (*Store) GetClientsByIDs

func (s *Store) GetClientsByIDs(clientIDs []string) (map[string]*models.OAuthApplication, error)

func (*Store) GetDashboardCounts added in v0.24.0

func (s *Store) GetDashboardCounts() (DashboardCounts, error)

GetDashboardCounts returns all dashboard metrics in a single raw SQL query using scalar subselects. Works on both SQLite and PostgreSQL.

func (*Store) GetDeviceCodeByUserCode

func (s *Store) GetDeviceCodeByUserCode(userCode string) (*models.DeviceCode, error)

GetDeviceCodeByUserCode retrieves a device code by user code

func (*Store) GetDeviceCodesByID

func (s *Store) GetDeviceCodesByID(deviceCodeID string) ([]*models.DeviceCode, error)

GetDeviceCodesByID retrieves all device codes with matching ID suffix Used for hash verification during token exchange

func (*Store) GetOAuthConnection

func (s *Store) GetOAuthConnection(
	provider, providerUserID string,
) (*models.OAuthConnection, error)

GetOAuthConnection finds an OAuth connection by provider and provider user ID

func (*Store) GetOAuthConnectionByUserAndID added in v0.26.0

func (s *Store) GetOAuthConnectionByUserAndID(
	userID, connectionID string,
) (*models.OAuthConnection, error)

GetOAuthConnectionByUserAndID finds an OAuth connection by user ID and connection ID.

func (*Store) GetOAuthConnectionByUserAndProvider

func (s *Store) GetOAuthConnectionByUserAndProvider(
	userID, provider string,
) (*models.OAuthConnection, error)

GetOAuthConnectionByUserAndProvider finds an OAuth connection by user ID and provider

func (*Store) GetOAuthConnectionsByUserID

func (s *Store) GetOAuthConnectionsByUserID(userID string) ([]models.OAuthConnection, error)

GetOAuthConnectionsByUserID returns all OAuth connections for a user

func (*Store) GetTokenHashesByUserID added in v0.24.0

func (s *Store) GetTokenHashesByUserID(userID string) ([]string, error)

GetTokenHashesByUserID returns token hashes for all tokens belonging to a user (any status). Unlike GetActiveTokenHashesBy*, this includes revoked/disabled tokens because the caller (RevokeTokensByUserID) performs a hard DELETE regardless of status.

func (*Store) GetTokensByCategoryAndStatus

func (s *Store) GetTokensByCategoryAndStatus(
	userID, category, status string,
) ([]models.AccessToken, error)

GetTokensByCategoryAndStatus returns tokens filtered by category and status

func (*Store) GetTokensByUserID

func (s *Store) GetTokensByUserID(userID string) ([]models.AccessToken, error)

func (*Store) GetTokensByUserIDPaginated

func (s *Store) GetTokensByUserIDPaginated(
	userID string,
	params PaginationParams,
) ([]models.AccessToken, PaginationResult, error)

GetTokensByUserIDPaginated returns paginated tokens for a user with search support.

func (*Store) GetTokensPaginated added in v0.24.0

func (s *Store) GetTokensPaginated(
	params PaginationParams,
) ([]models.AccessToken, PaginationResult, error)

GetTokensPaginated returns paginated tokens across all users with search support. Search additionally matches against username and email (via subquery).

func (*Store) GetUserAuthorization

func (s *Store) GetUserAuthorization(
	userID string,
	applicationID int64,
) (*models.UserAuthorization, error)

GetUserAuthorization retrieves the active consent record for a (user, application) pair

func (*Store) GetUserAuthorizationByUUID

func (s *Store) GetUserAuthorizationByUUID(
	authUUID, userID string,
) (*models.UserAuthorization, error)

GetUserAuthorizationByUUID retrieves an authorization by its public UUID, scoped to the owner

func (*Store) GetUserByEmail

func (s *Store) GetUserByEmail(email string) (*models.User, error)

GetUserByEmail finds a user by the exact email address, using the UNIQUE index on the column. The input is trimmed with strings.TrimSpace before matching so callers can pass user-entered values safely, but the stored side is not normalized — legacy rows whose stored email carries incidental whitespace will NOT be found by this method. Callers that must protect against ambiguous legacy duplicates (notably the OAuth auto-link path) should use FindUserByNormalizedEmail instead; that path pays for a non-indexed TRIM-based scan in exchange for whitespace tolerance and ambiguity detection.

func (*Store) GetUserByExternalID

func (s *Store) GetUserByExternalID(externalID, authSource string) (*models.User, error)

GetUserByExternalID finds a user by their external ID and auth source

func (*Store) GetUserByID

func (s *Store) GetUserByID(id string) (*models.User, error)

func (*Store) GetUserByUsername

func (s *Store) GetUserByUsername(username string) (*models.User, error)

func (*Store) GetUserStatsByUserID added in v0.24.0

func (s *Store) GetUserStatsByUserID(userID string) (types.UserStatsCounts, error)

GetUserStatsByUserID returns all user stats (active tokens, OAuth connections, active authorizations) in a single database query using subqueries.

func (*Store) GetUsersByIDs

func (s *Store) GetUsersByIDs(userIDs []string) (map[string]*models.User, error)

GetUsersByIDs batch loads users by IDs using WHERE IN to prevent N+1 queries

func (*Store) Health

func (s *Store) Health() error

Health checks the database connection

func (*Store) ListClientsByUserID added in v0.17.0

func (s *Store) ListClientsByUserID(
	userID string,
	params PaginationParams,
) ([]models.OAuthApplication, PaginationResult, error)

ListClientsByUserID returns paginated OAuth clients owned by the given user

func (*Store) ListClientsPaginated

func (s *Store) ListClientsPaginated(
	params PaginationParams,
) ([]models.OAuthApplication, PaginationResult, error)

ListClientsPaginated returns paginated OAuth clients with search and optional status filter support

func (*Store) ListUserAuthorizations

func (s *Store) ListUserAuthorizations(userID string) ([]models.UserAuthorization, error)

ListUserAuthorizations returns all active authorizations for a user, newest first

func (*Store) ListUsersPaginated added in v0.24.0

func (s *Store) ListUsersPaginated(
	params PaginationParams,
) ([]models.User, PaginationResult, error)

ListUsersPaginated returns paginated users with search, role, and auth source filtering.

func (*Store) MarkAuthorizationCodeUsed

func (s *Store) MarkAuthorizationCodeUsed(id uint) error

MarkAuthorizationCodeUsed atomically sets UsedAt only when the code has not yet been consumed. The WHERE clause includes "used_at IS NULL" so that a concurrent request that races past the application-level IsUsed() check will update 0 rows and receive ErrAuthCodeAlreadyUsed, preventing double issuance.

func (*Store) RevokeAllActiveTokensByClientID

func (s *Store) RevokeAllActiveTokensByClientID(clientID string) (int64, error)

RevokeAllActiveTokensByClientID revokes every active token for a client and returns the count

func (*Store) RevokeAllUserAuthorizationsByClientID

func (s *Store) RevokeAllUserAuthorizationsByClientID(clientID string) error

RevokeAllUserAuthorizationsByClientID invalidates all active consent records for a client

func (*Store) RevokeAllUserAuthorizationsByUserID added in v0.24.0

func (s *Store) RevokeAllUserAuthorizationsByUserID(userID string) error

RevokeAllUserAuthorizationsByUserID invalidates all active consent records for a user.

func (*Store) RevokeToken

func (s *Store) RevokeToken(tokenID string) error

func (*Store) RevokeTokenFamily added in v0.20.0

func (s *Store) RevokeTokenFamily(familyID string) (int64, error)

RevokeTokenFamily revokes all active tokens that share the same TokenFamilyID. This is used for refresh token rotation replay detection: when a revoked refresh token is reused, all tokens in the family must be invalidated to prevent stolen token abuse.

func (*Store) RevokeTokensByAuthorizationID

func (s *Store) RevokeTokensByAuthorizationID(authorizationID uint) error

RevokeTokensByAuthorizationID revokes all active tokens linked to a specific UserAuthorization

func (*Store) RevokeTokensByClientID

func (s *Store) RevokeTokensByClientID(clientID string) error

func (*Store) RevokeTokensByUserID

func (s *Store) RevokeTokensByUserID(userID string) error

func (*Store) RevokeUserAuthorization

func (s *Store) RevokeUserAuthorization(
	authUUID, userID string,
) (*models.UserAuthorization, error)

RevokeUserAuthorization marks an authorization as revoked and returns the record

func (*Store) RunInTransaction added in v0.20.0

func (s *Store) RunInTransaction(fn func(tx core.Store) error) error

RunInTransaction executes fn inside a database transaction. The Store passed to fn operates within the transaction scope.

func (*Store) UpdateClient

func (s *Store) UpdateClient(client *models.OAuthApplication) error

func (*Store) UpdateDeviceCode

func (s *Store) UpdateDeviceCode(dc *models.DeviceCode) error

UpdateDeviceCode updates a device code

func (*Store) UpdateOAuthConnection

func (s *Store) UpdateOAuthConnection(conn *models.OAuthConnection) error

UpdateOAuthConnection updates an existing OAuth connection

func (*Store) UpdateTokenLastUsedAt added in v0.20.0

func (s *Store) UpdateTokenLastUsedAt(tokenID string, t time.Time) error

UpdateTokenLastUsedAt updates the last_used_at timestamp of a token

func (*Store) UpdateTokenStatus

func (s *Store) UpdateTokenStatus(tokenID, status string) error

UpdateTokenStatus updates the status of a token

func (*Store) UpdateUser

func (s *Store) UpdateUser(user *models.User) error

UpdateUser updates an existing user

func (*Store) UpsertExternalUser

func (s *Store) UpsertExternalUser(
	username, externalID, authSource, email, fullName string,
) (*models.User, error)

UpsertExternalUser creates or updates a user from external authentication

func (*Store) UpsertUserAuthorization

func (s *Store) UpsertUserAuthorization(auth *models.UserAuthorization) error

UpsertUserAuthorization creates a new consent record or re-activates and updates an existing one. Uses a single atomic INSERT ... ON CONFLICT DO UPDATE to avoid the race condition that arises from a non-atomic SELECT-then-INSERT/UPDATE pattern.

Directories

Path Synopsis

Jump to

Keyboard shortcuts

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