2019-11-16 17:38:21 +07:00
package storage
import (
2021-11-23 16:45:38 +07:00
"context"
2021-11-25 08:56:58 +07:00
"crypto/sha256"
2019-11-16 17:38:21 +07:00
"database/sql"
2021-11-23 16:45:38 +07:00
"errors"
2019-11-17 02:50:58 +07:00
"fmt"
2019-11-16 17:38:21 +07:00
"time"
2022-04-07 12:33:53 +07:00
"github.com/google/uuid"
2021-11-23 16:45:38 +07:00
"github.com/jmoiron/sqlx"
2020-07-16 12:56:08 +07:00
"github.com/sirupsen/logrus"
2021-12-01 19:11:29 +07:00
"github.com/authelia/authelia/v4/internal/configuration/schema"
2021-08-11 08:04:35 +07:00
"github.com/authelia/authelia/v4/internal/logging"
2022-03-06 12:47:40 +07:00
"github.com/authelia/authelia/v4/internal/model"
2019-11-16 17:38:21 +07:00
)
2021-11-23 16:45:38 +07:00
// NewSQLProvider generates a generic SQLProvider to be used with other SQL provider NewUp's.
2021-12-01 19:11:29 +07:00
func NewSQLProvider ( config * schema . Configuration , name , driverName , dataSourceName string ) ( provider SQLProvider ) {
2021-11-23 16:45:38 +07:00
db , err := sqlx . Open ( driverName , dataSourceName )
2020-03-05 06:25:52 +07:00
2021-11-23 16:45:38 +07:00
provider = SQLProvider {
2021-11-25 08:56:58 +07:00
db : db ,
2021-12-01 19:11:29 +07:00
key : sha256 . Sum256 ( [ ] byte ( config . Storage . EncryptionKey ) ) ,
2021-11-23 16:45:38 +07:00
name : name ,
driverName : driverName ,
2021-12-01 19:11:29 +07:00
config : config ,
2021-11-23 16:45:38 +07:00
errOpen : err ,
2021-11-25 08:56:58 +07:00
log : logging . Logger ( ) ,
2019-11-17 02:50:58 +07:00
2021-11-23 16:45:38 +07:00
sqlInsertAuthenticationAttempt : fmt . Sprintf ( queryFmtInsertAuthenticationLogEntry , tableAuthenticationLogs ) ,
sqlSelectAuthenticationAttemptsByUsername : fmt . Sprintf ( queryFmtSelect1FAAuthenticationLogEntryByUsername , tableAuthenticationLogs ) ,
2019-11-17 02:50:58 +07:00
2021-12-04 11:34:20 +07:00
sqlInsertIdentityVerification : fmt . Sprintf ( queryFmtInsertIdentityVerification , tableIdentityVerification ) ,
sqlConsumeIdentityVerification : fmt . Sprintf ( queryFmtConsumeIdentityVerification , tableIdentityVerification ) ,
sqlSelectIdentityVerification : fmt . Sprintf ( queryFmtSelectIdentityVerification , tableIdentityVerification ) ,
2019-11-17 02:50:58 +07:00
2021-11-25 08:56:58 +07:00
sqlUpsertTOTPConfig : fmt . Sprintf ( queryFmtUpsertTOTPConfiguration , tableTOTPConfigurations ) ,
sqlDeleteTOTPConfig : fmt . Sprintf ( queryFmtDeleteTOTPConfiguration , tableTOTPConfigurations ) ,
sqlSelectTOTPConfig : fmt . Sprintf ( queryFmtSelectTOTPConfiguration , tableTOTPConfigurations ) ,
sqlSelectTOTPConfigs : fmt . Sprintf ( queryFmtSelectTOTPConfigurations , tableTOTPConfigurations ) ,
2022-03-03 18:20:43 +07:00
sqlUpdateTOTPConfigSecret : fmt . Sprintf ( queryFmtUpdateTOTPConfigurationSecret , tableTOTPConfigurations ) ,
sqlUpdateTOTPConfigSecretByUsername : fmt . Sprintf ( queryFmtUpdateTOTPConfigurationSecretByUsername , tableTOTPConfigurations ) ,
sqlUpdateTOTPConfigRecordSignIn : fmt . Sprintf ( queryFmtUpdateTOTPConfigRecordSignIn , tableTOTPConfigurations ) ,
sqlUpdateTOTPConfigRecordSignInByUsername : fmt . Sprintf ( queryFmtUpdateTOTPConfigRecordSignInByUsername , tableTOTPConfigurations ) ,
2019-11-17 02:50:58 +07:00
2022-03-03 18:20:43 +07:00
sqlUpsertWebauthnDevice : fmt . Sprintf ( queryFmtUpsertWebauthnDevice , tableWebauthnDevices ) ,
sqlSelectWebauthnDevices : fmt . Sprintf ( queryFmtSelectWebauthnDevices , tableWebauthnDevices ) ,
sqlSelectWebauthnDevicesByUsername : fmt . Sprintf ( queryFmtSelectWebauthnDevicesByUsername , tableWebauthnDevices ) ,
2021-12-03 07:04:11 +07:00
2022-03-03 18:20:43 +07:00
sqlUpdateWebauthnDevicePublicKey : fmt . Sprintf ( queryFmtUpdateWebauthnDevicePublicKey , tableWebauthnDevices ) ,
sqlUpdateWebauthnDevicePublicKeyByUsername : fmt . Sprintf ( queryFmtUpdateUpdateWebauthnDevicePublicKeyByUsername , tableWebauthnDevices ) ,
sqlUpdateWebauthnDeviceRecordSignIn : fmt . Sprintf ( queryFmtUpdateWebauthnDeviceRecordSignIn , tableWebauthnDevices ) ,
sqlUpdateWebauthnDeviceRecordSignInByUsername : fmt . Sprintf ( queryFmtUpdateWebauthnDeviceRecordSignInByUsername , tableWebauthnDevices ) ,
2020-07-16 12:56:08 +07:00
2021-12-01 10:32:58 +07:00
sqlUpsertDuoDevice : fmt . Sprintf ( queryFmtUpsertDuoDevice , tableDuoDevices ) ,
sqlDeleteDuoDevice : fmt . Sprintf ( queryFmtDeleteDuoDevice , tableDuoDevices ) ,
sqlSelectDuoDevice : fmt . Sprintf ( queryFmtSelectDuoDevice , tableDuoDevices ) ,
2021-11-23 16:45:38 +07:00
sqlUpsertPreferred2FAMethod : fmt . Sprintf ( queryFmtUpsertPreferred2FAMethod , tableUserPreferences ) ,
sqlSelectPreferred2FAMethod : fmt . Sprintf ( queryFmtSelectPreferred2FAMethod , tableUserPreferences ) ,
2022-03-03 18:20:43 +07:00
sqlSelectUserInfo : fmt . Sprintf ( queryFmtSelectUserInfo , tableTOTPConfigurations , tableWebauthnDevices , tableDuoDevices , tableUserPreferences ) ,
2019-11-16 17:38:21 +07:00
2022-04-07 12:33:53 +07:00
sqlInsertUserOpaqueIdentifier : fmt . Sprintf ( queryFmtInsertUserOpaqueIdentifier , tableUserOpaqueIdentifier ) ,
sqlSelectUserOpaqueIdentifier : fmt . Sprintf ( queryFmtSelectUserOpaqueIdentifier , tableUserOpaqueIdentifier ) ,
2022-04-09 14:13:19 +07:00
sqlSelectUserOpaqueIdentifiers : fmt . Sprintf ( queryFmtSelectUserOpaqueIdentifiers , tableUserOpaqueIdentifier ) ,
2022-04-07 12:33:53 +07:00
sqlSelectUserOpaqueIdentifierBySignature : fmt . Sprintf ( queryFmtSelectUserOpaqueIdentifierBySignature , tableUserOpaqueIdentifier ) ,
sqlInsertOAuth2AuthorizeCodeSession : fmt . Sprintf ( queryFmtInsertOAuth2Session , tableOAuth2AuthorizeCodeSession ) ,
sqlSelectOAuth2AuthorizeCodeSession : fmt . Sprintf ( queryFmtSelectOAuth2Session , tableOAuth2AuthorizeCodeSession ) ,
sqlRevokeOAuth2AuthorizeCodeSession : fmt . Sprintf ( queryFmtRevokeOAuth2Session , tableOAuth2AuthorizeCodeSession ) ,
sqlRevokeOAuth2AuthorizeCodeSessionByRequestID : fmt . Sprintf ( queryFmtRevokeOAuth2SessionByRequestID , tableOAuth2AuthorizeCodeSession ) ,
sqlDeactivateOAuth2AuthorizeCodeSession : fmt . Sprintf ( queryFmtDeactivateOAuth2Session , tableOAuth2AuthorizeCodeSession ) ,
sqlDeactivateOAuth2AuthorizeCodeSessionByRequestID : fmt . Sprintf ( queryFmtDeactivateOAuth2SessionByRequestID , tableOAuth2AuthorizeCodeSession ) ,
sqlInsertOAuth2AccessTokenSession : fmt . Sprintf ( queryFmtInsertOAuth2Session , tableOAuth2AccessTokenSession ) ,
sqlSelectOAuth2AccessTokenSession : fmt . Sprintf ( queryFmtSelectOAuth2Session , tableOAuth2AccessTokenSession ) ,
sqlRevokeOAuth2AccessTokenSession : fmt . Sprintf ( queryFmtRevokeOAuth2Session , tableOAuth2AccessTokenSession ) ,
sqlRevokeOAuth2AccessTokenSessionByRequestID : fmt . Sprintf ( queryFmtRevokeOAuth2SessionByRequestID , tableOAuth2AccessTokenSession ) ,
sqlDeactivateOAuth2AccessTokenSession : fmt . Sprintf ( queryFmtDeactivateOAuth2Session , tableOAuth2AccessTokenSession ) ,
sqlDeactivateOAuth2AccessTokenSessionByRequestID : fmt . Sprintf ( queryFmtDeactivateOAuth2SessionByRequestID , tableOAuth2AccessTokenSession ) ,
sqlInsertOAuth2RefreshTokenSession : fmt . Sprintf ( queryFmtInsertOAuth2Session , tableOAuth2RefreshTokenSession ) ,
sqlSelectOAuth2RefreshTokenSession : fmt . Sprintf ( queryFmtSelectOAuth2Session , tableOAuth2RefreshTokenSession ) ,
sqlRevokeOAuth2RefreshTokenSession : fmt . Sprintf ( queryFmtRevokeOAuth2Session , tableOAuth2RefreshTokenSession ) ,
sqlRevokeOAuth2RefreshTokenSessionByRequestID : fmt . Sprintf ( queryFmtRevokeOAuth2SessionByRequestID , tableOAuth2RefreshTokenSession ) ,
sqlDeactivateOAuth2RefreshTokenSession : fmt . Sprintf ( queryFmtDeactivateOAuth2Session , tableOAuth2RefreshTokenSession ) ,
sqlDeactivateOAuth2RefreshTokenSessionByRequestID : fmt . Sprintf ( queryFmtDeactivateOAuth2SessionByRequestID , tableOAuth2RefreshTokenSession ) ,
sqlInsertOAuth2PKCERequestSession : fmt . Sprintf ( queryFmtInsertOAuth2Session , tableOAuth2PKCERequestSession ) ,
sqlSelectOAuth2PKCERequestSession : fmt . Sprintf ( queryFmtSelectOAuth2Session , tableOAuth2PKCERequestSession ) ,
sqlRevokeOAuth2PKCERequestSession : fmt . Sprintf ( queryFmtRevokeOAuth2Session , tableOAuth2PKCERequestSession ) ,
sqlRevokeOAuth2PKCERequestSessionByRequestID : fmt . Sprintf ( queryFmtRevokeOAuth2SessionByRequestID , tableOAuth2PKCERequestSession ) ,
sqlDeactivateOAuth2PKCERequestSession : fmt . Sprintf ( queryFmtDeactivateOAuth2Session , tableOAuth2PKCERequestSession ) ,
sqlDeactivateOAuth2PKCERequestSessionByRequestID : fmt . Sprintf ( queryFmtDeactivateOAuth2SessionByRequestID , tableOAuth2PKCERequestSession ) ,
sqlInsertOAuth2OpenIDConnectSession : fmt . Sprintf ( queryFmtInsertOAuth2Session , tableOAuth2OpenIDConnectSession ) ,
sqlSelectOAuth2OpenIDConnectSession : fmt . Sprintf ( queryFmtSelectOAuth2Session , tableOAuth2OpenIDConnectSession ) ,
sqlRevokeOAuth2OpenIDConnectSession : fmt . Sprintf ( queryFmtRevokeOAuth2Session , tableOAuth2OpenIDConnectSession ) ,
sqlRevokeOAuth2OpenIDConnectSessionByRequestID : fmt . Sprintf ( queryFmtRevokeOAuth2SessionByRequestID , tableOAuth2OpenIDConnectSession ) ,
sqlDeactivateOAuth2OpenIDConnectSession : fmt . Sprintf ( queryFmtDeactivateOAuth2Session , tableOAuth2OpenIDConnectSession ) ,
sqlDeactivateOAuth2OpenIDConnectSessionByRequestID : fmt . Sprintf ( queryFmtDeactivateOAuth2SessionByRequestID , tableOAuth2OpenIDConnectSession ) ,
sqlInsertOAuth2ConsentSession : fmt . Sprintf ( queryFmtInsertOAuth2ConsentSession , tableOAuth2ConsentSession ) ,
2022-04-25 07:31:05 +07:00
sqlUpdateOAuth2ConsentSessionSubject : fmt . Sprintf ( queryFmtUpdateOAuth2ConsentSessionSubject , tableOAuth2ConsentSession ) ,
2022-04-07 12:33:53 +07:00
sqlUpdateOAuth2ConsentSessionResponse : fmt . Sprintf ( queryFmtUpdateOAuth2ConsentSessionResponse , tableOAuth2ConsentSession ) ,
sqlUpdateOAuth2ConsentSessionGranted : fmt . Sprintf ( queryFmtUpdateOAuth2ConsentSessionGranted , tableOAuth2ConsentSession ) ,
sqlSelectOAuth2ConsentSessionByChallengeID : fmt . Sprintf ( queryFmtSelectOAuth2ConsentSessionByChallengeID , tableOAuth2ConsentSession ) ,
sqlSelectOAuth2ConsentSessionsPreConfigured : fmt . Sprintf ( queryFmtSelectOAuth2ConsentSessionsPreConfigured , tableOAuth2ConsentSession ) ,
sqlUpsertOAuth2BlacklistedJTI : fmt . Sprintf ( queryFmtUpsertOAuth2BlacklistedJTI , tableOAuth2BlacklistedJTI ) ,
sqlSelectOAuth2BlacklistedJTI : fmt . Sprintf ( queryFmtSelectOAuth2BlacklistedJTI , tableOAuth2BlacklistedJTI ) ,
2021-11-23 16:45:38 +07:00
sqlInsertMigration : fmt . Sprintf ( queryFmtInsertMigration , tableMigrations ) ,
sqlSelectMigrations : fmt . Sprintf ( queryFmtSelectMigrations , tableMigrations ) ,
sqlSelectLatestMigration : fmt . Sprintf ( queryFmtSelectLatestMigration , tableMigrations ) ,
2019-11-16 17:38:21 +07:00
2021-11-25 08:56:58 +07:00
sqlUpsertEncryptionValue : fmt . Sprintf ( queryFmtUpsertEncryptionValue , tableEncryption ) ,
sqlSelectEncryptionValue : fmt . Sprintf ( queryFmtSelectEncryptionValue , tableEncryption ) ,
2021-11-23 16:45:38 +07:00
sqlFmtRenameTable : queryFmtRenameTable ,
2019-11-16 17:38:21 +07:00
}
2021-11-23 16:45:38 +07:00
return provider
}
2020-07-16 12:56:08 +07:00
2021-11-23 16:45:38 +07:00
// SQLProvider is a storage provider persisting data in a SQL database.
type SQLProvider struct {
db * sqlx . DB
2021-11-25 08:56:58 +07:00
key [ 32 ] byte
2021-11-23 16:45:38 +07:00
name string
driverName string
2021-12-03 13:29:55 +07:00
schema string
2021-12-01 19:11:29 +07:00
config * schema . Configuration
2021-11-23 16:45:38 +07:00
errOpen error
2021-11-25 08:56:58 +07:00
log * logrus . Logger
2021-11-23 16:45:38 +07:00
// Table: authentication_logs.
sqlInsertAuthenticationAttempt string
sqlSelectAuthenticationAttemptsByUsername string
2021-11-30 13:58:21 +07:00
// Table: identity_verification.
2021-12-04 11:34:20 +07:00
sqlInsertIdentityVerification string
sqlConsumeIdentityVerification string
sqlSelectIdentityVerification string
2021-11-23 16:45:38 +07:00
// Table: totp_configurations.
2021-11-25 08:56:58 +07:00
sqlUpsertTOTPConfig string
sqlDeleteTOTPConfig string
sqlSelectTOTPConfig string
sqlSelectTOTPConfigs string
2022-03-03 18:20:43 +07:00
sqlUpdateTOTPConfigSecret string
sqlUpdateTOTPConfigSecretByUsername string
sqlUpdateTOTPConfigRecordSignIn string
sqlUpdateTOTPConfigRecordSignInByUsername string
2021-11-23 16:45:38 +07:00
2022-03-03 18:20:43 +07:00
// Table: webauthn_devices.
sqlUpsertWebauthnDevice string
sqlSelectWebauthnDevices string
sqlSelectWebauthnDevicesByUsername string
2021-12-03 07:04:11 +07:00
2022-03-03 18:20:43 +07:00
sqlUpdateWebauthnDevicePublicKey string
sqlUpdateWebauthnDevicePublicKeyByUsername string
sqlUpdateWebauthnDeviceRecordSignIn string
sqlUpdateWebauthnDeviceRecordSignInByUsername string
2021-11-23 16:45:38 +07:00
2022-01-31 12:25:15 +07:00
// Table: duo_devices.
2021-12-01 10:32:58 +07:00
sqlUpsertDuoDevice string
sqlDeleteDuoDevice string
sqlSelectDuoDevice string
2021-11-23 16:45:38 +07:00
// Table: user_preferences.
sqlUpsertPreferred2FAMethod string
sqlSelectPreferred2FAMethod string
sqlSelectUserInfo string
2022-04-07 12:33:53 +07:00
// Table: user_opaque_identifier.
sqlInsertUserOpaqueIdentifier string
sqlSelectUserOpaqueIdentifier string
2022-04-09 14:13:19 +07:00
sqlSelectUserOpaqueIdentifiers string
2022-04-07 12:33:53 +07:00
sqlSelectUserOpaqueIdentifierBySignature string
2021-11-23 16:45:38 +07:00
// Table: migrations.
sqlInsertMigration string
sqlSelectMigrations string
sqlSelectLatestMigration string
2021-11-25 08:56:58 +07:00
// Table: encryption.
sqlUpsertEncryptionValue string
sqlSelectEncryptionValue string
2022-04-07 12:33:53 +07:00
// Table: oauth2_authorization_code_session.
sqlInsertOAuth2AuthorizeCodeSession string
sqlSelectOAuth2AuthorizeCodeSession string
sqlRevokeOAuth2AuthorizeCodeSession string
sqlRevokeOAuth2AuthorizeCodeSessionByRequestID string
sqlDeactivateOAuth2AuthorizeCodeSession string
sqlDeactivateOAuth2AuthorizeCodeSessionByRequestID string
// Table: oauth2_access_token_session.
sqlInsertOAuth2AccessTokenSession string
sqlSelectOAuth2AccessTokenSession string
sqlRevokeOAuth2AccessTokenSession string
sqlRevokeOAuth2AccessTokenSessionByRequestID string
sqlDeactivateOAuth2AccessTokenSession string
sqlDeactivateOAuth2AccessTokenSessionByRequestID string
// Table: oauth2_refresh_token_session.
sqlInsertOAuth2RefreshTokenSession string
sqlSelectOAuth2RefreshTokenSession string
sqlRevokeOAuth2RefreshTokenSession string
sqlRevokeOAuth2RefreshTokenSessionByRequestID string
sqlDeactivateOAuth2RefreshTokenSession string
sqlDeactivateOAuth2RefreshTokenSessionByRequestID string
// Table: oauth2_pkce_request_session.
sqlInsertOAuth2PKCERequestSession string
sqlSelectOAuth2PKCERequestSession string
sqlRevokeOAuth2PKCERequestSession string
sqlRevokeOAuth2PKCERequestSessionByRequestID string
sqlDeactivateOAuth2PKCERequestSession string
sqlDeactivateOAuth2PKCERequestSessionByRequestID string
// Table: oauth2_openid_connect_session.
sqlInsertOAuth2OpenIDConnectSession string
sqlSelectOAuth2OpenIDConnectSession string
sqlRevokeOAuth2OpenIDConnectSession string
sqlRevokeOAuth2OpenIDConnectSessionByRequestID string
sqlDeactivateOAuth2OpenIDConnectSession string
sqlDeactivateOAuth2OpenIDConnectSessionByRequestID string
// Table: oauth2_consent_session.
sqlInsertOAuth2ConsentSession string
2022-04-25 07:31:05 +07:00
sqlUpdateOAuth2ConsentSessionSubject string
2022-04-07 12:33:53 +07:00
sqlUpdateOAuth2ConsentSessionResponse string
sqlUpdateOAuth2ConsentSessionGranted string
sqlSelectOAuth2ConsentSessionByChallengeID string
sqlSelectOAuth2ConsentSessionsPreConfigured string
sqlUpsertOAuth2BlacklistedJTI string
sqlSelectOAuth2BlacklistedJTI string
2021-11-23 16:45:38 +07:00
// Utility.
sqlSelectExistingTables string
sqlFmtRenameTable string
}
2020-07-16 12:56:08 +07:00
2021-11-25 08:56:58 +07:00
// Close the underlying database connection.
func ( p * SQLProvider ) Close ( ) ( err error ) {
return p . db . Close ( )
}
2021-11-23 16:45:38 +07:00
// StartupCheck implements the provider startup check interface.
func ( p * SQLProvider ) StartupCheck ( ) ( err error ) {
if p . errOpen != nil {
2021-11-25 08:56:58 +07:00
return fmt . Errorf ( "error opening database: %w" , p . errOpen )
2019-11-16 17:38:21 +07:00
}
2021-11-23 16:45:38 +07:00
// TODO: Decide if this is needed, or if it should be configurable.
for i := 0 ; i < 19 ; i ++ {
2021-11-25 08:56:58 +07:00
if err = p . db . Ping ( ) ; err == nil {
2021-11-23 16:45:38 +07:00
break
2020-07-16 12:56:08 +07:00
}
2021-11-23 16:45:38 +07:00
time . Sleep ( time . Millisecond * 500 )
2019-11-16 17:38:21 +07:00
}
if err != nil {
2021-11-25 08:56:58 +07:00
return fmt . Errorf ( "error pinging database: %w" , err )
2019-11-16 17:38:21 +07:00
}
2021-11-23 16:45:38 +07:00
p . log . Infof ( "Storage schema is being checked for updates" )
2020-07-16 12:56:08 +07:00
2021-11-23 16:45:38 +07:00
ctx := context . Background ( )
2020-07-16 12:56:08 +07:00
2021-11-25 08:56:58 +07:00
if err = p . SchemaEncryptionCheckKey ( ctx , false ) ; err != nil && ! errors . Is ( err , ErrSchemaEncryptionVersionUnsupported ) {
return err
}
2021-11-23 16:45:38 +07:00
err = p . SchemaMigrate ( ctx , true , SchemaLatest )
2020-05-06 02:35:32 +07:00
2021-11-23 16:45:38 +07:00
switch err {
case ErrSchemaAlreadyUpToDate :
p . log . Infof ( "Storage schema is already up to date" )
return nil
case nil :
return nil
default :
2021-11-25 08:56:58 +07:00
return fmt . Errorf ( "error during schema migrate: %w" , err )
2021-11-23 16:45:38 +07:00
}
2019-11-16 17:38:21 +07:00
}
2022-04-07 12:33:53 +07:00
// BeginTX begins a transaction.
func ( p * SQLProvider ) BeginTX ( ctx context . Context ) ( c context . Context , err error ) {
var tx * sql . Tx
if tx , err = p . db . Begin ( ) ; err != nil {
return nil , err
}
return context . WithValue ( ctx , ctxKeyTransaction , tx ) , nil
}
// Commit performs a database commit.
func ( p * SQLProvider ) Commit ( ctx context . Context ) ( err error ) {
tx , ok := ctx . Value ( ctxKeyTransaction ) . ( * sql . Tx )
if ! ok {
return errors . New ( "could not retrieve tx" )
}
return tx . Commit ( )
}
// Rollback performs a database rollback.
func ( p * SQLProvider ) Rollback ( ctx context . Context ) ( err error ) {
tx , ok := ctx . Value ( ctxKeyTransaction ) . ( * sql . Tx )
if ! ok {
return errors . New ( "could not retrieve tx" )
}
return tx . Rollback ( )
}
// SaveUserOpaqueIdentifier saves a new opaque user identifier to the database.
func ( p * SQLProvider ) SaveUserOpaqueIdentifier ( ctx context . Context , opaqueID model . UserOpaqueIdentifier ) ( err error ) {
if _ , err = p . db . ExecContext ( ctx , p . sqlInsertUserOpaqueIdentifier , opaqueID . Service , opaqueID . SectorID , opaqueID . Username , opaqueID . Identifier ) ; err != nil {
return fmt . Errorf ( "error inserting user opaque id for user '%s' with opaque id '%s': %w" , opaqueID . Username , opaqueID . Identifier . String ( ) , err )
}
return nil
}
// LoadUserOpaqueIdentifier selects an opaque user identifier from the database.
func ( p * SQLProvider ) LoadUserOpaqueIdentifier ( ctx context . Context , opaqueUUID uuid . UUID ) ( opaqueID * model . UserOpaqueIdentifier , err error ) {
opaqueID = & model . UserOpaqueIdentifier { }
if err = p . db . GetContext ( ctx , opaqueID , p . sqlSelectUserOpaqueIdentifier , opaqueUUID ) ; err != nil {
switch {
case errors . Is ( err , sql . ErrNoRows ) :
return nil , nil
default :
return nil , err
}
}
return opaqueID , nil
}
2022-04-09 14:13:19 +07:00
// LoadUserOpaqueIdentifiers selects an opaque user identifiers from the database.
func ( p * SQLProvider ) LoadUserOpaqueIdentifiers ( ctx context . Context ) ( opaqueIDs [ ] model . UserOpaqueIdentifier , err error ) {
var rows * sqlx . Rows
if rows , err = p . db . QueryxContext ( ctx , p . sqlSelectUserOpaqueIdentifiers ) ; err != nil {
return nil , fmt . Errorf ( "error selecting user opaque identifiers: %w" , err )
}
var opaqueID * model . UserOpaqueIdentifier
for rows . Next ( ) {
opaqueID = & model . UserOpaqueIdentifier { }
if err = rows . StructScan ( opaqueID ) ; err != nil {
return nil , fmt . Errorf ( "error selecting user opaque identifiers: error scanning row: %w" , err )
}
opaqueIDs = append ( opaqueIDs , * opaqueID )
}
return opaqueIDs , nil
}
2022-04-07 12:33:53 +07:00
// LoadUserOpaqueIdentifierBySignature selects an opaque user identifier from the database given a service name, sector id, and username.
func ( p * SQLProvider ) LoadUserOpaqueIdentifierBySignature ( ctx context . Context , service , sectorID , username string ) ( opaqueID * model . UserOpaqueIdentifier , err error ) {
opaqueID = & model . UserOpaqueIdentifier { }
if err = p . db . GetContext ( ctx , opaqueID , p . sqlSelectUserOpaqueIdentifierBySignature , service , sectorID , username ) ; err != nil {
switch {
case errors . Is ( err , sql . ErrNoRows ) :
return nil , nil
default :
return nil , err
}
}
return opaqueID , nil
}
2022-04-25 07:31:05 +07:00
// SaveOAuth2ConsentSession inserts an OAuth2.0 consent session.
2022-04-07 12:33:53 +07:00
func ( p * SQLProvider ) SaveOAuth2ConsentSession ( ctx context . Context , consent model . OAuth2ConsentSession ) ( err error ) {
if _ , err = p . db . ExecContext ( ctx , p . sqlInsertOAuth2ConsentSession ,
consent . ChallengeID , consent . ClientID , consent . Subject , consent . Authorized , consent . Granted ,
consent . RequestedAt , consent . RespondedAt , consent . ExpiresAt , consent . Form ,
consent . RequestedScopes , consent . GrantedScopes , consent . RequestedAudience , consent . GrantedAudience ) ; err != nil {
2022-07-26 12:43:39 +07:00
return fmt . Errorf ( "error inserting oauth2 consent session with challenge id '%s' for subject '%s': %w" , consent . ChallengeID . String ( ) , consent . Subject . UUID . String ( ) , err )
2022-04-07 12:33:53 +07:00
}
return nil
}
2022-04-25 07:31:05 +07:00
// SaveOAuth2ConsentSessionSubject updates an OAuth2.0 consent session with the subject.
func ( p * SQLProvider ) SaveOAuth2ConsentSessionSubject ( ctx context . Context , consent model . OAuth2ConsentSession ) ( err error ) {
if _ , err = p . db . ExecContext ( ctx , p . sqlUpdateOAuth2ConsentSessionSubject , consent . Subject , consent . ID ) ; err != nil {
2022-07-26 12:43:39 +07:00
return fmt . Errorf ( "error updating oauth2 consent session subject with id '%d' and challenge id '%s' for subject '%s': %w" , consent . ID , consent . ChallengeID , consent . Subject . UUID , err )
2022-04-25 07:31:05 +07:00
}
return nil
}
// SaveOAuth2ConsentSessionResponse updates an OAuth2.0 consent session with the response.
2022-04-07 12:33:53 +07:00
func ( p * SQLProvider ) SaveOAuth2ConsentSessionResponse ( ctx context . Context , consent model . OAuth2ConsentSession , authorized bool ) ( err error ) {
2022-04-25 07:31:05 +07:00
if _ , err = p . db . ExecContext ( ctx , p . sqlUpdateOAuth2ConsentSessionResponse , authorized , consent . ExpiresAt , consent . GrantedScopes , consent . GrantedAudience , consent . ID ) ; err != nil {
2022-07-26 12:43:39 +07:00
return fmt . Errorf ( "error updating oauth2 consent session (authorized '%t') with id '%d' and challenge id '%s' for subject '%s': %w" , authorized , consent . ID , consent . ChallengeID , consent . Subject . UUID , err )
2022-04-07 12:33:53 +07:00
}
return nil
}
// SaveOAuth2ConsentSessionGranted updates an OAuth2.0 consent recording that it has been granted by the authorization endpoint.
func ( p * SQLProvider ) SaveOAuth2ConsentSessionGranted ( ctx context . Context , id int ) ( err error ) {
if _ , err = p . db . ExecContext ( ctx , p . sqlUpdateOAuth2ConsentSessionGranted , id ) ; err != nil {
return fmt . Errorf ( "error updating oauth2 consent session (granted) with id '%d': %w" , id , err )
}
return nil
}
// LoadOAuth2ConsentSessionByChallengeID returns an OAuth2.0 consent given the challenge ID.
func ( p * SQLProvider ) LoadOAuth2ConsentSessionByChallengeID ( ctx context . Context , challengeID uuid . UUID ) ( consent * model . OAuth2ConsentSession , err error ) {
consent = & model . OAuth2ConsentSession { }
if err = p . db . GetContext ( ctx , consent , p . sqlSelectOAuth2ConsentSessionByChallengeID , challengeID ) ; err != nil {
return nil , fmt . Errorf ( "error selecting oauth2 consent session with challenge id '%s': %w" , challengeID . String ( ) , err )
}
return consent , nil
}
// LoadOAuth2ConsentSessionsPreConfigured returns an OAuth2.0 consents that are pre-configured given the consent signature.
func ( p * SQLProvider ) LoadOAuth2ConsentSessionsPreConfigured ( ctx context . Context , clientID string , subject uuid . UUID ) ( rows * ConsentSessionRows , err error ) {
var r * sqlx . Rows
if r , err = p . db . QueryxContext ( ctx , p . sqlSelectOAuth2ConsentSessionsPreConfigured , clientID , subject ) ; err != nil {
if errors . Is ( err , sql . ErrNoRows ) {
return & ConsentSessionRows { } , nil
}
return & ConsentSessionRows { } , fmt . Errorf ( "error selecting oauth2 consent session by signature with client id '%s' and subject '%s': %w" , clientID , subject . String ( ) , err )
}
return & ConsentSessionRows { rows : r } , nil
}
// SaveOAuth2Session saves a OAuth2Session to the database.
func ( p * SQLProvider ) SaveOAuth2Session ( ctx context . Context , sessionType OAuth2SessionType , session model . OAuth2Session ) ( err error ) {
var query string
switch sessionType {
case OAuth2SessionTypeAuthorizeCode :
query = p . sqlInsertOAuth2AuthorizeCodeSession
case OAuth2SessionTypeAccessToken :
query = p . sqlInsertOAuth2AccessTokenSession
case OAuth2SessionTypeRefreshToken :
query = p . sqlInsertOAuth2RefreshTokenSession
case OAuth2SessionTypePKCEChallenge :
query = p . sqlInsertOAuth2PKCERequestSession
case OAuth2SessionTypeOpenIDConnect :
query = p . sqlInsertOAuth2OpenIDConnectSession
default :
return fmt . Errorf ( "error inserting oauth2 session for subject '%s' and request id '%s': unknown oauth2 session type '%s'" , session . Subject , session . RequestID , sessionType )
}
if session . Session , err = p . encrypt ( session . Session ) ; err != nil {
return fmt . Errorf ( "error encrypting the oauth2 %s session data for subject '%s' and request id '%s' and challenge id '%s': %w" , sessionType , session . Subject , session . RequestID , session . ChallengeID . String ( ) , err )
}
_ , err = p . db . ExecContext ( ctx , query ,
session . ChallengeID , session . RequestID , session . ClientID , session . Signature ,
session . Subject , session . RequestedAt , session . RequestedScopes , session . GrantedScopes ,
session . RequestedAudience , session . GrantedAudience ,
session . Active , session . Revoked , session . Form , session . Session )
if err != nil {
return fmt . Errorf ( "error inserting oauth2 %s session data for subject '%s' and request id '%s' and challenge id '%s': %w" , sessionType , session . Subject , session . RequestID , session . ChallengeID . String ( ) , err )
}
return nil
}
// RevokeOAuth2Session marks a OAuth2Session as revoked in the database.
func ( p * SQLProvider ) RevokeOAuth2Session ( ctx context . Context , sessionType OAuth2SessionType , signature string ) ( err error ) {
var query string
switch sessionType {
case OAuth2SessionTypeAuthorizeCode :
query = p . sqlRevokeOAuth2AuthorizeCodeSession
case OAuth2SessionTypeAccessToken :
query = p . sqlRevokeOAuth2AccessTokenSession
case OAuth2SessionTypeRefreshToken :
query = p . sqlRevokeOAuth2RefreshTokenSession
case OAuth2SessionTypePKCEChallenge :
query = p . sqlRevokeOAuth2PKCERequestSession
case OAuth2SessionTypeOpenIDConnect :
query = p . sqlRevokeOAuth2OpenIDConnectSession
default :
return fmt . Errorf ( "error revoking oauth2 session with signature '%s': unknown oauth2 session type '%s'" , signature , sessionType )
}
if _ , err = p . db . ExecContext ( ctx , query , signature ) ; err != nil {
return fmt . Errorf ( "error revoking oauth2 %s session with signature '%s': %w" , sessionType , signature , err )
}
return nil
}
// RevokeOAuth2SessionByRequestID marks a OAuth2Session as revoked in the database.
func ( p * SQLProvider ) RevokeOAuth2SessionByRequestID ( ctx context . Context , sessionType OAuth2SessionType , requestID string ) ( err error ) {
var query string
switch sessionType {
case OAuth2SessionTypeAuthorizeCode :
query = p . sqlRevokeOAuth2AuthorizeCodeSessionByRequestID
case OAuth2SessionTypeAccessToken :
query = p . sqlRevokeOAuth2AccessTokenSessionByRequestID
case OAuth2SessionTypeRefreshToken :
query = p . sqlRevokeOAuth2RefreshTokenSessionByRequestID
case OAuth2SessionTypePKCEChallenge :
query = p . sqlRevokeOAuth2PKCERequestSessionByRequestID
case OAuth2SessionTypeOpenIDConnect :
query = p . sqlRevokeOAuth2OpenIDConnectSessionByRequestID
default :
return fmt . Errorf ( "error revoking oauth2 session with request id '%s': unknown oauth2 session type '%s'" , requestID , sessionType )
}
if _ , err = p . db . ExecContext ( ctx , query , requestID ) ; err != nil {
return fmt . Errorf ( "error revoking oauth2 %s session with request id '%s': %w" , sessionType , requestID , err )
}
return nil
}
// DeactivateOAuth2Session marks a OAuth2Session as inactive in the database.
func ( p * SQLProvider ) DeactivateOAuth2Session ( ctx context . Context , sessionType OAuth2SessionType , signature string ) ( err error ) {
var query string
switch sessionType {
case OAuth2SessionTypeAuthorizeCode :
query = p . sqlDeactivateOAuth2AuthorizeCodeSession
case OAuth2SessionTypeAccessToken :
query = p . sqlDeactivateOAuth2AccessTokenSession
case OAuth2SessionTypeRefreshToken :
query = p . sqlDeactivateOAuth2RefreshTokenSession
case OAuth2SessionTypePKCEChallenge :
query = p . sqlDeactivateOAuth2PKCERequestSession
case OAuth2SessionTypeOpenIDConnect :
query = p . sqlDeactivateOAuth2OpenIDConnectSession
default :
return fmt . Errorf ( "error deactivating oauth2 session with signature '%s': unknown oauth2 session type '%s'" , signature , sessionType )
}
if _ , err = p . db . ExecContext ( ctx , query , signature ) ; err != nil {
return fmt . Errorf ( "error deactivating oauth2 %s session with signature '%s': %w" , sessionType , signature , err )
}
return nil
}
// DeactivateOAuth2SessionByRequestID marks a OAuth2Session as inactive in the database.
func ( p * SQLProvider ) DeactivateOAuth2SessionByRequestID ( ctx context . Context , sessionType OAuth2SessionType , requestID string ) ( err error ) {
var query string
switch sessionType {
case OAuth2SessionTypeAuthorizeCode :
query = p . sqlDeactivateOAuth2AuthorizeCodeSession
case OAuth2SessionTypeAccessToken :
query = p . sqlDeactivateOAuth2AccessTokenSessionByRequestID
case OAuth2SessionTypeRefreshToken :
query = p . sqlDeactivateOAuth2RefreshTokenSessionByRequestID
case OAuth2SessionTypePKCEChallenge :
query = p . sqlDeactivateOAuth2PKCERequestSessionByRequestID
case OAuth2SessionTypeOpenIDConnect :
query = p . sqlDeactivateOAuth2OpenIDConnectSessionByRequestID
default :
return fmt . Errorf ( "error deactivating oauth2 session with request id '%s': unknown oauth2 session type '%s'" , requestID , sessionType )
}
if _ , err = p . db . ExecContext ( ctx , query , requestID ) ; err != nil {
return fmt . Errorf ( "error deactivating oauth2 %s session with request id '%s': %w" , sessionType , requestID , err )
}
return nil
}
// LoadOAuth2Session saves a OAuth2Session from the database.
func ( p * SQLProvider ) LoadOAuth2Session ( ctx context . Context , sessionType OAuth2SessionType , signature string ) ( session * model . OAuth2Session , err error ) {
var query string
switch sessionType {
case OAuth2SessionTypeAuthorizeCode :
query = p . sqlSelectOAuth2AuthorizeCodeSession
case OAuth2SessionTypeAccessToken :
query = p . sqlSelectOAuth2AccessTokenSession
case OAuth2SessionTypeRefreshToken :
query = p . sqlSelectOAuth2RefreshTokenSession
case OAuth2SessionTypePKCEChallenge :
query = p . sqlSelectOAuth2PKCERequestSession
case OAuth2SessionTypeOpenIDConnect :
query = p . sqlSelectOAuth2OpenIDConnectSession
default :
return nil , fmt . Errorf ( "error selecting oauth2 session: unknown oauth2 session type '%s'" , sessionType )
}
session = & model . OAuth2Session { }
if err = p . db . GetContext ( ctx , session , query , signature ) ; err != nil {
return nil , fmt . Errorf ( "error selecting oauth2 %s session with signature '%s': %w" , sessionType , signature , err )
}
if session . Session , err = p . decrypt ( session . Session ) ; err != nil {
return nil , fmt . Errorf ( "error decrypting the oauth2 %s session data with signature '%s' for subject '%s' and request id '%s': %w" , sessionType , signature , session . Subject , session . RequestID , err )
}
return session , nil
}
// SaveOAuth2BlacklistedJTI saves a OAuth2BlacklistedJTI to the database.
func ( p * SQLProvider ) SaveOAuth2BlacklistedJTI ( ctx context . Context , blacklistedJTI model . OAuth2BlacklistedJTI ) ( err error ) {
if _ , err = p . db . ExecContext ( ctx , p . sqlUpsertOAuth2BlacklistedJTI , blacklistedJTI . Signature , blacklistedJTI . ExpiresAt ) ; err != nil {
return fmt . Errorf ( "error inserting oauth2 blacklisted JTI with signature '%s': %w" , blacklistedJTI . Signature , err )
}
return nil
}
// LoadOAuth2BlacklistedJTI loads a OAuth2BlacklistedJTI from the database.
func ( p * SQLProvider ) LoadOAuth2BlacklistedJTI ( ctx context . Context , signature string ) ( blacklistedJTI * model . OAuth2BlacklistedJTI , err error ) {
blacklistedJTI = & model . OAuth2BlacklistedJTI { }
if err = p . db . GetContext ( ctx , blacklistedJTI , p . sqlSelectOAuth2BlacklistedJTI , signature ) ; err != nil {
return nil , fmt . Errorf ( "error selecting oauth2 blacklisted JTI with signature '%s': %w" , blacklistedJTI . Signature , err )
}
return blacklistedJTI , nil
}
2021-11-23 16:45:38 +07:00
// SavePreferred2FAMethod save the preferred method for 2FA to the database.
func ( p * SQLProvider ) SavePreferred2FAMethod ( ctx context . Context , username string , method string ) ( err error ) {
2022-03-03 18:20:43 +07:00
if _ , err = p . db . ExecContext ( ctx , p . sqlUpsertPreferred2FAMethod , username , method ) ; err != nil {
return fmt . Errorf ( "error upserting preferred two factor method for user '%s': %w" , username , err )
}
2020-07-16 12:56:08 +07:00
2022-03-03 18:20:43 +07:00
return nil
2020-07-16 12:56:08 +07:00
}
// LoadPreferred2FAMethod load the preferred method for 2FA from the database.
2021-11-23 16:45:38 +07:00
func ( p * SQLProvider ) LoadPreferred2FAMethod ( ctx context . Context , username string ) ( method string , err error ) {
err = p . db . GetContext ( ctx , & method , p . sqlSelectPreferred2FAMethod , username )
2020-05-06 02:35:32 +07:00
2021-11-25 08:56:58 +07:00
switch {
case err == nil :
return method , nil
case errors . Is ( err , sql . ErrNoRows ) :
2022-03-28 08:26:30 +07:00
return "" , sql . ErrNoRows
2021-11-23 16:45:38 +07:00
default :
2021-11-25 08:56:58 +07:00
return "" , fmt . Errorf ( "error selecting preferred two factor method for user '%s': %w" , username , err )
2019-11-16 17:38:21 +07:00
}
2021-11-23 16:45:38 +07:00
}
2020-05-06 02:35:32 +07:00
2022-03-06 12:47:40 +07:00
// LoadUserInfo loads the model.UserInfo from the database.
func ( p * SQLProvider ) LoadUserInfo ( ctx context . Context , username string ) ( info model . UserInfo , err error ) {
2021-12-01 10:32:58 +07:00
err = p . db . GetContext ( ctx , & info , p . sqlSelectUserInfo , username , username , username , username )
2020-05-06 02:35:32 +07:00
2021-11-23 16:45:38 +07:00
switch {
2022-03-28 08:26:30 +07:00
case err == nil , errors . Is ( err , sql . ErrNoRows ) :
2021-11-23 16:45:38 +07:00
return info , nil
default :
2022-03-06 12:47:40 +07:00
return model . UserInfo { } , fmt . Errorf ( "error selecting user info for user '%s': %w" , username , err )
2021-11-23 16:45:38 +07:00
}
2019-11-16 17:38:21 +07:00
}
2021-11-23 16:45:38 +07:00
// SaveIdentityVerification save an identity verification record to the database.
2022-03-06 12:47:40 +07:00
func ( p * SQLProvider ) SaveIdentityVerification ( ctx context . Context , verification model . IdentityVerification ) ( err error ) {
2021-11-30 13:58:21 +07:00
if _ , err = p . db . ExecContext ( ctx , p . sqlInsertIdentityVerification ,
2021-12-03 07:04:11 +07:00
verification . JTI , verification . IssuedAt , verification . IssuedIP , verification . ExpiresAt ,
2021-11-30 13:58:21 +07:00
verification . Username , verification . Action ) ; err != nil {
2022-03-03 18:20:43 +07:00
return fmt . Errorf ( "error inserting identity verification for user '%s' with uuid '%s': %w" , verification . Username , verification . JTI , err )
2021-11-25 08:56:58 +07:00
}
2021-11-23 16:45:38 +07:00
2021-11-25 08:56:58 +07:00
return nil
2019-11-16 17:38:21 +07:00
}
2021-12-03 07:04:11 +07:00
// ConsumeIdentityVerification marks an identity verification record in the database as consumed.
2022-03-06 12:47:40 +07:00
func ( p * SQLProvider ) ConsumeIdentityVerification ( ctx context . Context , jti string , ip model . NullIP ) ( err error ) {
2021-12-03 07:04:11 +07:00
if _ , err = p . db . ExecContext ( ctx , p . sqlConsumeIdentityVerification , ip , jti ) ; err != nil {
2021-11-25 08:56:58 +07:00
return fmt . Errorf ( "error updating identity verification: %w" , err )
}
2021-11-23 16:45:38 +07:00
2021-11-25 08:56:58 +07:00
return nil
2021-11-23 16:45:38 +07:00
}
2020-05-06 02:35:32 +07:00
2021-11-23 16:45:38 +07:00
// FindIdentityVerification checks if an identity verification record is in the database and active.
2021-11-30 13:58:21 +07:00
func ( p * SQLProvider ) FindIdentityVerification ( ctx context . Context , jti string ) ( found bool , err error ) {
2022-03-06 12:47:40 +07:00
verification := model . IdentityVerification { }
2021-12-04 11:34:20 +07:00
if err = p . db . GetContext ( ctx , & verification , p . sqlSelectIdentityVerification , jti ) ; err != nil {
if errors . Is ( err , sql . ErrNoRows ) {
return false , nil
}
2021-11-25 08:56:58 +07:00
return false , fmt . Errorf ( "error selecting identity verification exists: %w" , err )
2019-11-16 17:38:21 +07:00
}
2020-05-06 02:35:32 +07:00
2021-12-04 11:34:20 +07:00
switch {
2022-03-02 12:33:47 +07:00
case verification . Consumed != nil :
return false , fmt . Errorf ( "the token has already been consumed" )
case verification . ExpiresAt . Before ( time . Now ( ) ) :
return false , fmt . Errorf ( "the token expired %s ago" , time . Since ( verification . ExpiresAt ) )
2021-12-04 11:34:20 +07:00
default :
return true , nil
}
2019-11-16 17:38:21 +07:00
}
2021-11-25 08:56:58 +07:00
// SaveTOTPConfiguration save a TOTP configuration of a given user in the database.
2022-03-06 12:47:40 +07:00
func ( p * SQLProvider ) SaveTOTPConfiguration ( ctx context . Context , config model . TOTPConfiguration ) ( err error ) {
2021-11-25 08:56:58 +07:00
if config . Secret , err = p . encrypt ( config . Secret ) ; err != nil {
2022-03-03 18:20:43 +07:00
return fmt . Errorf ( "error encrypting the TOTP configuration secret for user '%s': %w" , config . Username , err )
2021-11-25 08:56:58 +07:00
}
2019-11-16 17:38:21 +07:00
2021-11-25 08:56:58 +07:00
if _ , err = p . db . ExecContext ( ctx , p . sqlUpsertTOTPConfig ,
2022-03-03 18:20:43 +07:00
config . CreatedAt , config . LastUsedAt ,
config . Username , config . Issuer ,
config . Algorithm , config . Digits , config . Period , config . Secret ) ; err != nil {
return fmt . Errorf ( "error upserting TOTP configuration for user '%s': %w" , config . Username , err )
}
return nil
}
// UpdateTOTPConfigurationSignIn updates a registered Webauthn devices sign in information.
func ( p * SQLProvider ) UpdateTOTPConfigurationSignIn ( ctx context . Context , id int , lastUsedAt * time . Time ) ( err error ) {
if _ , err = p . db . ExecContext ( ctx , p . sqlUpdateTOTPConfigRecordSignIn , lastUsedAt , id ) ; err != nil {
return fmt . Errorf ( "error updating TOTP configuration id %d: %w" , id , err )
2021-11-25 08:56:58 +07:00
}
return nil
2019-11-16 17:38:21 +07:00
}
2021-11-25 08:56:58 +07:00
// DeleteTOTPConfiguration delete a TOTP configuration from the database given a username.
2021-11-23 16:45:38 +07:00
func ( p * SQLProvider ) DeleteTOTPConfiguration ( ctx context . Context , username string ) ( err error ) {
2021-11-25 08:56:58 +07:00
if _ , err = p . db . ExecContext ( ctx , p . sqlDeleteTOTPConfig , username ) ; err != nil {
2022-03-03 18:20:43 +07:00
return fmt . Errorf ( "error deleting TOTP configuration for user '%s': %w" , username , err )
2021-11-25 08:56:58 +07:00
}
2021-11-23 16:45:38 +07:00
2021-11-25 08:56:58 +07:00
return nil
2019-11-16 17:38:21 +07:00
}
2021-11-25 08:56:58 +07:00
// LoadTOTPConfiguration load a TOTP configuration given a username from the database.
2022-03-06 12:47:40 +07:00
func ( p * SQLProvider ) LoadTOTPConfiguration ( ctx context . Context , username string ) ( config * model . TOTPConfiguration , err error ) {
config = & model . TOTPConfiguration { }
2021-11-23 16:45:38 +07:00
2021-11-25 08:56:58 +07:00
if err = p . db . QueryRowxContext ( ctx , p . sqlSelectTOTPConfig , username ) . StructScan ( config ) ; err != nil {
if errors . Is ( err , sql . ErrNoRows ) {
2021-12-01 19:11:29 +07:00
return nil , ErrNoTOTPConfiguration
2019-11-16 17:38:21 +07:00
}
2020-05-06 02:35:32 +07:00
2022-03-03 18:20:43 +07:00
return nil , fmt . Errorf ( "error selecting TOTP configuration for user '%s': %w" , username , err )
2021-11-25 08:56:58 +07:00
}
if config . Secret , err = p . decrypt ( config . Secret ) ; err != nil {
2022-03-03 18:20:43 +07:00
return nil , fmt . Errorf ( "error decrypting the TOTP secret for user '%s': %w" , username , err )
2019-11-16 17:38:21 +07:00
}
2020-05-06 02:35:32 +07:00
2021-11-23 16:45:38 +07:00
return config , nil
2019-11-16 17:38:21 +07:00
}
2021-11-25 08:56:58 +07:00
// LoadTOTPConfigurations load a set of TOTP configurations.
2022-03-06 12:47:40 +07:00
func ( p * SQLProvider ) LoadTOTPConfigurations ( ctx context . Context , limit , page int ) ( configs [ ] model . TOTPConfiguration , err error ) {
configs = make ( [ ] model . TOTPConfiguration , 0 , limit )
2022-03-03 18:20:43 +07:00
if err = p . db . SelectContext ( ctx , & configs , p . sqlSelectTOTPConfigs , limit , limit * page ) ; err != nil {
2021-11-25 08:56:58 +07:00
if errors . Is ( err , sql . ErrNoRows ) {
2022-03-03 18:20:43 +07:00
return nil , nil
2021-11-25 08:56:58 +07:00
}
return nil , fmt . Errorf ( "error selecting TOTP configurations: %w" , err )
}
2022-03-03 18:20:43 +07:00
for i , c := range configs {
if configs [ i ] . Secret , err = p . decrypt ( c . Secret ) ; err != nil {
return nil , fmt . Errorf ( "error decrypting TOTP configuration for user '%s': %w" , c . Username , err )
2021-11-25 08:56:58 +07:00
}
}
return configs , nil
}
2022-03-06 12:47:40 +07:00
func ( p * SQLProvider ) updateTOTPConfigurationSecret ( ctx context . Context , config model . TOTPConfiguration ) ( err error ) {
2021-11-25 08:56:58 +07:00
switch config . ID {
case 0 :
_ , err = p . db . ExecContext ( ctx , p . sqlUpdateTOTPConfigSecretByUsername , config . Secret , config . Username )
default :
_ , err = p . db . ExecContext ( ctx , p . sqlUpdateTOTPConfigSecret , config . Secret , config . ID )
}
if err != nil {
2022-03-03 18:20:43 +07:00
return fmt . Errorf ( "error updating TOTP configuration secret for user '%s': %w" , config . Username , err )
2021-11-25 08:56:58 +07:00
}
return nil
}
2022-03-03 18:20:43 +07:00
// SaveWebauthnDevice saves a registered Webauthn device.
2022-03-06 12:47:40 +07:00
func ( p * SQLProvider ) SaveWebauthnDevice ( ctx context . Context , device model . WebauthnDevice ) ( err error ) {
2021-12-03 07:04:11 +07:00
if device . PublicKey , err = p . encrypt ( device . PublicKey ) ; err != nil {
2022-03-03 18:20:43 +07:00
return fmt . Errorf ( "error encrypting the Webauthn device public key for user '%s' kid '%x': %w" , device . Username , device . KID , err )
2021-12-03 07:04:11 +07:00
}
2022-03-03 18:20:43 +07:00
if _ , err = p . db . ExecContext ( ctx , p . sqlUpsertWebauthnDevice ,
device . CreatedAt , device . LastUsedAt ,
device . RPID , device . Username , device . Description ,
device . KID , device . PublicKey ,
device . AttestationType , device . Transport , device . AAGUID , device . SignCount , device . CloneWarning ,
) ; err != nil {
return fmt . Errorf ( "error upserting Webauthn device for user '%s' kid '%x': %w" , device . Username , device . KID , err )
2021-11-25 08:56:58 +07:00
}
2020-05-06 02:35:32 +07:00
2021-11-25 08:56:58 +07:00
return nil
2019-11-16 17:38:21 +07:00
}
2022-03-03 18:20:43 +07:00
// UpdateWebauthnDeviceSignIn updates a registered Webauthn devices sign in information.
func ( p * SQLProvider ) UpdateWebauthnDeviceSignIn ( ctx context . Context , id int , rpid string , lastUsedAt * time . Time , signCount uint32 , cloneWarning bool ) ( err error ) {
if _ , err = p . db . ExecContext ( ctx , p . sqlUpdateWebauthnDeviceRecordSignIn , rpid , lastUsedAt , signCount , cloneWarning , id ) ; err != nil {
return fmt . Errorf ( "error updating Webauthn signin metadata for id '%x': %w" , id , err )
}
2019-11-17 08:05:46 +07:00
2022-03-03 18:20:43 +07:00
return nil
}
// LoadWebauthnDevices loads Webauthn device registrations.
2022-03-06 12:47:40 +07:00
func ( p * SQLProvider ) LoadWebauthnDevices ( ctx context . Context , limit , page int ) ( devices [ ] model . WebauthnDevice , err error ) {
devices = make ( [ ] model . WebauthnDevice , 0 , limit )
2022-03-03 18:20:43 +07:00
if err = p . db . SelectContext ( ctx , & devices , p . sqlSelectWebauthnDevices , limit , limit * page ) ; err != nil {
2021-11-25 08:56:58 +07:00
if errors . Is ( err , sql . ErrNoRows ) {
2022-03-03 18:20:43 +07:00
return nil , nil
2021-11-23 16:45:38 +07:00
}
2019-11-17 08:05:46 +07:00
2022-03-03 18:20:43 +07:00
return nil , fmt . Errorf ( "error selecting Webauthn devices: %w" , err )
2019-11-16 17:38:21 +07:00
}
2019-11-17 02:50:58 +07:00
2022-03-03 18:20:43 +07:00
for i , device := range devices {
if devices [ i ] . PublicKey , err = p . decrypt ( device . PublicKey ) ; err != nil {
return nil , fmt . Errorf ( "error decrypting Webauthn public key for user '%s': %w" , device . Username , err )
}
2021-12-03 07:04:11 +07:00
}
2022-03-03 18:20:43 +07:00
return devices , nil
2019-11-16 17:38:21 +07:00
}
2022-03-03 18:20:43 +07:00
// LoadWebauthnDevicesByUsername loads all webauthn devices registration for a given username.
2022-03-06 12:47:40 +07:00
func ( p * SQLProvider ) LoadWebauthnDevicesByUsername ( ctx context . Context , username string ) ( devices [ ] model . WebauthnDevice , err error ) {
2022-03-03 18:20:43 +07:00
if err = p . db . SelectContext ( ctx , & devices , p . sqlSelectWebauthnDevicesByUsername , username ) ; err != nil {
2021-12-03 07:04:11 +07:00
if errors . Is ( err , sql . ErrNoRows ) {
2022-03-03 18:20:43 +07:00
return nil , ErrNoWebauthnDevice
2021-12-03 07:04:11 +07:00
}
2022-03-03 18:20:43 +07:00
return nil , fmt . Errorf ( "error selecting Webauthn devices for user '%s': %w" , username , err )
2021-12-03 07:04:11 +07:00
}
2022-03-03 18:20:43 +07:00
for i , device := range devices {
if devices [ i ] . PublicKey , err = p . decrypt ( device . PublicKey ) ; err != nil {
return nil , fmt . Errorf ( "error decrypting Webauthn public key for user '%s': %w" , username , err )
2021-12-03 07:04:11 +07:00
}
}
return devices , nil
}
2022-03-06 12:47:40 +07:00
func ( p * SQLProvider ) updateWebauthnDevicePublicKey ( ctx context . Context , device model . WebauthnDevice ) ( err error ) {
2021-12-03 07:04:11 +07:00
switch device . ID {
case 0 :
2022-03-03 18:20:43 +07:00
_ , err = p . db . ExecContext ( ctx , p . sqlUpdateWebauthnDevicePublicKeyByUsername , device . PublicKey , device . Username , device . KID )
2021-12-03 07:04:11 +07:00
default :
2022-03-03 18:20:43 +07:00
_ , err = p . db . ExecContext ( ctx , p . sqlUpdateWebauthnDevicePublicKey , device . PublicKey , device . ID )
2021-12-03 07:04:11 +07:00
}
if err != nil {
2022-03-03 18:20:43 +07:00
return fmt . Errorf ( "error updating Webauthn public key for user '%s' kid '%x': %w" , device . Username , device . KID , err )
2021-12-03 07:04:11 +07:00
}
return nil
}
2021-12-01 10:32:58 +07:00
// SavePreferredDuoDevice saves a Duo device.
2022-03-06 12:47:40 +07:00
func ( p * SQLProvider ) SavePreferredDuoDevice ( ctx context . Context , device model . DuoDevice ) ( err error ) {
2022-03-03 18:20:43 +07:00
if _ , err = p . db . ExecContext ( ctx , p . sqlUpsertDuoDevice , device . Username , device . Device , device . Method ) ; err != nil {
return fmt . Errorf ( "error upserting preferred duo device for user '%s': %w" , device . Username , err )
}
return nil
2021-12-01 10:32:58 +07:00
}
// DeletePreferredDuoDevice deletes a Duo device of a given user.
func ( p * SQLProvider ) DeletePreferredDuoDevice ( ctx context . Context , username string ) ( err error ) {
2022-03-03 18:20:43 +07:00
if _ , err = p . db . ExecContext ( ctx , p . sqlDeleteDuoDevice , username ) ; err != nil {
return fmt . Errorf ( "error deleting preferred duo device for user '%s': %w" , username , err )
}
return nil
2021-12-01 10:32:58 +07:00
}
// LoadPreferredDuoDevice loads a Duo device of a given user.
2022-03-06 12:47:40 +07:00
func ( p * SQLProvider ) LoadPreferredDuoDevice ( ctx context . Context , username string ) ( device * model . DuoDevice , err error ) {
device = & model . DuoDevice { }
2021-12-01 10:32:58 +07:00
2022-03-03 18:20:43 +07:00
if err = p . db . QueryRowxContext ( ctx , p . sqlSelectDuoDevice , username ) . StructScan ( device ) ; err != nil {
2021-12-01 10:32:58 +07:00
if err == sql . ErrNoRows {
return nil , ErrNoDuoDevice
}
2022-03-03 18:20:43 +07:00
return nil , fmt . Errorf ( "error selecting preferred duo device for user '%s': %w" , username , err )
2021-12-01 10:32:58 +07:00
}
return device , nil
}
2019-11-16 17:38:21 +07:00
// AppendAuthenticationLog append a mark to the authentication log.
2022-03-06 12:47:40 +07:00
func ( p * SQLProvider ) AppendAuthenticationLog ( ctx context . Context , attempt model . AuthenticationAttempt ) ( err error ) {
2021-11-29 10:09:14 +07:00
if _ , err = p . db . ExecContext ( ctx , p . sqlInsertAuthenticationAttempt ,
attempt . Time , attempt . Successful , attempt . Banned , attempt . Username ,
attempt . Type , attempt . RemoteIP , attempt . RequestURI , attempt . RequestMethod ) ; err != nil {
2022-03-03 18:20:43 +07:00
return fmt . Errorf ( "error inserting authentication attempt for user '%s': %w" , attempt . Username , err )
2021-11-25 08:56:58 +07:00
}
return nil
2019-11-16 17:38:21 +07:00
}
2021-11-23 16:45:38 +07:00
// LoadAuthenticationLogs retrieve the latest failed authentications from the authentication log.
2022-03-06 12:47:40 +07:00
func ( p * SQLProvider ) LoadAuthenticationLogs ( ctx context . Context , username string , fromDate time . Time , limit , page int ) ( attempts [ ] model . AuthenticationAttempt , err error ) {
attempts = make ( [ ] model . AuthenticationAttempt , 0 , limit )
2020-05-06 02:35:32 +07:00
2022-03-03 18:20:43 +07:00
if err = p . db . SelectContext ( ctx , & attempts , p . sqlSelectAuthenticationAttemptsByUsername , fromDate , username , limit , limit * page ) ; err != nil {
if errors . Is ( err , sql . ErrNoRows ) {
return nil , ErrNoAuthenticationLogs
2019-11-16 17:38:21 +07:00
}
2020-05-06 02:35:32 +07:00
2022-03-03 18:20:43 +07:00
return nil , fmt . Errorf ( "error selecting authentication logs for user '%s': %w" , username , err )
2019-11-16 17:38:21 +07:00
}
2020-05-06 02:35:32 +07:00
2019-11-16 17:38:21 +07:00
return attempts , nil
}