2021-11-23 09:45:38 +00:00
package commands
import (
2022-12-23 04:00:23 +00:00
"bytes"
2022-04-25 08:49:18 +00:00
"database/sql"
2021-11-23 09:45:38 +00:00
"errors"
"fmt"
2022-03-02 07:50:36 +00:00
"image"
"image/png"
2021-11-23 09:45:38 +00:00
"os"
2022-03-02 07:50:36 +00:00
"path/filepath"
2022-11-25 12:44:55 +00:00
"sort"
2021-11-23 09:45:38 +00:00
"strings"
2022-04-09 07:13:19 +00:00
"github.com/google/uuid"
2021-11-23 09:45:38 +00:00
"github.com/spf13/cobra"
2022-04-09 07:13:19 +00:00
"gopkg.in/yaml.v3"
2021-11-23 09:45:38 +00:00
"github.com/authelia/authelia/v4/internal/configuration/validator"
2022-03-06 05:47:40 +00:00
"github.com/authelia/authelia/v4/internal/model"
2023-01-07 00:19:41 +00:00
"github.com/authelia/authelia/v4/internal/random"
2021-11-23 09:45:38 +00:00
"github.com/authelia/authelia/v4/internal/storage"
2021-12-01 12:11:29 +00:00
"github.com/authelia/authelia/v4/internal/totp"
2022-03-02 07:50:36 +00:00
"github.com/authelia/authelia/v4/internal/utils"
2021-11-23 09:45:38 +00:00
)
2022-12-22 00:21:29 +00:00
// LoadProvidersStorageRunE is a special PreRunE that loads the storage provider into the CmdCtx.
func ( ctx * CmdCtx ) LoadProvidersStorageRunE ( cmd * cobra . Command , args [ ] string ) ( err error ) {
switch warns , errs := ctx . LoadTrustedCertificates ( ) ; {
case len ( errs ) != 0 :
err = fmt . Errorf ( "had the following errors loading the trusted certificates" )
for _ , e := range errs {
err = fmt . Errorf ( "%+v: %w" , err , e )
}
2022-03-02 07:50:36 +00:00
2021-11-23 09:45:38 +00:00
return err
2022-12-22 00:21:29 +00:00
case len ( warns ) != 0 :
err = fmt . Errorf ( "had the following warnings loading the trusted certificates" )
2021-11-23 09:45:38 +00:00
2022-12-22 00:21:29 +00:00
for _ , e := range errs {
err = fmt . Errorf ( "%+v: %w" , err , e )
}
2021-11-23 09:45:38 +00:00
2022-12-22 00:21:29 +00:00
return err
default :
ctx . providers . StorageProvider = getStorageProvider ( ctx )
2021-11-23 09:45:38 +00:00
2022-12-22 00:21:29 +00:00
return nil
2021-11-23 09:45:38 +00:00
}
2022-12-22 00:21:29 +00:00
}
2021-11-23 09:45:38 +00:00
2022-12-23 04:00:23 +00:00
// ConfigStorageCommandLineConfigRunE configures the storage command mapping.
func ( ctx * CmdCtx ) ConfigStorageCommandLineConfigRunE ( cmd * cobra . Command , _ [ ] string ) ( err error ) {
2022-12-22 00:21:29 +00:00
flagsMap := map [ string ] string {
2022-11-25 12:44:55 +00:00
cmdFlagNameEncryptionKey : "storage.encryption_key" ,
cmdFlagNameSQLite3Path : "storage.local.path" ,
cmdFlagNameMySQLHost : "storage.mysql.host" ,
cmdFlagNameMySQLPort : "storage.mysql.port" ,
cmdFlagNameMySQLDatabase : "storage.mysql.database" ,
cmdFlagNameMySQLUsername : "storage.mysql.username" ,
cmdFlagNameMySQLPassword : "storage.mysql.password" ,
cmdFlagNamePostgreSQLHost : "storage.postgres.host" ,
cmdFlagNamePostgreSQLPort : "storage.postgres.port" ,
cmdFlagNamePostgreSQLDatabase : "storage.postgres.database" ,
cmdFlagNamePostgreSQLSchema : "storage.postgres.schema" ,
cmdFlagNamePostgreSQLUsername : "storage.postgres.username" ,
cmdFlagNamePostgreSQLPassword : "storage.postgres.password" ,
2021-12-02 05:36:03 +00:00
"postgres.ssl.mode" : "storage.postgres.ssl.mode" ,
"postgres.ssl.root_certificate" : "storage.postgres.ssl.root_certificate" ,
"postgres.ssl.certificate" : "storage.postgres.ssl.certificate" ,
"postgres.ssl.key" : "storage.postgres.ssl.key" ,
2022-11-25 12:44:55 +00:00
cmdFlagNamePeriod : "totp.period" ,
cmdFlagNameDigits : "totp.digits" ,
cmdFlagNameAlgorithm : "totp.algorithm" ,
cmdFlagNameIssuer : "totp.issuer" ,
cmdFlagNameSecretSize : "totp.secret_size" ,
2021-11-23 09:45:38 +00:00
}
2022-12-22 00:21:29 +00:00
return ctx . ConfigSetFlagsMapRunE ( cmd . Flags ( ) , flagsMap , true , false )
}
2021-11-23 09:45:38 +00:00
2022-12-23 04:00:23 +00:00
// ConfigValidateStorageRunE validates the storage config before running commands using it.
func ( ctx * CmdCtx ) ConfigValidateStorageRunE ( _ * cobra . Command , _ [ ] string ) ( err error ) {
2022-12-22 00:21:29 +00:00
if errs := ctx . cconfig . validator . Errors ( ) ; len ( errs ) != 0 {
var (
i int
e error
)
2021-11-23 09:45:38 +00:00
2022-12-22 00:21:29 +00:00
for i , e = range errs {
2021-11-23 09:45:38 +00:00
if i == 0 {
2022-12-22 00:21:29 +00:00
err = e
2021-11-23 09:45:38 +00:00
continue
}
2022-12-22 00:21:29 +00:00
err = fmt . Errorf ( "%w, %v" , err , e )
2021-11-23 09:45:38 +00:00
}
2022-12-22 00:21:29 +00:00
return err
2021-11-23 09:45:38 +00:00
}
2022-12-22 00:21:29 +00:00
validator . ValidateStorage ( ctx . config . Storage , ctx . cconfig . validator )
2021-11-23 09:45:38 +00:00
2022-12-22 00:21:29 +00:00
validator . ValidateTOTP ( ctx . config , ctx . cconfig . validator )
2021-12-01 12:11:29 +00:00
2022-12-22 00:21:29 +00:00
if errs := ctx . cconfig . validator . Errors ( ) ; len ( errs ) != 0 {
var (
i int
e error
)
2021-11-23 09:45:38 +00:00
2022-12-22 00:21:29 +00:00
for i , e = range errs {
2021-11-23 09:45:38 +00:00
if i == 0 {
2022-12-22 00:21:29 +00:00
err = e
2021-11-23 09:45:38 +00:00
continue
}
2022-12-22 00:21:29 +00:00
err = fmt . Errorf ( "%w, %v" , err , e )
2021-11-23 09:45:38 +00:00
}
2022-12-22 00:21:29 +00:00
return err
2021-11-23 09:45:38 +00:00
}
return nil
}
2022-12-22 00:21:29 +00:00
func ( ctx * CmdCtx ) StorageSchemaEncryptionCheckRunE ( cmd * cobra . Command , args [ ] string ) ( err error ) {
2022-12-23 04:00:23 +00:00
defer func ( ) {
_ = ctx . providers . StorageProvider . Close ( )
} ( )
2021-11-25 01:56:58 +00:00
var (
2022-12-22 00:21:29 +00:00
verbose bool
result storage . EncryptionValidationResult
2021-11-25 01:56:58 +00:00
)
2022-12-22 00:21:29 +00:00
if err = ctx . CheckSchemaVersion ( ) ; err != nil {
return storageWrapCheckSchemaErr ( err )
}
2022-11-25 12:44:55 +00:00
if verbose , err = cmd . Flags ( ) . GetBool ( cmdFlagNameVerbose ) ; err != nil {
2021-11-25 01:56:58 +00:00
return err
}
2022-12-22 00:21:29 +00:00
if result , err = ctx . providers . StorageProvider . SchemaEncryptionCheckKey ( ctx , verbose ) ; err != nil {
2021-11-25 01:56:58 +00:00
switch {
case errors . Is ( err , storage . ErrSchemaEncryptionVersionUnsupported ) :
2022-11-25 12:44:55 +00:00
fmt . Printf ( "Storage Encryption Key Validation: FAILURE\n\n\tCause: The schema version doesn't support encryption.\n" )
2021-11-25 01:56:58 +00:00
default :
2022-11-25 12:44:55 +00:00
fmt . Printf ( "Storage Encryption Key Validation: UNKNOWN\n\n\tCause: %v.\n" , err )
2021-11-25 01:56:58 +00:00
}
} else {
2022-11-25 12:44:55 +00:00
if result . Success ( ) {
fmt . Println ( "Storage Encryption Key Validation: SUCCESS" )
} else {
fmt . Printf ( "Storage Encryption Key Validation: FAILURE\n\n\tCause: %v.\n" , storage . ErrSchemaEncryptionInvalidKey )
}
if verbose {
fmt . Printf ( "\nTables:" )
tables := make ( [ ] string , 0 , len ( result . Tables ) )
for name := range result . Tables {
tables = append ( tables , name )
}
sort . Strings ( tables )
for _ , name := range tables {
table := result . Tables [ name ]
fmt . Printf ( "\n\n\tTable (%s): %s\n\t\tInvalid Rows: %d\n\t\tTotal Rows: %d" , name , table . ResultDescriptor ( ) , table . Invalid , table . Total )
}
fmt . Printf ( "\n" )
}
2021-11-25 01:56:58 +00:00
}
return nil
}
2022-12-22 00:21:29 +00:00
// StorageSchemaEncryptionChangeKeyRunE is the RunE for the authelia storage encryption change-key command.
func ( ctx * CmdCtx ) StorageSchemaEncryptionChangeKeyRunE ( cmd * cobra . Command , args [ ] string ) ( err error ) {
2022-12-23 04:00:23 +00:00
defer func ( ) {
_ = ctx . providers . StorageProvider . Close ( )
} ( )
2021-11-25 01:56:58 +00:00
var (
2022-12-22 00:21:29 +00:00
key string
version int
2021-11-25 01:56:58 +00:00
)
2022-12-23 04:00:23 +00:00
if err = ctx . CheckSchema ( ) ; err != nil {
2022-12-22 00:21:29 +00:00
return storageWrapCheckSchemaErr ( err )
2021-11-25 01:56:58 +00:00
}
2022-12-22 00:21:29 +00:00
if version , err = ctx . providers . StorageProvider . SchemaVersion ( ctx ) ; err != nil {
2021-11-25 01:56:58 +00:00
return err
}
if version <= 0 {
return errors . New ( "schema version must be at least version 1 to change the encryption key" )
}
2022-11-25 12:44:55 +00:00
useFlag := cmd . Flags ( ) . Changed ( cmdFlagNameNewEncryptionKey )
if useFlag {
if key , err = cmd . Flags ( ) . GetString ( cmdFlagNameNewEncryptionKey ) ; err != nil {
return err
}
}
if ! useFlag || key == "" {
2022-12-22 00:21:29 +00:00
if key , err = termReadPasswordWithPrompt ( "Enter New Storage Encryption Key: " , cmdFlagNameNewEncryptionKey ) ; err != nil {
2022-11-25 12:44:55 +00:00
return err
}
}
2021-11-25 01:56:58 +00:00
2022-03-02 07:50:36 +00:00
switch {
case key == "" :
2022-11-25 12:44:55 +00:00
return errors . New ( "the new encryption key must not be blank" )
2022-03-02 07:50:36 +00:00
case len ( key ) < 20 :
return errors . New ( "the new encryption key must be at least 20 characters" )
2021-11-25 01:56:58 +00:00
}
2022-12-22 00:21:29 +00:00
if err = ctx . providers . StorageProvider . SchemaEncryptionChangeKey ( ctx , key ) ; err != nil {
2021-11-25 01:56:58 +00:00
return err
}
fmt . Println ( "Completed the encryption key change. Please adjust your configuration to use the new key." )
return nil
}
2022-12-23 04:00:23 +00:00
// StorageMigrateHistoryRunE is the RunE for the authelia storage migrate history command.
func ( ctx * CmdCtx ) StorageMigrateHistoryRunE ( _ * cobra . Command , _ [ ] string ) ( err error ) {
defer func ( ) {
_ = ctx . providers . StorageProvider . Close ( )
} ( )
var (
version int
migrations [ ] model . Migration
)
if version , err = ctx . providers . StorageProvider . SchemaVersion ( ctx ) ; err != nil {
return err
}
if version <= 0 {
fmt . Println ( "No migration history is available for schemas that not version 1 or above." )
return
}
if migrations , err = ctx . providers . StorageProvider . SchemaMigrationHistory ( ctx ) ; err != nil {
return err
}
if len ( migrations ) == 0 {
return errors . New ( "no migration history found which may indicate a broken schema" )
}
fmt . Printf ( "Migration History:\n\nID\tDate\t\t\t\tBefore\tAfter\tAuthelia Version\n" )
for _ , m := range migrations {
fmt . Printf ( "%d\t%s\t%d\t%d\t%s\n" , m . ID , m . Applied . Format ( "2006-01-02 15:04:05 -0700" ) , m . Before , m . After , m . Version )
}
return nil
}
// NewStorageMigrateListRunE creates the RunE for the authelia storage migrate list command.
func ( ctx * CmdCtx ) NewStorageMigrateListRunE ( up bool ) func ( cmd * cobra . Command , args [ ] string ) ( err error ) {
return func ( cmd * cobra . Command , args [ ] string ) ( err error ) {
defer func ( ) {
_ = ctx . providers . StorageProvider . Close ( )
} ( )
var (
migrations [ ] model . SchemaMigration
directionStr string
)
if up {
migrations , err = ctx . providers . StorageProvider . SchemaMigrationsUp ( ctx , 0 )
directionStr = "Up"
} else {
migrations , err = ctx . providers . StorageProvider . SchemaMigrationsDown ( ctx , 0 )
directionStr = "Down"
}
if err != nil && ! errors . Is ( err , storage . ErrNoAvailableMigrations ) && ! errors . Is ( err , storage . ErrMigrateCurrentVersionSameAsTarget ) {
return err
}
if len ( migrations ) == 0 {
fmt . Printf ( "Storage Schema Migration List (%s)\n\nNo Migrations Available\n" , directionStr )
} else {
fmt . Printf ( "Storage Schema Migration List (%s)\n\nVersion\t\tDescription\n" , directionStr )
for _ , migration := range migrations {
fmt . Printf ( "%d\t\t%s\n" , migration . Version , migration . Name )
}
}
return nil
}
}
// NewStorageMigrationRunE creates the RunE for the authelia storage migrate command.
func ( ctx * CmdCtx ) NewStorageMigrationRunE ( up bool ) func ( cmd * cobra . Command , args [ ] string ) ( err error ) {
return func ( cmd * cobra . Command , args [ ] string ) ( err error ) {
defer func ( ) {
_ = ctx . providers . StorageProvider . Close ( )
} ( )
var (
target int
)
if target , err = cmd . Flags ( ) . GetInt ( cmdFlagNameTarget ) ; err != nil {
return err
}
switch {
case up :
switch cmd . Flags ( ) . Changed ( cmdFlagNameTarget ) {
case true :
return ctx . providers . StorageProvider . SchemaMigrate ( ctx , true , target )
default :
return ctx . providers . StorageProvider . SchemaMigrate ( ctx , true , storage . SchemaLatest )
}
default :
if ! cmd . Flags ( ) . Changed ( cmdFlagNameTarget ) {
return errors . New ( "you must set a target version" )
}
var confirmed bool
if confirmed , err = termReadConfirmation ( cmd . Flags ( ) , cmdFlagNameDestroyData , "Schema Down Migrations may DESTROY data, type 'DESTROY' and press return to continue: " , "DESTROY" ) ; err != nil {
return err
}
if ! confirmed {
return errors . New ( "cancelling down migration due to user not accepting data destruction" )
}
return ctx . providers . StorageProvider . SchemaMigrate ( ctx , false , target )
}
2022-10-19 07:17:55 +00:00
}
2022-12-23 04:00:23 +00:00
}
2022-10-19 07:17:55 +00:00
2022-12-23 04:00:23 +00:00
// StorageSchemaInfoRunE is the RunE for the authelia storage schema info command.
func ( ctx * CmdCtx ) StorageSchemaInfoRunE ( _ * cobra . Command , _ [ ] string ) ( err error ) {
2022-10-19 07:17:55 +00:00
defer func ( ) {
2022-12-22 00:21:29 +00:00
_ = ctx . providers . StorageProvider . Close ( )
2022-10-19 07:17:55 +00:00
} ( )
2022-12-23 04:00:23 +00:00
var (
upgradeStr , tablesStr string
tables [ ] string
version , latest int
)
if version , err = ctx . providers . StorageProvider . SchemaVersion ( ctx ) ; err != nil && err . Error ( ) != "unknown schema state" {
return err
}
if tables , err = ctx . providers . StorageProvider . SchemaTables ( ctx ) ; err != nil {
return err
}
if len ( tables ) == 0 {
tablesStr = "N/A"
} else {
tablesStr = strings . Join ( tables , ", " )
}
if latest , err = ctx . providers . StorageProvider . SchemaLatestVersion ( ) ; err != nil {
return err
}
if latest > version {
upgradeStr = fmt . Sprintf ( "yes - version %d" , latest )
} else {
upgradeStr = "no"
}
var (
encryption string
result storage . EncryptionValidationResult
)
switch result , err = ctx . providers . StorageProvider . SchemaEncryptionCheckKey ( ctx , false ) ; {
case err != nil :
if errors . Is ( err , storage . ErrSchemaEncryptionVersionUnsupported ) {
encryption = "unsupported (schema version)"
} else {
encryption = invalid
}
case ! result . Success ( ) :
encryption = invalid
default :
encryption = "valid"
}
fmt . Printf ( "Schema Version: %s\nSchema Upgrade Available: %s\nSchema Tables: %s\nSchema Encryption Key: %s\n" , storage . SchemaVersionToString ( version ) , upgradeStr , tablesStr , encryption )
return nil
}
func ( ctx * CmdCtx ) StorageUserWebauthnExportRunE ( cmd * cobra . Command , args [ ] string ) ( err error ) {
defer func ( ) {
_ = ctx . providers . StorageProvider . Close ( )
} ( )
if err = ctx . CheckSchema ( ) ; err != nil {
return storageWrapCheckSchemaErr ( err )
}
var (
filename string
)
if filename , err = cmd . Flags ( ) . GetString ( cmdFlagNameFile ) ; err != nil {
return err
}
switch _ , err = os . Stat ( filename ) ; {
case err == nil :
return fmt . Errorf ( "must specify a file that doesn't exist but '%s' exists" , filename )
case ! os . IsNotExist ( err ) :
return fmt . Errorf ( "error occurred opening '%s': %w" , filename , err )
}
limit := 10
count := 0
var (
devices [ ] model . WebauthnDevice
)
export := & model . WebauthnDeviceExport {
WebauthnDevices : nil ,
}
for page := 0 ; true ; page ++ {
if devices , err = ctx . providers . StorageProvider . LoadWebauthnDevices ( ctx , limit , page ) ; err != nil {
return err
}
export . WebauthnDevices = append ( export . WebauthnDevices , devices ... )
l := len ( devices )
count += l
if l < limit {
break
}
}
var data [ ] byte
if data , err = yaml . Marshal ( export ) ; err != nil {
return fmt . Errorf ( "error occurred marshalling data to YAML: %w" , err )
}
if err = os . WriteFile ( filename , data , 0600 ) ; err != nil {
return fmt . Errorf ( "error occurred writing to file '%s': %w" , filename , err )
}
fmt . Printf ( cliOutputFmtSuccessfulUserExportFile , count , "Webauthn devices" , "YAML" , filename )
return nil
}
func ( ctx * CmdCtx ) StorageUserWebauthnImportRunE ( cmd * cobra . Command , args [ ] string ) ( err error ) {
defer func ( ) {
_ = ctx . providers . StorageProvider . Close ( )
} ( )
var (
filename string
stat os . FileInfo
data [ ] byte
)
filename = args [ 0 ]
if stat , err = os . Stat ( filename ) ; err != nil {
return fmt . Errorf ( "must specify a filename that exists but '%s' had an error opening it: %w" , filename , err )
}
if stat . IsDir ( ) {
return fmt . Errorf ( "must specify a filename that exists but '%s' is a directory" , filename )
}
if data , err = os . ReadFile ( filename ) ; err != nil {
return err
}
export := & model . WebauthnDeviceExport { }
if err = yaml . Unmarshal ( data , export ) ; err != nil {
return err
}
if len ( export . WebauthnDevices ) == 0 {
return fmt . Errorf ( "can't import a YAML file without Webauthn devices data" )
}
if err = ctx . CheckSchema ( ) ; err != nil {
return storageWrapCheckSchemaErr ( err )
}
for _ , device := range export . WebauthnDevices {
if err = ctx . providers . StorageProvider . SaveWebauthnDevice ( ctx , device ) ; err != nil {
return err
}
}
fmt . Printf ( cliOutputFmtSuccessfulUserImportFile , len ( export . WebauthnDevices ) , "Webauthn devices" , "YAML" , filename )
return nil
}
// StorageUserWebauthnListRunE is the RunE for the authelia storage user webauthn list command.
func ( ctx * CmdCtx ) StorageUserWebauthnListRunE ( cmd * cobra . Command , args [ ] string ) ( err error ) {
defer func ( ) {
_ = ctx . providers . StorageProvider . Close ( )
} ( )
if len ( args ) == 0 || args [ 0 ] == "" {
return ctx . StorageUserWebauthnListAllRunE ( cmd , args )
}
if err = ctx . CheckSchema ( ) ; err != nil {
2022-12-22 00:21:29 +00:00
return storageWrapCheckSchemaErr ( err )
}
2022-10-19 07:17:55 +00:00
var devices [ ] model . WebauthnDevice
user := args [ 0 ]
2023-02-11 15:47:03 +00:00
devices , err = ctx . providers . StorageProvider . LoadWebauthnDevicesByUsername ( ctx , "" , user )
2022-10-19 07:17:55 +00:00
switch {
case len ( devices ) == 0 || ( err != nil && errors . Is ( err , storage . ErrNoWebauthnDevice ) ) :
return fmt . Errorf ( "user '%s' has no webauthn devices" , user )
case err != nil :
return fmt . Errorf ( "can't list devices for user '%s': %w" , user , err )
default :
fmt . Printf ( "Webauthn Devices for user '%s':\n\n" , user )
fmt . Printf ( "ID\tKID\tDescription\n" )
for _ , device := range devices {
fmt . Printf ( "%d\t%s\t%s" , device . ID , device . KID , device . Description )
}
}
return nil
}
2022-12-23 04:00:23 +00:00
// StorageUserWebauthnListAllRunE is the RunE for the authelia storage user webauthn list command when no args are specified.
func ( ctx * CmdCtx ) StorageUserWebauthnListAllRunE ( _ * cobra . Command , _ [ ] string ) ( err error ) {
2022-10-19 07:17:55 +00:00
defer func ( ) {
2022-12-22 00:21:29 +00:00
_ = ctx . providers . StorageProvider . Close ( )
2022-10-19 07:17:55 +00:00
} ( )
2022-12-23 04:00:23 +00:00
if err = ctx . CheckSchema ( ) ; err != nil {
2022-12-22 00:21:29 +00:00
return storageWrapCheckSchemaErr ( err )
}
2022-10-19 07:17:55 +00:00
var devices [ ] model . WebauthnDevice
limit := 10
output := strings . Builder { }
for page := 0 ; true ; page ++ {
2022-12-22 00:21:29 +00:00
if devices , err = ctx . providers . StorageProvider . LoadWebauthnDevices ( ctx , limit , page ) ; err != nil {
2022-10-19 07:17:55 +00:00
return fmt . Errorf ( "failed to list devices: %w" , err )
}
if page == 0 && len ( devices ) == 0 {
return errors . New ( "no webauthn devices in database" )
}
for _ , device := range devices {
output . WriteString ( fmt . Sprintf ( "%d\t%s\t%s\t%s\n" , device . ID , device . KID , device . Description , device . Username ) )
}
if len ( devices ) < limit {
break
}
}
fmt . Printf ( "Webauthn Devices:\n\nID\tKID\tDescription\tUsername\n" )
fmt . Println ( output . String ( ) )
return nil
}
2022-12-23 04:00:23 +00:00
// StorageUserWebauthnDeleteRunE is the RunE for the authelia storage user webauthn delete command.
func ( ctx * CmdCtx ) StorageUserWebauthnDeleteRunE ( cmd * cobra . Command , args [ ] string ) ( err error ) {
2022-10-19 07:17:55 +00:00
defer func ( ) {
2022-12-22 00:21:29 +00:00
_ = ctx . providers . StorageProvider . Close ( )
2022-10-19 07:17:55 +00:00
} ( )
2022-12-23 04:00:23 +00:00
if err = ctx . CheckSchema ( ) ; err != nil {
2022-12-22 00:21:29 +00:00
return storageWrapCheckSchemaErr ( err )
}
2022-10-19 07:17:55 +00:00
var (
all , byKID bool
description , kid , user string
)
2022-12-22 00:21:29 +00:00
if all , byKID , description , kid , user , err = storageWebauthnDeleteRunEOptsFromFlags ( cmd . Flags ( ) , args ) ; err != nil {
2022-10-19 07:17:55 +00:00
return err
}
if byKID {
2022-12-22 00:21:29 +00:00
if err = ctx . providers . StorageProvider . DeleteWebauthnDevice ( ctx , kid ) ; err != nil {
2022-12-23 04:00:23 +00:00
return fmt . Errorf ( "failed to delete webauthn device with kid '%s': %w" , kid , err )
2022-10-19 07:17:55 +00:00
}
2022-12-23 04:00:23 +00:00
fmt . Printf ( "Successfully deleted Webauthn device with key id '%s'\n" , kid )
2022-10-19 07:17:55 +00:00
} else {
2022-12-22 00:21:29 +00:00
err = ctx . providers . StorageProvider . DeleteWebauthnDeviceByUsername ( ctx , user , description )
2022-10-19 07:17:55 +00:00
if all {
if err != nil {
2022-12-23 04:00:23 +00:00
return fmt . Errorf ( "failed to delete all webauthn devices with username '%s': %w" , user , err )
2022-10-19 07:17:55 +00:00
}
2022-12-23 04:00:23 +00:00
fmt . Printf ( "Successfully deleted all Webauthn devices for user '%s'\n" , user )
2022-10-19 07:17:55 +00:00
} else {
if err != nil {
2022-12-23 04:00:23 +00:00
return fmt . Errorf ( "failed to delete webauthn device with username '%s' and description '%s': %w" , user , description , err )
2022-10-19 07:17:55 +00:00
}
2022-12-23 04:00:23 +00:00
fmt . Printf ( "Successfully deleted Webauthn device with description '%s' for user '%s'\n" , description , user )
2022-10-19 07:17:55 +00:00
}
}
return nil
}
2022-12-23 04:00:23 +00:00
// StorageUserTOTPGenerateRunE is the RunE for the authelia storage user totp generate command.
func ( ctx * CmdCtx ) StorageUserTOTPGenerateRunE ( cmd * cobra . Command , args [ ] string ) ( err error ) {
defer func ( ) {
_ = ctx . providers . StorageProvider . Close ( )
} ( )
2021-11-25 01:56:58 +00:00
var (
2022-04-07 23:01:01 +00:00
c * model . TOTPConfiguration
force bool
filename , secret string
file * os . File
img image . Image
2021-11-25 01:56:58 +00:00
)
2022-12-23 04:00:23 +00:00
if err = ctx . CheckSchema ( ) ; err != nil {
2022-12-22 00:21:29 +00:00
return storageWrapCheckSchemaErr ( err )
}
2022-04-07 23:01:01 +00:00
if force , filename , secret , err = storageTOTPGenerateRunEOptsFromFlags ( cmd . Flags ( ) ) ; err != nil {
2022-03-02 07:50:36 +00:00
return err
}
2022-12-22 00:21:29 +00:00
if _ , err = ctx . providers . StorageProvider . LoadTOTPConfiguration ( ctx , args [ 0 ] ) ; err == nil && ! force {
2021-12-01 12:11:29 +00:00
return fmt . Errorf ( "%s already has a TOTP configuration, use --force to overwrite" , args [ 0 ] )
2022-04-07 23:01:01 +00:00
} else if err != nil && ! errors . Is ( err , storage . ErrNoTOTPConfiguration ) {
2021-12-01 12:11:29 +00:00
return err
}
2022-12-22 00:21:29 +00:00
totpProvider := totp . NewTimeBasedProvider ( ctx . config . TOTP )
2021-12-01 12:11:29 +00:00
2022-12-22 00:21:29 +00:00
if c , err = totpProvider . GenerateCustom ( args [ 0 ] , ctx . config . TOTP . Algorithm , secret , ctx . config . TOTP . Digits , ctx . config . TOTP . Period , ctx . config . TOTP . SecretSize ) ; err != nil {
2021-12-01 12:11:29 +00:00
return err
}
2022-03-02 07:50:36 +00:00
extraInfo := ""
if filename != "" {
if _ , err = os . Stat ( filename ) ; ! os . IsNotExist ( err ) {
return errors . New ( "image output filepath already exists" )
}
if file , err = os . Create ( filename ) ; err != nil {
return err
}
defer file . Close ( )
if img , err = c . Image ( 256 , 256 ) ; err != nil {
return err
}
if err = png . Encode ( file , img ) ; err != nil {
return err
}
extraInfo = fmt . Sprintf ( " and saved it as a PNG image at the path '%s'" , filename )
}
2022-12-23 04:00:23 +00:00
if err = ctx . providers . StorageProvider . SaveTOTPConfiguration ( ctx , * c ) ; err != nil {
return err
}
fmt . Printf ( "Successfully generated TOTP configuration for user '%s' with URI '%s'%s\n" , args [ 0 ] , c . URI ( ) , extraInfo )
return nil
}
// StorageUserTOTPDeleteRunE is the RunE for the authelia storage user totp delete command.
func ( ctx * CmdCtx ) StorageUserTOTPDeleteRunE ( cmd * cobra . Command , args [ ] string ) ( err error ) {
defer func ( ) {
_ = ctx . providers . StorageProvider . Close ( )
} ( )
user := args [ 0 ]
if err = ctx . CheckSchema ( ) ; err != nil {
return storageWrapCheckSchemaErr ( err )
}
if _ , err = ctx . providers . StorageProvider . LoadTOTPConfiguration ( ctx , user ) ; err != nil {
return fmt . Errorf ( "failed to delete TOTP configuration for user '%s': %+v" , user , err )
}
if err = ctx . providers . StorageProvider . DeleteTOTPConfiguration ( ctx , user ) ; err != nil {
return fmt . Errorf ( "failed to delete TOTP configuration for user '%s': %+v" , user , err )
}
fmt . Printf ( "Successfully deleted TOTP configuration for user '%s'\n" , user )
return nil
}
const (
cliOutputFmtSuccessfulUserExportFile = "Successfully exported %d %s as %s to the '%s' file\n"
cliOutputFmtSuccessfulUserImportFile = "Successfully imported %d %s from the %s file '%s' into the database\n"
)
// StorageUserTOTPExportRunE is the RunE for the authelia storage user totp export command.
func ( ctx * CmdCtx ) StorageUserTOTPExportRunE ( cmd * cobra . Command , _ [ ] string ) ( err error ) {
defer func ( ) {
_ = ctx . providers . StorageProvider . Close ( )
} ( )
if err = ctx . CheckSchema ( ) ; err != nil {
return storageWrapCheckSchemaErr ( err )
}
var (
filename string
)
if filename , err = cmd . Flags ( ) . GetString ( cmdFlagNameFile ) ; err != nil {
return err
}
switch _ , err = os . Stat ( filename ) ; {
case err == nil :
return fmt . Errorf ( "must specify a file that doesn't exist but '%s' exists" , filename )
case ! os . IsNotExist ( err ) :
return fmt . Errorf ( "error occurred opening '%s': %w" , filename , err )
}
limit := 10
count := 0
var (
configs [ ] model . TOTPConfiguration
)
export := & model . TOTPConfigurationExport { }
for page := 0 ; true ; page ++ {
if configs , err = ctx . providers . StorageProvider . LoadTOTPConfigurations ( ctx , limit , page ) ; err != nil {
return err
}
export . TOTPConfigurations = append ( export . TOTPConfigurations , configs ... )
l := len ( configs )
count += l
if l < limit {
break
}
}
var data [ ] byte
if data , err = yaml . Marshal ( export ) ; err != nil {
return fmt . Errorf ( "error occurred marshalling data to YAML: %w" , err )
}
if err = os . WriteFile ( filename , data , 0600 ) ; err != nil {
return fmt . Errorf ( "error occurred writing to file '%s': %w" , filename , err )
}
fmt . Printf ( cliOutputFmtSuccessfulUserExportFile , count , "TOTP configurations" , "YAML" , filename )
return nil
}
func ( ctx * CmdCtx ) StorageUserTOTPImportRunE ( _ * cobra . Command , args [ ] string ) ( err error ) {
defer func ( ) {
_ = ctx . providers . StorageProvider . Close ( )
} ( )
var (
filename string
stat os . FileInfo
data [ ] byte
)
filename = args [ 0 ]
if stat , err = os . Stat ( filename ) ; err != nil {
return fmt . Errorf ( "must specify a filename that exists but '%s' had an error opening it: %w" , filename , err )
}
if stat . IsDir ( ) {
return fmt . Errorf ( "must specify a filename that exists but '%s' is a directory" , filename )
}
if data , err = os . ReadFile ( filename ) ; err != nil {
return err
}
export := & model . TOTPConfigurationExport { }
if err = yaml . Unmarshal ( data , export ) ; err != nil {
return err
}
if len ( export . TOTPConfigurations ) == 0 {
return fmt . Errorf ( "can't import a YAML file without TOTP configuration data" )
}
if err = ctx . CheckSchema ( ) ; err != nil {
return storageWrapCheckSchemaErr ( err )
}
for _ , config := range export . TOTPConfigurations {
if err = ctx . providers . StorageProvider . SaveTOTPConfiguration ( ctx , config ) ; err != nil {
return err
}
2021-12-01 12:11:29 +00:00
}
2022-12-23 04:00:23 +00:00
fmt . Printf ( cliOutputFmtSuccessfulUserImportFile , len ( export . TOTPConfigurations ) , "TOTP configurations" , "YAML" , filename )
2021-12-01 12:11:29 +00:00
return nil
}
2022-12-23 04:00:23 +00:00
func ( ctx * CmdCtx ) StorageUserTOTPExportURIRunE ( _ * cobra . Command , _ [ ] string ) ( err error ) {
2021-12-01 12:11:29 +00:00
defer func ( ) {
2022-12-22 00:21:29 +00:00
_ = ctx . providers . StorageProvider . Close ( )
2021-12-01 12:11:29 +00:00
} ( )
2022-12-23 04:00:23 +00:00
var (
configs [ ] model . TOTPConfiguration
)
if err = ctx . CheckSchema ( ) ; err != nil {
2022-12-22 00:21:29 +00:00
return storageWrapCheckSchemaErr ( err )
}
2022-12-23 04:00:23 +00:00
limit := 10
count := 0
2021-12-01 12:11:29 +00:00
2022-12-23 04:00:23 +00:00
buf := & bytes . Buffer { }
for page := 0 ; true ; page ++ {
if configs , err = ctx . providers . StorageProvider . LoadTOTPConfigurations ( ctx , limit , page ) ; err != nil {
return err
}
for _ , c := range configs {
buf . WriteString ( fmt . Sprintf ( "%s\n" , c . URI ( ) ) )
}
l := len ( configs )
count += l
if l < limit {
break
}
2021-11-25 01:56:58 +00:00
}
2022-12-23 04:00:23 +00:00
fmt . Print ( buf . String ( ) )
fmt . Printf ( "\n\nSuccessfully exported %d TOTP configurations as TOTP URI's and printed them to the console\n" , count )
2021-12-01 12:11:29 +00:00
return nil
}
2022-12-23 04:00:23 +00:00
func ( ctx * CmdCtx ) StorageUserTOTPExportCSVRunE ( cmd * cobra . Command , _ [ ] string ) ( err error ) {
2021-11-25 01:56:58 +00:00
defer func ( ) {
2022-12-22 00:21:29 +00:00
_ = ctx . providers . StorageProvider . Close ( )
2021-11-25 01:56:58 +00:00
} ( )
2022-12-23 04:00:23 +00:00
var (
filename string
configs [ ] model . TOTPConfiguration
buf * bytes . Buffer
)
if err = ctx . CheckSchema ( ) ; err != nil {
2022-12-22 00:21:29 +00:00
return storageWrapCheckSchemaErr ( err )
2021-11-25 01:56:58 +00:00
}
2022-12-23 04:00:23 +00:00
if filename , err = cmd . Flags ( ) . GetString ( cmdFlagNameFile ) ; err != nil {
2021-11-25 01:56:58 +00:00
return err
}
limit := 10
2022-12-23 04:00:23 +00:00
count := 0
buf = & bytes . Buffer { }
buf . WriteString ( "issuer,username,algorithm,digits,period,secret\n" )
2021-11-25 01:56:58 +00:00
for page := 0 ; true ; page ++ {
2022-12-23 04:00:23 +00:00
if configs , err = ctx . providers . StorageProvider . LoadTOTPConfigurations ( ctx , limit , page ) ; err != nil {
2021-11-25 01:56:58 +00:00
return err
}
2022-12-23 04:00:23 +00:00
for _ , c := range configs {
buf . WriteString ( fmt . Sprintf ( "%s,%s,%s,%d,%d,%s\n" , c . Issuer , c . Username , c . Algorithm , c . Digits , c . Period , string ( c . Secret ) ) )
2021-11-25 01:56:58 +00:00
}
2022-12-23 04:00:23 +00:00
l := len ( configs )
2022-03-17 05:53:07 +00:00
2022-12-23 04:00:23 +00:00
count += l
2021-11-25 01:56:58 +00:00
2022-12-23 04:00:23 +00:00
if l < limit {
2021-11-25 01:56:58 +00:00
break
}
}
2022-12-23 04:00:23 +00:00
if err = os . WriteFile ( filename , buf . Bytes ( ) , 0600 ) ; err != nil {
return err
2022-03-02 07:50:36 +00:00
}
2022-12-23 04:00:23 +00:00
fmt . Printf ( cliOutputFmtSuccessfulUserExportFile , count , "TOTP configurations" , "CSV" , filename )
2021-11-25 01:56:58 +00:00
return nil
}
2022-12-23 04:00:23 +00:00
func ( ctx * CmdCtx ) StorageUserTOTPExportPNGRunE ( cmd * cobra . Command , _ [ ] string ) ( err error ) {
2021-11-25 01:56:58 +00:00
defer func ( ) {
2022-12-22 00:21:29 +00:00
_ = ctx . providers . StorageProvider . Close ( )
2021-11-25 01:56:58 +00:00
} ( )
2022-12-23 04:00:23 +00:00
var (
dir string
configs [ ] model . TOTPConfiguration
img image . Image
)
2021-11-23 09:45:38 +00:00
2022-12-23 04:00:23 +00:00
if err = ctx . CheckSchema ( ) ; err != nil {
return storageWrapCheckSchemaErr ( err )
2021-11-25 01:56:58 +00:00
}
2022-12-23 04:00:23 +00:00
if dir , err = cmd . Flags ( ) . GetString ( cmdFlagNameDirectory ) ; err != nil {
2021-11-23 09:45:38 +00:00
return err
}
2022-12-23 04:00:23 +00:00
if dir == "" {
2023-01-07 00:19:41 +00:00
rand := & random . Cryptographical { }
dir = rand . StringCustom ( 8 , random . CharSetAlphaNumeric )
2021-11-23 09:45:38 +00:00
}
2022-12-23 04:00:23 +00:00
if _ , err = os . Stat ( dir ) ; ! os . IsNotExist ( err ) {
return errors . New ( "output directory must not exist" )
2021-11-23 09:45:38 +00:00
}
2022-12-23 04:00:23 +00:00
if err = os . MkdirAll ( dir , 0700 ) ; err != nil {
return err
2021-11-23 09:45:38 +00:00
}
2022-12-23 04:00:23 +00:00
limit := 10
count := 0
2021-11-23 09:45:38 +00:00
2022-12-23 04:00:23 +00:00
var file * os . File
2021-11-25 01:56:58 +00:00
2022-12-23 04:00:23 +00:00
for page := 0 ; true ; page ++ {
if configs , err = ctx . providers . StorageProvider . LoadTOTPConfigurations ( ctx , limit , page ) ; err != nil {
2021-11-23 09:45:38 +00:00
return err
}
2022-12-23 04:00:23 +00:00
for _ , c := range configs {
if file , err = os . Create ( filepath . Join ( dir , fmt . Sprintf ( "%s.png" , c . Username ) ) ) ; err != nil {
return err
2021-11-23 09:45:38 +00:00
}
2022-12-23 04:00:23 +00:00
if img , err = c . Image ( 256 , 256 ) ; err != nil {
_ = file . Close ( )
2022-12-22 00:21:29 +00:00
2021-11-23 09:45:38 +00:00
return err
}
2022-12-23 04:00:23 +00:00
if err = png . Encode ( file , img ) ; err != nil {
_ = file . Close ( )
return err
2022-12-22 00:21:29 +00:00
}
2021-11-23 09:45:38 +00:00
2022-12-23 04:00:23 +00:00
_ = file . Close ( )
2021-11-23 09:45:38 +00:00
}
2022-12-23 04:00:23 +00:00
l := len ( configs )
2021-11-23 09:45:38 +00:00
2022-12-23 04:00:23 +00:00
count += l
2021-11-25 01:56:58 +00:00
2022-12-23 04:00:23 +00:00
if l < limit {
break
2021-11-25 01:56:58 +00:00
}
}
2022-12-23 04:00:23 +00:00
fmt . Printf ( "Successfully exported %d TOTP configuration as QR codes in PNG format to the '%s' directory\n" , count , dir )
2021-11-25 01:56:58 +00:00
return nil
}
2022-12-22 00:21:29 +00:00
// StorageUserIdentifiersExportRunE is the RunE for the authelia storage user identifiers export command.
func ( ctx * CmdCtx ) StorageUserIdentifiersExportRunE ( cmd * cobra . Command , _ [ ] string ) ( err error ) {
2022-12-23 04:00:23 +00:00
defer func ( ) {
_ = ctx . providers . StorageProvider . Close ( )
} ( )
if err = ctx . CheckSchema ( ) ; err != nil {
return storageWrapCheckSchemaErr ( err )
}
2022-04-09 07:13:19 +00:00
var (
2022-12-23 04:00:23 +00:00
filename string
2022-04-09 07:13:19 +00:00
)
2022-12-23 04:00:23 +00:00
if filename , err = cmd . Flags ( ) . GetString ( cmdFlagNameFile ) ; err != nil {
2022-04-09 07:13:19 +00:00
return err
}
2022-12-23 04:00:23 +00:00
switch _ , err = os . Stat ( filename ) ; {
2022-04-09 07:13:19 +00:00
case err == nil :
2022-12-23 04:00:23 +00:00
return fmt . Errorf ( "must specify a file that doesn't exist but '%s' exists" , filename )
2022-04-09 07:13:19 +00:00
case ! os . IsNotExist ( err ) :
2022-12-23 04:00:23 +00:00
return fmt . Errorf ( "error occurred opening '%s': %w" , filename , err )
2022-04-09 07:13:19 +00:00
}
2022-12-23 04:00:23 +00:00
export := & model . UserOpaqueIdentifiersExport {
Identifiers : nil ,
2022-12-22 00:21:29 +00:00
}
2022-04-09 07:13:19 +00:00
2022-12-22 00:21:29 +00:00
if export . Identifiers , err = ctx . providers . StorageProvider . LoadUserOpaqueIdentifiers ( ctx ) ; err != nil {
2022-04-09 07:13:19 +00:00
return err
}
if len ( export . Identifiers ) == 0 {
return fmt . Errorf ( "no data to export" )
}
2022-12-23 04:00:23 +00:00
var data [ ] byte
if data , err = yaml . Marshal ( export ) ; err != nil {
2022-04-09 07:13:19 +00:00
return fmt . Errorf ( "error occurred marshalling data to YAML: %w" , err )
}
2022-12-23 04:00:23 +00:00
if err = os . WriteFile ( filename , data , 0600 ) ; err != nil {
return fmt . Errorf ( "error occurred writing to file '%s': %w" , filename , err )
2022-04-09 07:13:19 +00:00
}
2022-12-23 04:00:23 +00:00
fmt . Printf ( cliOutputFmtSuccessfulUserExportFile , len ( export . Identifiers ) , "User Opaque Identifiers" , "YAML" , filename )
2022-04-09 07:13:19 +00:00
return nil
}
2022-12-22 00:21:29 +00:00
// StorageUserIdentifiersImportRunE is the RunE for the authelia storage user identifiers import command.
2022-12-23 04:00:23 +00:00
func ( ctx * CmdCtx ) StorageUserIdentifiersImportRunE ( cmd * cobra . Command , args [ ] string ) ( err error ) {
defer func ( ) {
_ = ctx . providers . StorageProvider . Close ( )
} ( )
2022-04-09 07:13:19 +00:00
var (
2022-12-23 04:00:23 +00:00
filename string
2022-04-09 07:13:19 +00:00
stat os . FileInfo
2022-12-23 04:00:23 +00:00
data [ ] byte
2022-04-09 07:13:19 +00:00
)
2022-12-23 04:00:23 +00:00
filename = args [ 0 ]
2022-04-09 07:13:19 +00:00
2022-12-23 04:00:23 +00:00
if stat , err = os . Stat ( filename ) ; err != nil {
return fmt . Errorf ( "must specify a file that exists but '%s' had an error opening it: %w" , filename , err )
2022-04-09 07:13:19 +00:00
}
if stat . IsDir ( ) {
2022-12-23 04:00:23 +00:00
return fmt . Errorf ( "must specify a file that exists but '%s' is a directory" , filename )
2022-04-09 07:13:19 +00:00
}
2022-12-23 04:00:23 +00:00
if data , err = os . ReadFile ( filename ) ; err != nil {
2022-04-09 07:13:19 +00:00
return err
}
2022-12-23 04:00:23 +00:00
export := & model . UserOpaqueIdentifiersExport { }
if err = yaml . Unmarshal ( data , export ) ; err != nil {
2022-04-09 07:13:19 +00:00
return err
}
if len ( export . Identifiers ) == 0 {
2022-12-23 04:00:23 +00:00
return fmt . Errorf ( "can't import a YAML file without User Opaque Identifiers data" )
2022-04-09 07:13:19 +00:00
}
2022-12-23 04:00:23 +00:00
if err = ctx . CheckSchema ( ) ; err != nil {
2022-12-22 00:21:29 +00:00
return storageWrapCheckSchemaErr ( err )
}
2022-04-09 07:13:19 +00:00
for _ , opaqueID := range export . Identifiers {
2022-12-22 00:21:29 +00:00
if err = ctx . providers . StorageProvider . SaveUserOpaqueIdentifier ( ctx , opaqueID ) ; err != nil {
2022-04-09 07:13:19 +00:00
return err
}
}
2022-12-23 04:00:23 +00:00
fmt . Printf ( cliOutputFmtSuccessfulUserImportFile , len ( export . Identifiers ) , "User Opaque Identifiers" , "YAML" , filename )
2022-04-09 07:13:19 +00:00
return nil
}
2022-12-22 00:21:29 +00:00
// StorageUserIdentifiersGenerateRunE is the RunE for the authelia storage user identifiers generate command.
func ( ctx * CmdCtx ) StorageUserIdentifiersGenerateRunE ( cmd * cobra . Command , _ [ ] string ) ( err error ) {
defer func ( ) {
_ = ctx . providers . StorageProvider . Close ( )
} ( )
2022-04-25 08:49:18 +00:00
2022-12-23 04:00:23 +00:00
var (
users , services , sectors [ ] string
)
if err = ctx . CheckSchema ( ) ; err != nil {
2022-12-22 00:21:29 +00:00
return storageWrapCheckSchemaErr ( err )
2022-04-25 08:49:18 +00:00
}
2022-12-22 00:21:29 +00:00
identifiers , err := ctx . providers . StorageProvider . LoadUserOpaqueIdentifiers ( ctx )
if err != nil && ! errors . Is ( err , sql . ErrNoRows ) {
return fmt . Errorf ( "can't load the existing identifiers: %w" , err )
2022-04-25 08:49:18 +00:00
}
2022-12-22 00:21:29 +00:00
if users , services , sectors , err = flagsGetUserIdentifiersGenerateOptions ( cmd . Flags ( ) ) ; err != nil {
2022-04-25 08:49:18 +00:00
return err
}
if len ( users ) == 0 {
return fmt . Errorf ( "must supply at least one user" )
}
if len ( sectors ) == 0 {
sectors = append ( sectors , "" )
}
if ! utils . IsStringSliceContainsAll ( services , validIdentifierServices ) {
return fmt . Errorf ( "one or more the service names '%s' is invalid, the valid values are: '%s'" , strings . Join ( services , "', '" ) , strings . Join ( validIdentifierServices , "', '" ) )
}
var added , duplicates int
for _ , service := range services {
for _ , sector := range sectors {
for _ , username := range users {
identifier := model . UserOpaqueIdentifier {
Service : service ,
SectorID : sector ,
Username : username ,
}
if containsIdentifier ( identifier , identifiers ) {
duplicates ++
continue
}
identifier . Identifier , err = uuid . NewRandom ( )
if err != nil {
return fmt . Errorf ( "failed to generate a uuid: %w" , err )
}
2022-12-22 00:21:29 +00:00
if err = ctx . providers . StorageProvider . SaveUserOpaqueIdentifier ( ctx , identifier ) ; err != nil {
2022-04-25 08:49:18 +00:00
return fmt . Errorf ( "failed to save identifier: %w" , err )
}
added ++
}
}
}
2022-12-23 04:00:23 +00:00
fmt . Printf ( "Successfully generated and addded opaque identifiers:\n" )
fmt . Printf ( "\tUsers: '%s'\n" , strings . Join ( users , "', '" ) )
fmt . Printf ( "\tSectors: '%s'\n" , strings . Join ( sectors , "', '" ) )
fmt . Printf ( "\tServices: '%s'\n" , strings . Join ( services , "', '" ) )
if duplicates != 0 {
fmt . Printf ( "\tSkipped Duplicates: %d\n" , duplicates )
}
fmt . Printf ( "\tTotal: %d" , added )
2022-04-25 08:49:18 +00:00
return nil
}
2022-12-22 00:21:29 +00:00
// StorageUserIdentifiersAddRunE is the RunE for the authelia storage user identifiers add command.
func ( ctx * CmdCtx ) StorageUserIdentifiersAddRunE ( cmd * cobra . Command , args [ ] string ) ( err error ) {
2022-12-23 04:00:23 +00:00
defer func ( ) {
_ = ctx . providers . StorageProvider . Close ( )
} ( )
2022-04-09 07:13:19 +00:00
var (
service , sector string
)
2022-11-25 12:44:55 +00:00
if service , err = cmd . Flags ( ) . GetString ( cmdFlagNameService ) ; err != nil {
2022-04-09 07:13:19 +00:00
return err
}
if service == "" {
service = identifierServiceOpenIDConnect
2022-04-25 08:49:18 +00:00
} else if ! utils . IsStringInSlice ( service , validIdentifierServices ) {
return fmt . Errorf ( "the service name '%s' is invalid, the valid values are: '%s'" , service , strings . Join ( validIdentifierServices , "', '" ) )
2022-04-09 07:13:19 +00:00
}
2022-11-25 12:44:55 +00:00
if sector , err = cmd . Flags ( ) . GetString ( cmdFlagNameSector ) ; err != nil {
2022-04-09 07:13:19 +00:00
return err
}
opaqueID := model . UserOpaqueIdentifier {
Service : service ,
Username : args [ 0 ] ,
SectorID : sector ,
}
2022-11-25 12:44:55 +00:00
if cmd . Flags ( ) . Changed ( cmdFlagNameIdentifier ) {
2022-04-09 07:13:19 +00:00
var identifierStr string
2022-11-25 12:44:55 +00:00
if identifierStr , err = cmd . Flags ( ) . GetString ( cmdFlagNameIdentifier ) ; err != nil {
2022-04-09 07:13:19 +00:00
return err
}
if opaqueID . Identifier , err = uuid . Parse ( identifierStr ) ; err != nil {
return fmt . Errorf ( "the identifier provided '%s' is invalid as it must be a version 4 UUID but parsing it had an error: %w" , identifierStr , err )
}
if opaqueID . Identifier . Version ( ) != 4 {
return fmt . Errorf ( "the identifier providerd '%s' is a version %d UUID but only version 4 UUID's accepted as identifiers" , identifierStr , opaqueID . Identifier . Version ( ) )
}
} else {
if opaqueID . Identifier , err = uuid . NewRandom ( ) ; err != nil {
return err
}
}
2022-12-23 04:00:23 +00:00
if err = ctx . CheckSchema ( ) ; err != nil {
2022-12-22 00:21:29 +00:00
return storageWrapCheckSchemaErr ( err )
}
2022-04-09 07:13:19 +00:00
2022-12-22 00:21:29 +00:00
if err = ctx . providers . StorageProvider . SaveUserOpaqueIdentifier ( ctx , opaqueID ) ; err != nil {
2022-04-09 07:13:19 +00:00
return err
}
fmt . Printf ( "Added User Opaque Identifier:\n\tService: %s\n\tSector: %s\n\tUsername: %s\n\tIdentifier: %s\n\n" , opaqueID . Service , opaqueID . SectorID , opaqueID . Username , opaqueID . Identifier )
return nil
}