refactor(commands): command context (#4539)
This moves a lot of machinery for commands into a context.Context with other struct values. This allows for PreRunE's to reliably load the configuration and avoids use of global vars.pull/4614/head^2
parent
0732e60e16
commit
e3e31e3cbc
|
@ -26,6 +26,7 @@ body:
|
|||
description: What version(s) of Authelia can you reproduce this bug on?
|
||||
multiple: true
|
||||
options:
|
||||
- v4.37.5
|
||||
- v4.37.4
|
||||
- v4.37.3
|
||||
- v4.37.2
|
||||
|
|
|
@ -232,9 +232,9 @@ Example:
|
|||
```yaml
|
||||
{{ if contains (env "DOMAIN") "https://" }}
|
||||
default_redirection_url: '{{ env "DOMAIN" }}'
|
||||
{{ else}}
|
||||
{{ else }}
|
||||
default_redirection_url: 'https://{{ env "DOMAIN" }}'
|
||||
{{ end }}
|
||||
{{ end }}
|
||||
```
|
||||
|
||||
##### hasPrefix
|
||||
|
@ -246,7 +246,7 @@ Example:
|
|||
```yaml
|
||||
{{ if hasPrefix (env "DOMAIN") "https://" }}
|
||||
default_redirection_url: '{{ env "DOMAIN" }}'
|
||||
{{ else}}
|
||||
{{ else }}
|
||||
default_redirection_url: 'https://{{ env "DOMAIN" }}'
|
||||
{{ end }}
|
||||
```
|
||||
|
@ -260,7 +260,7 @@ Example:
|
|||
```yaml
|
||||
{{ if hasSuffix (env "DOMAIN") "/" }}
|
||||
default_redirection_url: 'https://{{ env "DOMAIN" }}'
|
||||
{{ else}}
|
||||
{{ else }}
|
||||
default_redirection_url: 'https://{{ env "DOMAIN" }}/'
|
||||
{{ end }}
|
||||
```
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
---
|
||||
title: "Versioning Policy"
|
||||
description: "The Authelia Versioning Policy which is important reading for administrators"
|
||||
date: 2022-12-21T16:46:42+11:00
|
||||
date: 2022-12-21T20:48:14+11:00
|
||||
draft: false
|
||||
images: []
|
||||
aliases:
|
||||
|
|
|
@ -41,8 +41,8 @@ authelia --config /etc/authelia/config/
|
|||
### Options
|
||||
|
||||
```
|
||||
-c, --config strings configuration files to load
|
||||
--config.experimental.filters strings Applies filters in order to the configuration file before the YAML parser. Options are 'template', 'expand-env'
|
||||
-c, --config strings configuration files to load (default [configuration.yml])
|
||||
--config.experimental.filters strings applies filters in order to the configuration file before the YAML parser, options are 'template', 'expand-env'
|
||||
-h, --help help for authelia
|
||||
```
|
||||
|
||||
|
@ -51,7 +51,6 @@ authelia --config /etc/authelia/config/
|
|||
* [authelia access-control](authelia_access-control.md) - Helpers for the access control system
|
||||
* [authelia build-info](authelia_build-info.md) - Show the build information of Authelia
|
||||
* [authelia crypto](authelia_crypto.md) - Perform cryptographic operations
|
||||
* [authelia hash-password](authelia_hash-password.md) - Hash a password to be used in file-based users database
|
||||
* [authelia storage](authelia_storage.md) - Manage the Authelia storage
|
||||
* [authelia validate-config](authelia_validate-config.md) - Check a configuration against the internal configuration validation mechanisms
|
||||
|
||||
|
|
|
@ -32,6 +32,13 @@ authelia access-control --help
|
|||
-h, --help help for access-control
|
||||
```
|
||||
|
||||
### Options inherited from parent commands
|
||||
|
||||
```
|
||||
-c, --config strings configuration files to load (default [configuration.yml])
|
||||
--config.experimental.filters strings applies filters in order to the configuration file before the YAML parser, options are 'template', 'expand-env'
|
||||
```
|
||||
|
||||
### SEE ALSO
|
||||
|
||||
* [authelia](authelia.md) - authelia untagged-unknown-dirty (master, unknown)
|
||||
|
|
|
@ -53,7 +53,6 @@ authelia access-control check-policy --config config.yml --url https://example.c
|
|||
### Options
|
||||
|
||||
```
|
||||
-c, --config strings configuration files to load (default [configuration.yml])
|
||||
--groups strings the groups of the subject
|
||||
-h, --help help for check-policy
|
||||
--ip string the ip of the subject
|
||||
|
@ -63,6 +62,13 @@ authelia access-control check-policy --config config.yml --url https://example.c
|
|||
--verbose enables verbose output
|
||||
```
|
||||
|
||||
### Options inherited from parent commands
|
||||
|
||||
```
|
||||
-c, --config strings configuration files to load (default [configuration.yml])
|
||||
--config.experimental.filters strings applies filters in order to the configuration file before the YAML parser, options are 'template', 'expand-env'
|
||||
```
|
||||
|
||||
### SEE ALSO
|
||||
|
||||
* [authelia access-control](authelia_access-control.md) - Helpers for the access control system
|
||||
|
|
|
@ -45,6 +45,13 @@ authelia build-info
|
|||
-h, --help help for build-info
|
||||
```
|
||||
|
||||
### Options inherited from parent commands
|
||||
|
||||
```
|
||||
-c, --config strings configuration files to load (default [configuration.yml])
|
||||
--config.experimental.filters strings applies filters in order to the configuration file before the YAML parser, options are 'template', 'expand-env'
|
||||
```
|
||||
|
||||
### SEE ALSO
|
||||
|
||||
* [authelia](authelia.md) - authelia untagged-unknown-dirty (master, unknown)
|
||||
|
|
|
@ -34,6 +34,13 @@ authelia crypto --help
|
|||
-h, --help help for crypto
|
||||
```
|
||||
|
||||
### Options inherited from parent commands
|
||||
|
||||
```
|
||||
-c, --config strings configuration files to load (default [configuration.yml])
|
||||
--config.experimental.filters strings applies filters in order to the configuration file before the YAML parser, options are 'template', 'expand-env'
|
||||
```
|
||||
|
||||
### SEE ALSO
|
||||
|
||||
* [authelia](authelia.md) - authelia untagged-unknown-dirty (master, unknown)
|
||||
|
|
|
@ -34,6 +34,13 @@ authelia crypto certificate --help
|
|||
-h, --help help for certificate
|
||||
```
|
||||
|
||||
### Options inherited from parent commands
|
||||
|
||||
```
|
||||
-c, --config strings configuration files to load (default [configuration.yml])
|
||||
--config.experimental.filters strings applies filters in order to the configuration file before the YAML parser, options are 'template', 'expand-env'
|
||||
```
|
||||
|
||||
### SEE ALSO
|
||||
|
||||
* [authelia crypto](authelia_crypto.md) - Perform cryptographic operations
|
||||
|
|
|
@ -34,6 +34,13 @@ authelia crypto certificate ecdsa --help
|
|||
-h, --help help for ecdsa
|
||||
```
|
||||
|
||||
### Options inherited from parent commands
|
||||
|
||||
```
|
||||
-c, --config strings configuration files to load (default [configuration.yml])
|
||||
--config.experimental.filters strings applies filters in order to the configuration file before the YAML parser, options are 'template', 'expand-env'
|
||||
```
|
||||
|
||||
### SEE ALSO
|
||||
|
||||
* [authelia crypto certificate](authelia_crypto_certificate.md) - Perform certificate cryptographic operations
|
||||
|
|
|
@ -36,7 +36,7 @@ authelia crypto certificate ecdsa generate --help
|
|||
|
||||
```
|
||||
--ca create the certificate as a certificate authority certificate
|
||||
-c, --common-name string certificate common name
|
||||
-n, --common-name string certificate common name
|
||||
--country strings certificate country
|
||||
-b, --curve string Sets the elliptic curve which can be P224, P256, P384, or P521 (default "P256")
|
||||
-d, --directory string directory where the generated keys, certificates, etc will be stored
|
||||
|
@ -59,6 +59,13 @@ authelia crypto certificate ecdsa generate --help
|
|||
-s, --street-address strings certificate street address
|
||||
```
|
||||
|
||||
### Options inherited from parent commands
|
||||
|
||||
```
|
||||
-c, --config strings configuration files to load (default [configuration.yml])
|
||||
--config.experimental.filters strings applies filters in order to the configuration file before the YAML parser, options are 'template', 'expand-env'
|
||||
```
|
||||
|
||||
### SEE ALSO
|
||||
|
||||
* [authelia crypto certificate ecdsa](authelia_crypto_certificate_ecdsa.md) - Perform ECDSA certificate cryptographic operations
|
||||
|
|
|
@ -35,7 +35,7 @@ authelia crypto certificate ecdsa request --help
|
|||
### Options
|
||||
|
||||
```
|
||||
-c, --common-name string certificate common name
|
||||
-n, --common-name string certificate common name
|
||||
--country strings certificate country
|
||||
-b, --curve string Sets the elliptic curve which can be P224, P256, P384, or P521 (default "P256")
|
||||
-d, --directory string directory where the generated keys, certificates, etc will be stored
|
||||
|
@ -54,6 +54,13 @@ authelia crypto certificate ecdsa request --help
|
|||
-s, --street-address strings certificate street address
|
||||
```
|
||||
|
||||
### Options inherited from parent commands
|
||||
|
||||
```
|
||||
-c, --config strings configuration files to load (default [configuration.yml])
|
||||
--config.experimental.filters strings applies filters in order to the configuration file before the YAML parser, options are 'template', 'expand-env'
|
||||
```
|
||||
|
||||
### SEE ALSO
|
||||
|
||||
* [authelia crypto certificate ecdsa](authelia_crypto_certificate_ecdsa.md) - Perform ECDSA certificate cryptographic operations
|
||||
|
|
|
@ -34,6 +34,13 @@ authelia crypto certificate ed25519 --help
|
|||
-h, --help help for ed25519
|
||||
```
|
||||
|
||||
### Options inherited from parent commands
|
||||
|
||||
```
|
||||
-c, --config strings configuration files to load (default [configuration.yml])
|
||||
--config.experimental.filters strings applies filters in order to the configuration file before the YAML parser, options are 'template', 'expand-env'
|
||||
```
|
||||
|
||||
### SEE ALSO
|
||||
|
||||
* [authelia crypto certificate](authelia_crypto_certificate.md) - Perform certificate cryptographic operations
|
||||
|
|
|
@ -36,7 +36,7 @@ authelia crypto certificate ed25519 request --help
|
|||
|
||||
```
|
||||
--ca create the certificate as a certificate authority certificate
|
||||
-c, --common-name string certificate common name
|
||||
-n, --common-name string certificate common name
|
||||
--country strings certificate country
|
||||
-d, --directory string directory where the generated keys, certificates, etc will be stored
|
||||
--duration duration duration of time the certificate is valid for (default 8760h0m0s)
|
||||
|
@ -58,6 +58,13 @@ authelia crypto certificate ed25519 request --help
|
|||
-s, --street-address strings certificate street address
|
||||
```
|
||||
|
||||
### Options inherited from parent commands
|
||||
|
||||
```
|
||||
-c, --config strings configuration files to load (default [configuration.yml])
|
||||
--config.experimental.filters strings applies filters in order to the configuration file before the YAML parser, options are 'template', 'expand-env'
|
||||
```
|
||||
|
||||
### SEE ALSO
|
||||
|
||||
* [authelia crypto certificate ed25519](authelia_crypto_certificate_ed25519.md) - Perform Ed25519 certificate cryptographic operations
|
||||
|
|
|
@ -35,7 +35,7 @@ authelia crypto certificate ed25519 request --help
|
|||
### Options
|
||||
|
||||
```
|
||||
-c, --common-name string certificate common name
|
||||
-n, --common-name string certificate common name
|
||||
--country strings certificate country
|
||||
-d, --directory string directory where the generated keys, certificates, etc will be stored
|
||||
--duration duration duration of time the certificate is valid for (default 8760h0m0s)
|
||||
|
@ -53,6 +53,13 @@ authelia crypto certificate ed25519 request --help
|
|||
-s, --street-address strings certificate street address
|
||||
```
|
||||
|
||||
### Options inherited from parent commands
|
||||
|
||||
```
|
||||
-c, --config strings configuration files to load (default [configuration.yml])
|
||||
--config.experimental.filters strings applies filters in order to the configuration file before the YAML parser, options are 'template', 'expand-env'
|
||||
```
|
||||
|
||||
### SEE ALSO
|
||||
|
||||
* [authelia crypto certificate ed25519](authelia_crypto_certificate_ed25519.md) - Perform Ed25519 certificate cryptographic operations
|
||||
|
|
|
@ -34,6 +34,13 @@ authelia crypto certificate rsa --help
|
|||
-h, --help help for rsa
|
||||
```
|
||||
|
||||
### Options inherited from parent commands
|
||||
|
||||
```
|
||||
-c, --config strings configuration files to load (default [configuration.yml])
|
||||
--config.experimental.filters strings applies filters in order to the configuration file before the YAML parser, options are 'template', 'expand-env'
|
||||
```
|
||||
|
||||
### SEE ALSO
|
||||
|
||||
* [authelia crypto certificate](authelia_crypto_certificate.md) - Perform certificate cryptographic operations
|
||||
|
|
|
@ -37,7 +37,7 @@ authelia crypto certificate rsa generate --help
|
|||
```
|
||||
-b, --bits int number of RSA bits for the certificate (default 2048)
|
||||
--ca create the certificate as a certificate authority certificate
|
||||
-c, --common-name string certificate common name
|
||||
-n, --common-name string certificate common name
|
||||
--country strings certificate country
|
||||
-d, --directory string directory where the generated keys, certificates, etc will be stored
|
||||
--duration duration duration of time the certificate is valid for (default 8760h0m0s)
|
||||
|
@ -59,6 +59,13 @@ authelia crypto certificate rsa generate --help
|
|||
-s, --street-address strings certificate street address
|
||||
```
|
||||
|
||||
### Options inherited from parent commands
|
||||
|
||||
```
|
||||
-c, --config strings configuration files to load (default [configuration.yml])
|
||||
--config.experimental.filters strings applies filters in order to the configuration file before the YAML parser, options are 'template', 'expand-env'
|
||||
```
|
||||
|
||||
### SEE ALSO
|
||||
|
||||
* [authelia crypto certificate rsa](authelia_crypto_certificate_rsa.md) - Perform RSA certificate cryptographic operations
|
||||
|
|
|
@ -36,7 +36,7 @@ authelia crypto certificate rsa request --help
|
|||
|
||||
```
|
||||
-b, --bits int number of RSA bits for the certificate (default 2048)
|
||||
-c, --common-name string certificate common name
|
||||
-n, --common-name string certificate common name
|
||||
--country strings certificate country
|
||||
-d, --directory string directory where the generated keys, certificates, etc will be stored
|
||||
--duration duration duration of time the certificate is valid for (default 8760h0m0s)
|
||||
|
@ -54,6 +54,13 @@ authelia crypto certificate rsa request --help
|
|||
-s, --street-address strings certificate street address
|
||||
```
|
||||
|
||||
### Options inherited from parent commands
|
||||
|
||||
```
|
||||
-c, --config strings configuration files to load (default [configuration.yml])
|
||||
--config.experimental.filters strings applies filters in order to the configuration file before the YAML parser, options are 'template', 'expand-env'
|
||||
```
|
||||
|
||||
### SEE ALSO
|
||||
|
||||
* [authelia crypto certificate rsa](authelia_crypto_certificate_rsa.md) - Perform RSA certificate cryptographic operations
|
||||
|
|
|
@ -34,6 +34,13 @@ authelia crypto hash --help
|
|||
-h, --help help for hash
|
||||
```
|
||||
|
||||
### Options inherited from parent commands
|
||||
|
||||
```
|
||||
-c, --config strings configuration files to load (default [configuration.yml])
|
||||
--config.experimental.filters strings applies filters in order to the configuration file before the YAML parser, options are 'template', 'expand-env'
|
||||
```
|
||||
|
||||
### SEE ALSO
|
||||
|
||||
* [authelia crypto](authelia_crypto.md) - Perform cryptographic operations
|
||||
|
|
|
@ -37,7 +37,6 @@ authelia crypto hash generate --help
|
|||
### Options
|
||||
|
||||
```
|
||||
-c, --config strings configuration files to load (default [configuration.yml])
|
||||
-h, --help help for generate
|
||||
--no-confirm skip the password confirmation prompt
|
||||
--password string manually supply the password rather than using the terminal prompt
|
||||
|
@ -47,6 +46,13 @@ authelia crypto hash generate --help
|
|||
--random.length int sets the character length for the random string (default 72)
|
||||
```
|
||||
|
||||
### Options inherited from parent commands
|
||||
|
||||
```
|
||||
-c, --config strings configuration files to load (default [configuration.yml])
|
||||
--config.experimental.filters strings applies filters in order to the configuration file before the YAML parser, options are 'template', 'expand-env'
|
||||
```
|
||||
|
||||
### SEE ALSO
|
||||
|
||||
* [authelia crypto hash](authelia_crypto_hash.md) - Perform cryptographic hash operations
|
||||
|
|
|
@ -48,13 +48,14 @@ authelia crypto hash generate argon2 --help
|
|||
### Options inherited from parent commands
|
||||
|
||||
```
|
||||
-c, --config strings configuration files to load (default [configuration.yml])
|
||||
--no-confirm skip the password confirmation prompt
|
||||
--password string manually supply the password rather than using the terminal prompt
|
||||
--random uses a randomly generated password
|
||||
--random.characters string sets the explicit characters for the random string
|
||||
--random.charset string sets the charset for the random password, options are 'ascii', 'alphanumeric', 'alphabetic', 'numeric', 'numeric-hex', and 'rfc3986' (default "alphanumeric")
|
||||
--random.length int sets the character length for the random string (default 72)
|
||||
-c, --config strings configuration files to load (default [configuration.yml])
|
||||
--config.experimental.filters strings applies filters in order to the configuration file before the YAML parser, options are 'template', 'expand-env'
|
||||
--no-confirm skip the password confirmation prompt
|
||||
--password string manually supply the password rather than using the terminal prompt
|
||||
--random uses a randomly generated password
|
||||
--random.characters string sets the explicit characters for the random string
|
||||
--random.charset string sets the charset for the random password, options are 'ascii', 'alphanumeric', 'alphabetic', 'numeric', 'numeric-hex', and 'rfc3986' (default "alphanumeric")
|
||||
--random.length int sets the character length for the random string (default 72)
|
||||
```
|
||||
|
||||
### SEE ALSO
|
||||
|
|
|
@ -43,13 +43,14 @@ authelia crypto hash generate bcrypt --help
|
|||
### Options inherited from parent commands
|
||||
|
||||
```
|
||||
-c, --config strings configuration files to load (default [configuration.yml])
|
||||
--no-confirm skip the password confirmation prompt
|
||||
--password string manually supply the password rather than using the terminal prompt
|
||||
--random uses a randomly generated password
|
||||
--random.characters string sets the explicit characters for the random string
|
||||
--random.charset string sets the charset for the random password, options are 'ascii', 'alphanumeric', 'alphabetic', 'numeric', 'numeric-hex', and 'rfc3986' (default "alphanumeric")
|
||||
--random.length int sets the character length for the random string (default 72)
|
||||
-c, --config strings configuration files to load (default [configuration.yml])
|
||||
--config.experimental.filters strings applies filters in order to the configuration file before the YAML parser, options are 'template', 'expand-env'
|
||||
--no-confirm skip the password confirmation prompt
|
||||
--password string manually supply the password rather than using the terminal prompt
|
||||
--random uses a randomly generated password
|
||||
--random.characters string sets the explicit characters for the random string
|
||||
--random.charset string sets the charset for the random password, options are 'ascii', 'alphanumeric', 'alphabetic', 'numeric', 'numeric-hex', and 'rfc3986' (default "alphanumeric")
|
||||
--random.length int sets the character length for the random string (default 72)
|
||||
```
|
||||
|
||||
### SEE ALSO
|
||||
|
|
|
@ -44,13 +44,14 @@ authelia crypto hash generate pbkdf2 --help
|
|||
### Options inherited from parent commands
|
||||
|
||||
```
|
||||
-c, --config strings configuration files to load (default [configuration.yml])
|
||||
--no-confirm skip the password confirmation prompt
|
||||
--password string manually supply the password rather than using the terminal prompt
|
||||
--random uses a randomly generated password
|
||||
--random.characters string sets the explicit characters for the random string
|
||||
--random.charset string sets the charset for the random password, options are 'ascii', 'alphanumeric', 'alphabetic', 'numeric', 'numeric-hex', and 'rfc3986' (default "alphanumeric")
|
||||
--random.length int sets the character length for the random string (default 72)
|
||||
-c, --config strings configuration files to load (default [configuration.yml])
|
||||
--config.experimental.filters strings applies filters in order to the configuration file before the YAML parser, options are 'template', 'expand-env'
|
||||
--no-confirm skip the password confirmation prompt
|
||||
--password string manually supply the password rather than using the terminal prompt
|
||||
--random uses a randomly generated password
|
||||
--random.characters string sets the explicit characters for the random string
|
||||
--random.charset string sets the charset for the random password, options are 'ascii', 'alphanumeric', 'alphabetic', 'numeric', 'numeric-hex', and 'rfc3986' (default "alphanumeric")
|
||||
--random.length int sets the character length for the random string (default 72)
|
||||
```
|
||||
|
||||
### SEE ALSO
|
||||
|
|
|
@ -46,13 +46,14 @@ authelia crypto hash generate scrypt --help
|
|||
### Options inherited from parent commands
|
||||
|
||||
```
|
||||
-c, --config strings configuration files to load (default [configuration.yml])
|
||||
--no-confirm skip the password confirmation prompt
|
||||
--password string manually supply the password rather than using the terminal prompt
|
||||
--random uses a randomly generated password
|
||||
--random.characters string sets the explicit characters for the random string
|
||||
--random.charset string sets the charset for the random password, options are 'ascii', 'alphanumeric', 'alphabetic', 'numeric', 'numeric-hex', and 'rfc3986' (default "alphanumeric")
|
||||
--random.length int sets the character length for the random string (default 72)
|
||||
-c, --config strings configuration files to load (default [configuration.yml])
|
||||
--config.experimental.filters strings applies filters in order to the configuration file before the YAML parser, options are 'template', 'expand-env'
|
||||
--no-confirm skip the password confirmation prompt
|
||||
--password string manually supply the password rather than using the terminal prompt
|
||||
--random uses a randomly generated password
|
||||
--random.characters string sets the explicit characters for the random string
|
||||
--random.charset string sets the charset for the random password, options are 'ascii', 'alphanumeric', 'alphabetic', 'numeric', 'numeric-hex', and 'rfc3986' (default "alphanumeric")
|
||||
--random.length int sets the character length for the random string (default 72)
|
||||
```
|
||||
|
||||
### SEE ALSO
|
||||
|
|
|
@ -44,13 +44,14 @@ authelia crypto hash generate sha2crypt --help
|
|||
### Options inherited from parent commands
|
||||
|
||||
```
|
||||
-c, --config strings configuration files to load (default [configuration.yml])
|
||||
--no-confirm skip the password confirmation prompt
|
||||
--password string manually supply the password rather than using the terminal prompt
|
||||
--random uses a randomly generated password
|
||||
--random.characters string sets the explicit characters for the random string
|
||||
--random.charset string sets the charset for the random password, options are 'ascii', 'alphanumeric', 'alphabetic', 'numeric', 'numeric-hex', and 'rfc3986' (default "alphanumeric")
|
||||
--random.length int sets the character length for the random string (default 72)
|
||||
-c, --config strings configuration files to load (default [configuration.yml])
|
||||
--config.experimental.filters strings applies filters in order to the configuration file before the YAML parser, options are 'template', 'expand-env'
|
||||
--no-confirm skip the password confirmation prompt
|
||||
--password string manually supply the password rather than using the terminal prompt
|
||||
--random uses a randomly generated password
|
||||
--random.characters string sets the explicit characters for the random string
|
||||
--random.charset string sets the charset for the random password, options are 'ascii', 'alphanumeric', 'alphabetic', 'numeric', 'numeric-hex', and 'rfc3986' (default "alphanumeric")
|
||||
--random.length int sets the character length for the random string (default 72)
|
||||
```
|
||||
|
||||
### SEE ALSO
|
||||
|
|
|
@ -40,6 +40,13 @@ authelia crypto hash validate '$5$rounds=500000$WFjMpdCQxIkbNl0k$M0qZaZoK8Gwdh8C
|
|||
--password string manually supply the password rather than using the terminal prompt
|
||||
```
|
||||
|
||||
### Options inherited from parent commands
|
||||
|
||||
```
|
||||
-c, --config strings configuration files to load (default [configuration.yml])
|
||||
--config.experimental.filters strings applies filters in order to the configuration file before the YAML parser, options are 'template', 'expand-env'
|
||||
```
|
||||
|
||||
### SEE ALSO
|
||||
|
||||
* [authelia crypto hash](authelia_crypto_hash.md) - Perform cryptographic hash operations
|
||||
|
|
|
@ -34,6 +34,13 @@ authelia crypto pair --help
|
|||
-h, --help help for pair
|
||||
```
|
||||
|
||||
### Options inherited from parent commands
|
||||
|
||||
```
|
||||
-c, --config strings configuration files to load (default [configuration.yml])
|
||||
--config.experimental.filters strings applies filters in order to the configuration file before the YAML parser, options are 'template', 'expand-env'
|
||||
```
|
||||
|
||||
### SEE ALSO
|
||||
|
||||
* [authelia crypto](authelia_crypto.md) - Perform cryptographic operations
|
||||
|
|
|
@ -38,6 +38,13 @@ authelia crypto pair ecdsa --help
|
|||
-h, --help help for ecdsa
|
||||
```
|
||||
|
||||
### Options inherited from parent commands
|
||||
|
||||
```
|
||||
-c, --config strings configuration files to load (default [configuration.yml])
|
||||
--config.experimental.filters strings applies filters in order to the configuration file before the YAML parser, options are 'template', 'expand-env'
|
||||
```
|
||||
|
||||
### SEE ALSO
|
||||
|
||||
* [authelia crypto pair](authelia_crypto_pair.md) - Perform key pair cryptographic operations
|
||||
|
|
|
@ -43,6 +43,13 @@ authelia crypto pair ecdsa generate --help
|
|||
--pkcs8 force PKCS #8 ASN.1 format
|
||||
```
|
||||
|
||||
### Options inherited from parent commands
|
||||
|
||||
```
|
||||
-c, --config strings configuration files to load (default [configuration.yml])
|
||||
--config.experimental.filters strings applies filters in order to the configuration file before the YAML parser, options are 'template', 'expand-env'
|
||||
```
|
||||
|
||||
### SEE ALSO
|
||||
|
||||
* [authelia crypto pair ecdsa](authelia_crypto_pair_ecdsa.md) - Perform ECDSA key pair cryptographic operations
|
||||
|
|
|
@ -38,6 +38,13 @@ authelia crypto pair ed25519 --help
|
|||
-h, --help help for ed25519
|
||||
```
|
||||
|
||||
### Options inherited from parent commands
|
||||
|
||||
```
|
||||
-c, --config strings configuration files to load (default [configuration.yml])
|
||||
--config.experimental.filters strings applies filters in order to the configuration file before the YAML parser, options are 'template', 'expand-env'
|
||||
```
|
||||
|
||||
### SEE ALSO
|
||||
|
||||
* [authelia crypto pair](authelia_crypto_pair.md) - Perform key pair cryptographic operations
|
||||
|
|
|
@ -42,6 +42,13 @@ authelia crypto pair ed25519 generate --help
|
|||
--pkcs8 force PKCS #8 ASN.1 format
|
||||
```
|
||||
|
||||
### Options inherited from parent commands
|
||||
|
||||
```
|
||||
-c, --config strings configuration files to load (default [configuration.yml])
|
||||
--config.experimental.filters strings applies filters in order to the configuration file before the YAML parser, options are 'template', 'expand-env'
|
||||
```
|
||||
|
||||
### SEE ALSO
|
||||
|
||||
* [authelia crypto pair ed25519](authelia_crypto_pair_ed25519.md) - Perform Ed25519 key pair cryptographic operations
|
||||
|
|
|
@ -38,6 +38,13 @@ authelia crypto pair rsa --help
|
|||
-h, --help help for rsa
|
||||
```
|
||||
|
||||
### Options inherited from parent commands
|
||||
|
||||
```
|
||||
-c, --config strings configuration files to load (default [configuration.yml])
|
||||
--config.experimental.filters strings applies filters in order to the configuration file before the YAML parser, options are 'template', 'expand-env'
|
||||
```
|
||||
|
||||
### SEE ALSO
|
||||
|
||||
* [authelia crypto pair](authelia_crypto_pair.md) - Perform key pair cryptographic operations
|
||||
|
|
|
@ -43,6 +43,13 @@ authelia crypto pair rsa generate --help
|
|||
--pkcs8 force PKCS #8 ASN.1 format
|
||||
```
|
||||
|
||||
### Options inherited from parent commands
|
||||
|
||||
```
|
||||
-c, --config strings configuration files to load (default [configuration.yml])
|
||||
--config.experimental.filters strings applies filters in order to the configuration file before the YAML parser, options are 'template', 'expand-env'
|
||||
```
|
||||
|
||||
### SEE ALSO
|
||||
|
||||
* [authelia crypto pair rsa](authelia_crypto_pair_rsa.md) - Perform RSA key pair cryptographic operations
|
||||
|
|
|
@ -44,11 +44,18 @@ authelia crypto rand --characters 0123456789ABCDEF
|
|||
|
||||
```
|
||||
--characters string sets the explicit characters for the random string
|
||||
-c, --charset string sets the charset for the random password, options are 'ascii', 'alphanumeric', 'alphabetic', 'numeric', 'numeric-hex', and 'rfc3986' (default "alphanumeric")
|
||||
-x, --charset string sets the charset for the random password, options are 'ascii', 'alphanumeric', 'alphabetic', 'numeric', 'numeric-hex', and 'rfc3986' (default "alphanumeric")
|
||||
-h, --help help for rand
|
||||
-n, --length int sets the character length for the random string (default 72)
|
||||
```
|
||||
|
||||
### Options inherited from parent commands
|
||||
|
||||
```
|
||||
-c, --config strings configuration files to load (default [configuration.yml])
|
||||
--config.experimental.filters strings applies filters in order to the configuration file before the YAML parser, options are 'template', 'expand-env'
|
||||
```
|
||||
|
||||
### SEE ALSO
|
||||
|
||||
* [authelia crypto](authelia_crypto.md) - Perform cryptographic operations
|
||||
|
|
|
@ -1,55 +0,0 @@
|
|||
---
|
||||
title: "authelia hash-password"
|
||||
description: "Reference for the authelia hash-password command."
|
||||
lead: ""
|
||||
date: 2022-06-15T17:51:47+10:00
|
||||
draft: false
|
||||
images: []
|
||||
menu:
|
||||
reference:
|
||||
parent: "cli-authelia"
|
||||
weight: 905
|
||||
toc: true
|
||||
---
|
||||
|
||||
## authelia hash-password
|
||||
|
||||
Hash a password to be used in file-based users database
|
||||
|
||||
### Synopsis
|
||||
|
||||
Hash a password to be used in file-based users database.
|
||||
|
||||
```
|
||||
authelia hash-password [flags] -- [password]
|
||||
```
|
||||
|
||||
### Examples
|
||||
|
||||
```
|
||||
authelia hash-password -- 'mypass'
|
||||
authelia hash-password --sha512 -- 'mypass'
|
||||
authelia hash-password --iterations=4 -- 'mypass'
|
||||
authelia hash-password --memory=128 -- 'mypass'
|
||||
authelia hash-password --parallelism=1 -- 'mypass'
|
||||
authelia hash-password --key-length=64 -- 'mypass'
|
||||
```
|
||||
|
||||
### Options
|
||||
|
||||
```
|
||||
-c, --config strings configuration files to load (default [configuration.yml])
|
||||
-h, --help help for hash-password
|
||||
-i, --iterations int set the number of hashing iterations (default 3)
|
||||
-k, --key-length int [argon2id] set the key length param (default 32)
|
||||
-m, --memory int [argon2id] set the amount of memory param (in MB) (default 65536)
|
||||
--no-confirm skip the password confirmation prompt
|
||||
-p, --parallelism int [argon2id] set the parallelism param (default 4)
|
||||
-l, --salt-length int set the auto-generated salt length (default 16)
|
||||
-z, --sha512 use sha512 as the algorithm (changes iterations to 50000, change with -i)
|
||||
```
|
||||
|
||||
### SEE ALSO
|
||||
|
||||
* [authelia](authelia.md) - authelia untagged-unknown-dirty (master, unknown)
|
||||
|
|
@ -33,7 +33,6 @@ authelia storage --help
|
|||
### Options
|
||||
|
||||
```
|
||||
-c, --config strings configuration files to load (default [configuration.yml])
|
||||
--encryption-key string the storage encryption key to use
|
||||
-h, --help help for storage
|
||||
--mysql.database string the MySQL database name (default "authelia")
|
||||
|
@ -54,6 +53,13 @@ authelia storage --help
|
|||
--sqlite.path string the SQLite database path
|
||||
```
|
||||
|
||||
### Options inherited from parent commands
|
||||
|
||||
```
|
||||
-c, --config strings configuration files to load (default [configuration.yml])
|
||||
--config.experimental.filters strings applies filters in order to the configuration file before the YAML parser, options are 'template', 'expand-env'
|
||||
```
|
||||
|
||||
### SEE ALSO
|
||||
|
||||
* [authelia](authelia.md) - authelia untagged-unknown-dirty (master, unknown)
|
||||
|
|
|
@ -38,6 +38,7 @@ authelia storage encryption --help
|
|||
|
||||
```
|
||||
-c, --config strings configuration files to load (default [configuration.yml])
|
||||
--config.experimental.filters strings applies filters in order to the configuration file before the YAML parser, options are 'template', 'expand-env'
|
||||
--encryption-key string the storage encryption key to use
|
||||
--mysql.database string the MySQL database name (default "authelia")
|
||||
--mysql.host string the MySQL hostname
|
||||
|
|
|
@ -44,6 +44,7 @@ authelia storage encryption change-key --encryption-key b3453fde-ecc2-4a1f-9422-
|
|||
|
||||
```
|
||||
-c, --config strings configuration files to load (default [configuration.yml])
|
||||
--config.experimental.filters strings applies filters in order to the configuration file before the YAML parser, options are 'template', 'expand-env'
|
||||
--encryption-key string the storage encryption key to use
|
||||
--mysql.database string the MySQL database name (default "authelia")
|
||||
--mysql.host string the MySQL hostname
|
||||
|
|
|
@ -46,6 +46,7 @@ authelia storage encryption check --verbose --encryption-key b3453fde-ecc2-4a1f-
|
|||
|
||||
```
|
||||
-c, --config strings configuration files to load (default [configuration.yml])
|
||||
--config.experimental.filters strings applies filters in order to the configuration file before the YAML parser, options are 'template', 'expand-env'
|
||||
--encryption-key string the storage encryption key to use
|
||||
--mysql.database string the MySQL database name (default "authelia")
|
||||
--mysql.host string the MySQL hostname
|
||||
|
|
|
@ -38,6 +38,7 @@ authelia storage migrate --help
|
|||
|
||||
```
|
||||
-c, --config strings configuration files to load (default [configuration.yml])
|
||||
--config.experimental.filters strings applies filters in order to the configuration file before the YAML parser, options are 'template', 'expand-env'
|
||||
--encryption-key string the storage encryption key to use
|
||||
--mysql.database string the MySQL database name (default "authelia")
|
||||
--mysql.host string the MySQL hostname
|
||||
|
|
|
@ -47,6 +47,7 @@ authelia storage migrate down --target 20 --encryption-key b3453fde-ecc2-4a1f-94
|
|||
|
||||
```
|
||||
-c, --config strings configuration files to load (default [configuration.yml])
|
||||
--config.experimental.filters strings applies filters in order to the configuration file before the YAML parser, options are 'template', 'expand-env'
|
||||
--encryption-key string the storage encryption key to use
|
||||
--mysql.database string the MySQL database name (default "authelia")
|
||||
--mysql.host string the MySQL hostname
|
||||
|
|
|
@ -44,6 +44,7 @@ authelia storage migrate history --encryption-key b3453fde-ecc2-4a1f-9422-2707dd
|
|||
|
||||
```
|
||||
-c, --config strings configuration files to load (default [configuration.yml])
|
||||
--config.experimental.filters strings applies filters in order to the configuration file before the YAML parser, options are 'template', 'expand-env'
|
||||
--encryption-key string the storage encryption key to use
|
||||
--mysql.database string the MySQL database name (default "authelia")
|
||||
--mysql.host string the MySQL hostname
|
||||
|
|
|
@ -45,6 +45,7 @@ authelia storage migrate list-down --encryption-key b3453fde-ecc2-4a1f-9422-2707
|
|||
|
||||
```
|
||||
-c, --config strings configuration files to load (default [configuration.yml])
|
||||
--config.experimental.filters strings applies filters in order to the configuration file before the YAML parser, options are 'template', 'expand-env'
|
||||
--encryption-key string the storage encryption key to use
|
||||
--mysql.database string the MySQL database name (default "authelia")
|
||||
--mysql.host string the MySQL hostname
|
||||
|
|
|
@ -45,6 +45,7 @@ authelia storage migrate list-up --encryption-key b3453fde-ecc2-4a1f-9422-2707dd
|
|||
|
||||
```
|
||||
-c, --config strings configuration files to load (default [configuration.yml])
|
||||
--config.experimental.filters strings applies filters in order to the configuration file before the YAML parser, options are 'template', 'expand-env'
|
||||
--encryption-key string the storage encryption key to use
|
||||
--mysql.database string the MySQL database name (default "authelia")
|
||||
--mysql.host string the MySQL hostname
|
||||
|
|
|
@ -47,6 +47,7 @@ authelia storage migrate up --encryption-key b3453fde-ecc2-4a1f-9422-2707ddbed49
|
|||
|
||||
```
|
||||
-c, --config strings configuration files to load (default [configuration.yml])
|
||||
--config.experimental.filters strings applies filters in order to the configuration file before the YAML parser, options are 'template', 'expand-env'
|
||||
--encryption-key string the storage encryption key to use
|
||||
--mysql.database string the MySQL database name (default "authelia")
|
||||
--mysql.host string the MySQL hostname
|
||||
|
|
|
@ -44,6 +44,7 @@ authelia storage schema-info --encryption-key b3453fde-ecc2-4a1f-9422-2707ddbed4
|
|||
|
||||
```
|
||||
-c, --config strings configuration files to load (default [configuration.yml])
|
||||
--config.experimental.filters strings applies filters in order to the configuration file before the YAML parser, options are 'template', 'expand-env'
|
||||
--encryption-key string the storage encryption key to use
|
||||
--mysql.database string the MySQL database name (default "authelia")
|
||||
--mysql.host string the MySQL hostname
|
||||
|
|
|
@ -38,6 +38,7 @@ authelia storage user --help
|
|||
|
||||
```
|
||||
-c, --config strings configuration files to load (default [configuration.yml])
|
||||
--config.experimental.filters strings applies filters in order to the configuration file before the YAML parser, options are 'template', 'expand-env'
|
||||
--encryption-key string the storage encryption key to use
|
||||
--mysql.database string the MySQL database name (default "authelia")
|
||||
--mysql.host string the MySQL hostname
|
||||
|
|
|
@ -38,6 +38,7 @@ authelia storage user identifiers --help
|
|||
|
||||
```
|
||||
-c, --config strings configuration files to load (default [configuration.yml])
|
||||
--config.experimental.filters strings applies filters in order to the configuration file before the YAML parser, options are 'template', 'expand-env'
|
||||
--encryption-key string the storage encryption key to use
|
||||
--mysql.database string the MySQL database name (default "authelia")
|
||||
--mysql.host string the MySQL hostname
|
||||
|
|
|
@ -47,6 +47,7 @@ authelia storage user identifiers add john --identifier f0919359-9d15-4e15-bcba-
|
|||
|
||||
```
|
||||
-c, --config strings configuration files to load (default [configuration.yml])
|
||||
--config.experimental.filters strings applies filters in order to the configuration file before the YAML parser, options are 'template', 'expand-env'
|
||||
--encryption-key string the storage encryption key to use
|
||||
--mysql.database string the MySQL database name (default "authelia")
|
||||
--mysql.host string the MySQL hostname
|
||||
|
|
|
@ -46,6 +46,7 @@ authelia storage user identifiers export --file export.yaml --encryption-key b34
|
|||
|
||||
```
|
||||
-c, --config strings configuration files to load (default [configuration.yml])
|
||||
--config.experimental.filters strings applies filters in order to the configuration file before the YAML parser, options are 'template', 'expand-env'
|
||||
--encryption-key string the storage encryption key to use
|
||||
--mysql.database string the MySQL database name (default "authelia")
|
||||
--mysql.host string the MySQL hostname
|
||||
|
|
|
@ -49,6 +49,7 @@ authelia storage user identifiers generate --users john,mary --services openid -
|
|||
|
||||
```
|
||||
-c, --config strings configuration files to load (default [configuration.yml])
|
||||
--config.experimental.filters strings applies filters in order to the configuration file before the YAML parser, options are 'template', 'expand-env'
|
||||
--encryption-key string the storage encryption key to use
|
||||
--mysql.database string the MySQL database name (default "authelia")
|
||||
--mysql.host string the MySQL hostname
|
||||
|
|
|
@ -49,6 +49,7 @@ authelia storage user identifiers import --file export.yaml --encryption-key b34
|
|||
|
||||
```
|
||||
-c, --config strings configuration files to load (default [configuration.yml])
|
||||
--config.experimental.filters strings applies filters in order to the configuration file before the YAML parser, options are 'template', 'expand-env'
|
||||
--encryption-key string the storage encryption key to use
|
||||
--mysql.database string the MySQL database name (default "authelia")
|
||||
--mysql.host string the MySQL hostname
|
||||
|
|
|
@ -38,6 +38,7 @@ authelia storage user totp --help
|
|||
|
||||
```
|
||||
-c, --config strings configuration files to load (default [configuration.yml])
|
||||
--config.experimental.filters strings applies filters in order to the configuration file before the YAML parser, options are 'template', 'expand-env'
|
||||
--encryption-key string the storage encryption key to use
|
||||
--mysql.database string the MySQL database name (default "authelia")
|
||||
--mysql.host string the MySQL hostname
|
||||
|
|
|
@ -44,6 +44,7 @@ authelia storage user totp delete john --encryption-key b3453fde-ecc2-4a1f-9422-
|
|||
|
||||
```
|
||||
-c, --config strings configuration files to load (default [configuration.yml])
|
||||
--config.experimental.filters strings applies filters in order to the configuration file before the YAML parser, options are 'template', 'expand-env'
|
||||
--encryption-key string the storage encryption key to use
|
||||
--mysql.database string the MySQL database name (default "authelia")
|
||||
--mysql.host string the MySQL hostname
|
||||
|
|
|
@ -47,6 +47,7 @@ authelia storage user totp export --format png --dir ./totp-qr --encryption-key
|
|||
|
||||
```
|
||||
-c, --config strings configuration files to load (default [configuration.yml])
|
||||
--config.experimental.filters strings applies filters in order to the configuration file before the YAML parser, options are 'template', 'expand-env'
|
||||
--encryption-key string the storage encryption key to use
|
||||
--mysql.database string the MySQL database name (default "authelia")
|
||||
--mysql.host string the MySQL hostname
|
||||
|
|
|
@ -56,6 +56,7 @@ authelia storage user totp generate john --algorithm SHA512 --config config.yml
|
|||
|
||||
```
|
||||
-c, --config strings configuration files to load (default [configuration.yml])
|
||||
--config.experimental.filters strings applies filters in order to the configuration file before the YAML parser, options are 'template', 'expand-env'
|
||||
--encryption-key string the storage encryption key to use
|
||||
--mysql.database string the MySQL database name (default "authelia")
|
||||
--mysql.host string the MySQL hostname
|
||||
|
|
|
@ -38,6 +38,7 @@ authelia storage user webauthn --help
|
|||
|
||||
```
|
||||
-c, --config strings configuration files to load (default [configuration.yml])
|
||||
--config.experimental.filters strings applies filters in order to the configuration file before the YAML parser, options are 'template', 'expand-env'
|
||||
--encryption-key string the storage encryption key to use
|
||||
--mysql.database string the MySQL database name (default "authelia")
|
||||
--mysql.host string the MySQL hostname
|
||||
|
|
|
@ -53,6 +53,7 @@ authelia storage user webauthn delete --kid abc123 --encryption-key b3453fde-ecc
|
|||
|
||||
```
|
||||
-c, --config strings configuration files to load (default [configuration.yml])
|
||||
--config.experimental.filters strings applies filters in order to the configuration file before the YAML parser, options are 'template', 'expand-env'
|
||||
--encryption-key string the storage encryption key to use
|
||||
--mysql.database string the MySQL database name (default "authelia")
|
||||
--mysql.host string the MySQL hostname
|
||||
|
|
|
@ -47,6 +47,7 @@ authelia storage user webauthn list john --encryption-key b3453fde-ecc2-4a1f-942
|
|||
|
||||
```
|
||||
-c, --config strings configuration files to load (default [configuration.yml])
|
||||
--config.experimental.filters strings applies filters in order to the configuration file before the YAML parser, options are 'template', 'expand-env'
|
||||
--encryption-key string the storage encryption key to use
|
||||
--mysql.database string the MySQL database name (default "authelia")
|
||||
--mysql.host string the MySQL hostname
|
||||
|
|
|
@ -37,8 +37,14 @@ authelia validate-config --config config.yml
|
|||
### Options
|
||||
|
||||
```
|
||||
-c, --config strings configuration files to load (default [configuration.yml])
|
||||
-h, --help help for validate-config
|
||||
-h, --help help for validate-config
|
||||
```
|
||||
|
||||
### Options inherited from parent commands
|
||||
|
||||
```
|
||||
-c, --config strings configuration files to load (default [configuration.yml])
|
||||
--config.experimental.filters strings applies filters in order to the configuration file before the YAML parser, options are 'template', 'expand-env'
|
||||
```
|
||||
|
||||
### SEE ALSO
|
||||
|
|
|
@ -8,5 +8,5 @@
|
|||
{{ else }}
|
||||
{{ errorf "No valid text variable or Inner content given"}}
|
||||
{{ end }}
|
||||
{{ end}}
|
||||
{{ end }}
|
||||
</div>
|
||||
|
|
|
@ -10,12 +10,10 @@ import (
|
|||
"github.com/spf13/cobra"
|
||||
|
||||
"github.com/authelia/authelia/v4/internal/authorization"
|
||||
"github.com/authelia/authelia/v4/internal/configuration"
|
||||
"github.com/authelia/authelia/v4/internal/configuration/schema"
|
||||
"github.com/authelia/authelia/v4/internal/configuration/validator"
|
||||
)
|
||||
|
||||
func newAccessControlCommand() (cmd *cobra.Command) {
|
||||
func newAccessControlCommand(ctx *CmdCtx) (cmd *cobra.Command) {
|
||||
cmd = &cobra.Command{
|
||||
Use: "access-control",
|
||||
Short: cmdAutheliaAccessControlShort,
|
||||
|
@ -26,25 +24,26 @@ func newAccessControlCommand() (cmd *cobra.Command) {
|
|||
}
|
||||
|
||||
cmd.AddCommand(
|
||||
newAccessControlCheckCommand(),
|
||||
newAccessControlCheckCommand(ctx),
|
||||
)
|
||||
|
||||
return cmd
|
||||
}
|
||||
|
||||
func newAccessControlCheckCommand() (cmd *cobra.Command) {
|
||||
func newAccessControlCheckCommand(ctx *CmdCtx) (cmd *cobra.Command) {
|
||||
cmd = &cobra.Command{
|
||||
Use: "check-policy",
|
||||
Short: cmdAutheliaAccessControlCheckPolicyShort,
|
||||
Long: cmdAutheliaAccessControlCheckPolicyLong,
|
||||
Example: cmdAutheliaAccessControlCheckPolicyExample,
|
||||
RunE: accessControlCheckRunE,
|
||||
PreRunE: ctx.ChainRunE(
|
||||
ctx.ConfigLoadRunE,
|
||||
),
|
||||
RunE: ctx.AccessControlCheckRunE,
|
||||
|
||||
DisableAutoGenTag: true,
|
||||
}
|
||||
|
||||
cmdWithConfigFlags(cmd, false, []string{"configuration.yml"})
|
||||
|
||||
cmd.Flags().String("url", "", "the url of the object")
|
||||
cmd.Flags().String("method", "GET", "the HTTP method of the object")
|
||||
cmd.Flags().String("username", "", "the username of the subject")
|
||||
|
@ -55,36 +54,14 @@ func newAccessControlCheckCommand() (cmd *cobra.Command) {
|
|||
return cmd
|
||||
}
|
||||
|
||||
func accessControlCheckRunE(cmd *cobra.Command, _ []string) (err error) {
|
||||
configs, err := cmd.Flags().GetStringSlice(cmdFlagNameConfig)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
func (ctx *CmdCtx) AccessControlCheckRunE(cmd *cobra.Command, _ []string) (err error) {
|
||||
validator.ValidateAccessControl(ctx.config, ctx.cconfig.validator)
|
||||
|
||||
sources := make([]configuration.Source, len(configs)+2)
|
||||
|
||||
for i, path := range configs {
|
||||
sources[i] = configuration.NewYAMLFileSource(path)
|
||||
}
|
||||
|
||||
sources[0+len(configs)] = configuration.NewEnvironmentSource(configuration.DefaultEnvPrefix, configuration.DefaultEnvDelimiter)
|
||||
sources[1+len(configs)] = configuration.NewSecretsSource(configuration.DefaultEnvPrefix, configuration.DefaultEnvDelimiter)
|
||||
|
||||
val := schema.NewStructValidator()
|
||||
|
||||
accessControlConfig := &schema.Configuration{}
|
||||
|
||||
if _, err = configuration.LoadAdvanced(val, "access_control", &accessControlConfig.AccessControl, sources...); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
validator.ValidateAccessControl(accessControlConfig, val)
|
||||
|
||||
if val.HasErrors() || val.HasWarnings() {
|
||||
if ctx.cconfig.validator.HasErrors() || ctx.cconfig.validator.HasWarnings() {
|
||||
return errors.New("your configuration has errors")
|
||||
}
|
||||
|
||||
authorizer := authorization.NewAuthorizer(accessControlConfig)
|
||||
authorizer := authorization.NewAuthorizer(ctx.config)
|
||||
|
||||
subject, object, err := getSubjectAndObjectFromFlags(cmd)
|
||||
if err != nil {
|
||||
|
@ -94,7 +71,7 @@ func accessControlCheckRunE(cmd *cobra.Command, _ []string) (err error) {
|
|||
results := authorizer.GetRuleMatchResults(subject, object)
|
||||
|
||||
if len(results) == 0 {
|
||||
fmt.Printf("\nThe default policy '%s' will be applied to ALL requests as no rules are configured.\n\n", accessControlConfig.AccessControl.DefaultPolicy)
|
||||
fmt.Printf("\nThe default policy '%s' will be applied to ALL requests as no rules are configured.\n\n", ctx.config.AccessControl.DefaultPolicy)
|
||||
|
||||
return nil
|
||||
}
|
||||
|
@ -104,7 +81,7 @@ func accessControlCheckRunE(cmd *cobra.Command, _ []string) (err error) {
|
|||
return err
|
||||
}
|
||||
|
||||
accessControlCheckWriteOutput(object, subject, results, accessControlConfig.AccessControl.DefaultPolicy, verbose)
|
||||
accessControlCheckWriteOutput(object, subject, results, ctx.config.AccessControl.DefaultPolicy, verbose)
|
||||
|
||||
return nil
|
||||
}
|
||||
|
|
|
@ -9,13 +9,13 @@ import (
|
|||
"github.com/authelia/authelia/v4/internal/utils"
|
||||
)
|
||||
|
||||
func newBuildInfoCmd() (cmd *cobra.Command) {
|
||||
func newBuildInfoCmd(ctx *CmdCtx) (cmd *cobra.Command) {
|
||||
cmd = &cobra.Command{
|
||||
Use: "build-info",
|
||||
Short: cmdAutheliaBuildInfoShort,
|
||||
Long: cmdAutheliaBuildInfoLong,
|
||||
Example: cmdAutheliaBuildInfoExample,
|
||||
RunE: cmdBuildInfoRunE,
|
||||
RunE: ctx.BuildInfoRunE,
|
||||
Args: cobra.NoArgs,
|
||||
|
||||
DisableAutoGenTag: true,
|
||||
|
@ -24,7 +24,8 @@ func newBuildInfoCmd() (cmd *cobra.Command) {
|
|||
return cmd
|
||||
}
|
||||
|
||||
func cmdBuildInfoRunE(_ *cobra.Command, _ []string) (err error) {
|
||||
// BuildInfoRunE is the RunE for the authelia build-info command.
|
||||
func (ctx *CmdCtx) BuildInfoRunE(_ *cobra.Command, _ []string) (err error) {
|
||||
_, err = fmt.Printf(fmtAutheliaBuild, utils.BuildTag, utils.BuildState, utils.BuildBranch, utils.BuildCommit,
|
||||
utils.BuildNumber, runtime.GOOS, runtime.GOARCH, utils.BuildDate, utils.BuildExtra)
|
||||
|
||||
|
|
|
@ -1,113 +0,0 @@
|
|||
package commands
|
||||
|
||||
import (
|
||||
"os"
|
||||
|
||||
"github.com/sirupsen/logrus"
|
||||
"github.com/spf13/cobra"
|
||||
|
||||
"github.com/authelia/authelia/v4/internal/configuration"
|
||||
"github.com/authelia/authelia/v4/internal/configuration/schema"
|
||||
"github.com/authelia/authelia/v4/internal/configuration/validator"
|
||||
"github.com/authelia/authelia/v4/internal/logging"
|
||||
)
|
||||
|
||||
// cmdWithConfigFlags is used for commands which require access to the configuration to add the flag to the command.
|
||||
func cmdWithConfigFlags(cmd *cobra.Command, persistent bool, configs []string) {
|
||||
if persistent {
|
||||
cmd.PersistentFlags().StringSliceP(cmdFlagNameConfig, "c", configs, "configuration files to load")
|
||||
} else {
|
||||
cmd.Flags().StringSliceP(cmdFlagNameConfig, "c", configs, "configuration files to load")
|
||||
}
|
||||
}
|
||||
|
||||
var config *schema.Configuration
|
||||
|
||||
func newCmdWithConfigPreRun(ensureConfigExists, validateKeys, validateConfiguration bool) func(cmd *cobra.Command, args []string) {
|
||||
return func(cmd *cobra.Command, _ []string) {
|
||||
var (
|
||||
logger *logrus.Logger
|
||||
err error
|
||||
|
||||
configs, filterNames []string
|
||||
|
||||
filters []configuration.FileFilter
|
||||
)
|
||||
|
||||
logger = logging.Logger()
|
||||
|
||||
if configs, err = cmd.Flags().GetStringSlice(cmdFlagNameConfig); err != nil {
|
||||
logger.Fatalf("Error reading flags: %v", err)
|
||||
}
|
||||
|
||||
if filterNames, err = cmd.Flags().GetStringSlice(cmdFlagNameConfigExpFilters); err != nil {
|
||||
logger.Fatalf("Error reading flags: %v", err)
|
||||
}
|
||||
|
||||
if filters, err = configuration.NewFileFilters(filterNames); err != nil {
|
||||
logger.Fatalf("Error occurred loading configuration: flag '--%s' is invalid: %v", cmdFlagNameConfigExpFilters, err)
|
||||
}
|
||||
|
||||
if ensureConfigExists && len(configs) == 1 {
|
||||
created, err := configuration.EnsureConfigurationExists(configs[0])
|
||||
if err != nil {
|
||||
logger.Fatal(err)
|
||||
}
|
||||
|
||||
if created {
|
||||
logger.Warnf("Configuration did not exist so a default one has been generated at %s, you will need to configure this", configs[0])
|
||||
os.Exit(0)
|
||||
}
|
||||
}
|
||||
|
||||
var (
|
||||
val *schema.StructValidator
|
||||
)
|
||||
|
||||
config, val, err = loadConfig(configs, validateKeys, validateConfiguration, filters...)
|
||||
if err != nil {
|
||||
logger.Fatalf("Error occurred loading configuration: %v", err)
|
||||
}
|
||||
|
||||
warnings := val.Warnings()
|
||||
if len(warnings) != 0 {
|
||||
for _, warning := range warnings {
|
||||
logger.Warnf("Configuration: %+v", warning)
|
||||
}
|
||||
}
|
||||
|
||||
errs := val.Errors()
|
||||
if len(errs) != 0 {
|
||||
for _, err := range errs {
|
||||
logger.Errorf("Configuration: %+v", err)
|
||||
}
|
||||
|
||||
logger.Fatalf("Can't continue due to the errors loading the configuration")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func loadConfig(configs []string, validateKeys, validateConfiguration bool, filters ...configuration.FileFilter) (c *schema.Configuration, val *schema.StructValidator, err error) {
|
||||
var keys []string
|
||||
|
||||
val = schema.NewStructValidator()
|
||||
|
||||
if keys, c, err = configuration.Load(val,
|
||||
configuration.NewDefaultSourcesFiltered(
|
||||
configs,
|
||||
filters,
|
||||
configuration.DefaultEnvPrefix,
|
||||
configuration.DefaultEnvDelimiter)...); err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
|
||||
if validateKeys {
|
||||
validator.ValidateKeys(keys, configuration.DefaultEnvPrefix, val)
|
||||
}
|
||||
|
||||
if validateConfiguration {
|
||||
validator.ValidateConfiguration(c, val)
|
||||
}
|
||||
|
||||
return c, val, nil
|
||||
}
|
|
@ -463,18 +463,6 @@ This subcommand allows generating an %s key pair.`
|
|||
cmdAutheliaCryptoPairECDSAGenerateExample = `authelia crypto pair ecdsa generate --help`
|
||||
|
||||
cmdAutheliaCryptoPairEd25519GenerateExample = `authelia crypto pair ed25519 generate --help`
|
||||
|
||||
cmdAutheliaHashPasswordShort = "Hash a password to be used in file-based users database"
|
||||
|
||||
cmdAutheliaHashPasswordLong = `Hash a password to be used in file-based users database.`
|
||||
|
||||
//nolint:gosec // This is an example.
|
||||
cmdAutheliaHashPasswordExample = `authelia hash-password -- 'mypass'
|
||||
authelia hash-password --sha512 -- 'mypass'
|
||||
authelia hash-password --iterations=4 -- 'mypass'
|
||||
authelia hash-password --memory=128 -- 'mypass'
|
||||
authelia hash-password --parallelism=1 -- 'mypass'
|
||||
authelia hash-password --key-length=64 -- 'mypass'`
|
||||
)
|
||||
|
||||
const (
|
||||
|
@ -544,8 +532,8 @@ const (
|
|||
cmdFlagNameKeySize = "key-size"
|
||||
cmdFlagNameSaltSize = "salt-size"
|
||||
cmdFlagNameProfile = "profile"
|
||||
cmdFlagNameSHA512 = "sha512"
|
||||
cmdFlagNameConfig = "config"
|
||||
|
||||
cmdFlagNameConfig = "config"
|
||||
|
||||
cmdFlagNameConfigExpFilters = "config.experimental.filters"
|
||||
|
||||
|
@ -598,7 +586,6 @@ const (
|
|||
)
|
||||
|
||||
const (
|
||||
cmdUseHashPassword = "hash-password [flags] -- [password]"
|
||||
cmdUseHash = "hash"
|
||||
cmdUseHashArgon2 = "argon2"
|
||||
cmdUseHashSHA2Crypt = "sha2crypt"
|
||||
|
@ -627,7 +614,8 @@ const (
|
|||
)
|
||||
|
||||
var (
|
||||
errNoStorageProvider = errors.New("no storage provider configured")
|
||||
errStorageSchemaOutdated = errors.New("storage schema outdated")
|
||||
errStorageSchemaIncompatible = errors.New("storage schema incompatible")
|
||||
)
|
||||
|
||||
const (
|
||||
|
|
|
@ -0,0 +1,365 @@
|
|||
package commands
|
||||
|
||||
import (
|
||||
"crypto/x509"
|
||||
"fmt"
|
||||
"os"
|
||||
"strings"
|
||||
|
||||
"github.com/sirupsen/logrus"
|
||||
"github.com/spf13/cobra"
|
||||
"github.com/spf13/pflag"
|
||||
"golang.org/x/net/context"
|
||||
"golang.org/x/sync/errgroup"
|
||||
|
||||
"github.com/authelia/authelia/v4/internal/authentication"
|
||||
"github.com/authelia/authelia/v4/internal/authorization"
|
||||
"github.com/authelia/authelia/v4/internal/configuration"
|
||||
"github.com/authelia/authelia/v4/internal/configuration/schema"
|
||||
"github.com/authelia/authelia/v4/internal/configuration/validator"
|
||||
"github.com/authelia/authelia/v4/internal/logging"
|
||||
"github.com/authelia/authelia/v4/internal/metrics"
|
||||
"github.com/authelia/authelia/v4/internal/middlewares"
|
||||
"github.com/authelia/authelia/v4/internal/notification"
|
||||
"github.com/authelia/authelia/v4/internal/ntp"
|
||||
"github.com/authelia/authelia/v4/internal/oidc"
|
||||
"github.com/authelia/authelia/v4/internal/regulation"
|
||||
"github.com/authelia/authelia/v4/internal/session"
|
||||
"github.com/authelia/authelia/v4/internal/templates"
|
||||
"github.com/authelia/authelia/v4/internal/totp"
|
||||
"github.com/authelia/authelia/v4/internal/utils"
|
||||
)
|
||||
|
||||
// NewCmdCtx returns a new CmdCtx.
|
||||
func NewCmdCtx() *CmdCtx {
|
||||
ctx := context.Background()
|
||||
|
||||
ctx, cancel := context.WithCancel(ctx)
|
||||
|
||||
group, ctx := errgroup.WithContext(ctx)
|
||||
|
||||
return &CmdCtx{
|
||||
Context: ctx,
|
||||
cancel: cancel,
|
||||
group: group,
|
||||
log: logging.Logger(),
|
||||
config: &schema.Configuration{},
|
||||
}
|
||||
}
|
||||
|
||||
// CmdCtx is a context.Context used for the root command.
|
||||
type CmdCtx struct {
|
||||
context.Context
|
||||
|
||||
cancel context.CancelFunc
|
||||
group *errgroup.Group
|
||||
|
||||
log *logrus.Logger
|
||||
|
||||
config *schema.Configuration
|
||||
providers middlewares.Providers
|
||||
trusted *x509.CertPool
|
||||
|
||||
cconfig *CmdCtxConfig
|
||||
}
|
||||
|
||||
// NewCmdCtxConfig returns a new CmdCtxConfig.
|
||||
func NewCmdCtxConfig() *CmdCtxConfig {
|
||||
return &CmdCtxConfig{
|
||||
validator: schema.NewStructValidator(),
|
||||
}
|
||||
}
|
||||
|
||||
// CmdCtxConfig is the configuration for the CmdCtx.
|
||||
type CmdCtxConfig struct {
|
||||
defaults configuration.Source
|
||||
sources []configuration.Source
|
||||
keys []string
|
||||
validator *schema.StructValidator
|
||||
}
|
||||
|
||||
// CobraRunECmd describes a function that can be used as a *cobra.Command RunE, PreRunE, or PostRunE.
|
||||
type CobraRunECmd func(cmd *cobra.Command, args []string) (err error)
|
||||
|
||||
// CheckSchemaVersion is a utility function which checks the schema version.
|
||||
func (ctx *CmdCtx) CheckSchemaVersion() (err error) {
|
||||
if ctx.providers.StorageProvider == nil {
|
||||
return fmt.Errorf("storage not loaded")
|
||||
}
|
||||
|
||||
var version, latest int
|
||||
|
||||
if version, err = ctx.providers.StorageProvider.SchemaVersion(ctx); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if latest, err = ctx.providers.StorageProvider.SchemaLatestVersion(); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
switch {
|
||||
case version > latest:
|
||||
return fmt.Errorf("%w: version %d is not compatible with this version of the binary as the latest compatible version is %d", errStorageSchemaIncompatible, version, latest)
|
||||
case version < latest:
|
||||
return fmt.Errorf("%w: version %d is outdated please migrate to version %d in order to use this command or use an older binary", errStorageSchemaOutdated, version, latest)
|
||||
default:
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
// LoadTrustedCertificates loads the trusted certificates into the CmdCtx.
|
||||
func (ctx *CmdCtx) LoadTrustedCertificates() (warns, errs []error) {
|
||||
ctx.trusted, warns, errs = utils.NewX509CertPool(ctx.config.CertificatesDirectory)
|
||||
|
||||
return warns, errs
|
||||
}
|
||||
|
||||
// LoadProviders loads all providers into the CmdCtx.
|
||||
func (ctx *CmdCtx) LoadProviders() (warns, errs []error) {
|
||||
// TODO: Adjust this so the CertPool can be used like a provider.
|
||||
if warns, errs = ctx.LoadTrustedCertificates(); len(warns) != 0 || len(errs) != 0 {
|
||||
return warns, errs
|
||||
}
|
||||
|
||||
storage := getStorageProvider(ctx)
|
||||
|
||||
providers := middlewares.Providers{
|
||||
Authorizer: authorization.NewAuthorizer(ctx.config),
|
||||
NTP: ntp.NewProvider(&ctx.config.NTP),
|
||||
PasswordPolicy: middlewares.NewPasswordPolicyProvider(ctx.config.PasswordPolicy),
|
||||
Regulator: regulation.NewRegulator(ctx.config.Regulation, storage, utils.RealClock{}),
|
||||
SessionProvider: session.NewProvider(ctx.config.Session, ctx.trusted),
|
||||
StorageProvider: storage,
|
||||
TOTP: totp.NewTimeBasedProvider(ctx.config.TOTP),
|
||||
}
|
||||
|
||||
var err error
|
||||
|
||||
switch {
|
||||
case ctx.config.AuthenticationBackend.File != nil:
|
||||
providers.UserProvider = authentication.NewFileUserProvider(ctx.config.AuthenticationBackend.File)
|
||||
case ctx.config.AuthenticationBackend.LDAP != nil:
|
||||
providers.UserProvider = authentication.NewLDAPUserProvider(ctx.config.AuthenticationBackend, ctx.trusted)
|
||||
}
|
||||
|
||||
if providers.Templates, err = templates.New(templates.Config{EmailTemplatesPath: ctx.config.Notifier.TemplatePath}); err != nil {
|
||||
errs = append(errs, err)
|
||||
}
|
||||
|
||||
switch {
|
||||
case ctx.config.Notifier.SMTP != nil:
|
||||
providers.Notifier = notification.NewSMTPNotifier(ctx.config.Notifier.SMTP, ctx.trusted, providers.Templates)
|
||||
case ctx.config.Notifier.FileSystem != nil:
|
||||
providers.Notifier = notification.NewFileNotifier(*ctx.config.Notifier.FileSystem)
|
||||
}
|
||||
|
||||
if providers.OpenIDConnect, err = oidc.NewOpenIDConnectProvider(ctx.config.IdentityProviders.OIDC, storage); err != nil {
|
||||
errs = append(errs, err)
|
||||
}
|
||||
|
||||
if ctx.config.Telemetry.Metrics.Enabled {
|
||||
providers.Metrics = metrics.NewPrometheus()
|
||||
}
|
||||
|
||||
ctx.providers = providers
|
||||
|
||||
return warns, errs
|
||||
}
|
||||
|
||||
// ChainRunE runs multiple CobraRunECmd funcs one after the other returning errors.
|
||||
func (ctx *CmdCtx) ChainRunE(cmdRunEs ...CobraRunECmd) CobraRunECmd {
|
||||
return func(cmd *cobra.Command, args []string) (err error) {
|
||||
for _, cmdRunE := range cmdRunEs {
|
||||
if err = cmdRunE(cmd, args); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
// ConfigSetFlagsMapRunE adds a command line source with flags mapping.
|
||||
func (ctx *CmdCtx) ConfigSetFlagsMapRunE(flags *pflag.FlagSet, flagsMap map[string]string, includeInvalidKeys, includeUnchangedKeys bool) (err error) {
|
||||
if ctx.cconfig == nil {
|
||||
ctx.cconfig = NewCmdCtxConfig()
|
||||
}
|
||||
|
||||
ctx.cconfig.sources = append(ctx.cconfig.sources, configuration.NewCommandLineSourceWithMapping(flags, flagsMap, includeInvalidKeys, includeUnchangedKeys))
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// ConfigSetDefaultsRunE adds a defaults configuration source.
|
||||
func (ctx *CmdCtx) ConfigSetDefaultsRunE(defaults map[string]any) CobraRunECmd {
|
||||
return func(cmd *cobra.Command, args []string) (err error) {
|
||||
if ctx.cconfig == nil {
|
||||
ctx.cconfig = NewCmdCtxConfig()
|
||||
}
|
||||
|
||||
ctx.cconfig.defaults = configuration.NewMapSource(defaults)
|
||||
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
// ConfigValidateKeysRunE validates the configuration (keys).
|
||||
func (ctx *CmdCtx) ConfigValidateKeysRunE(_ *cobra.Command, _ []string) (err error) {
|
||||
if ctx.cconfig == nil {
|
||||
return fmt.Errorf("config validate keys must be used with ConfigLoadRunE")
|
||||
}
|
||||
|
||||
validator.ValidateKeys(ctx.cconfig.keys, configuration.DefaultEnvPrefix, ctx.cconfig.validator)
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// ConfigValidateRunE validates the configuration (structure).
|
||||
func (ctx *CmdCtx) ConfigValidateRunE(_ *cobra.Command, _ []string) (err error) {
|
||||
validator.ValidateConfiguration(ctx.config, ctx.cconfig.validator)
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// ConfigValidateLogRunE logs the warnings and errors detected during the validations that have ran.
|
||||
func (ctx *CmdCtx) ConfigValidateLogRunE(_ *cobra.Command, _ []string) (err error) {
|
||||
warnings := ctx.cconfig.validator.Warnings()
|
||||
if len(warnings) != 0 {
|
||||
for _, warning := range warnings {
|
||||
ctx.log.Warnf("Configuration: %+v", warning)
|
||||
}
|
||||
}
|
||||
|
||||
errs := ctx.cconfig.validator.Errors()
|
||||
if len(errs) != 0 {
|
||||
for _, err = range errs {
|
||||
ctx.log.Errorf("Configuration: %+v", err)
|
||||
}
|
||||
|
||||
ctx.log.Fatalf("Can't continue due to the errors loading the configuration")
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// ConfigValidateSectionPasswordRunE validates the configuration (structure, password section).
|
||||
func (ctx *CmdCtx) ConfigValidateSectionPasswordRunE(cmd *cobra.Command, _ []string) (err error) {
|
||||
if ctx.config.AuthenticationBackend.File == nil {
|
||||
return fmt.Errorf("password configuration was not initialized")
|
||||
}
|
||||
|
||||
val := &schema.StructValidator{}
|
||||
|
||||
validator.ValidatePasswordConfiguration(&ctx.config.AuthenticationBackend.File.Password, val)
|
||||
|
||||
errs := val.Errors()
|
||||
|
||||
if len(errs) == 0 {
|
||||
return nil
|
||||
}
|
||||
|
||||
for i, e := range errs {
|
||||
if i == 0 {
|
||||
err = e
|
||||
continue
|
||||
}
|
||||
|
||||
err = fmt.Errorf("%v, %w", err, e)
|
||||
}
|
||||
|
||||
return fmt.Errorf("errors occurred validating the password configuration: %w", err)
|
||||
}
|
||||
|
||||
// ConfigEnsureExistsRunE logs the warnings and errors detected during the validations that have ran.
|
||||
func (ctx *CmdCtx) ConfigEnsureExistsRunE(cmd *cobra.Command, _ []string) (err error) {
|
||||
var (
|
||||
configs []string
|
||||
created bool
|
||||
)
|
||||
|
||||
if configs, _, err = loadEnvCLIStringSliceValue(cmd, "", cmdFlagNameConfig); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if len(configs) != 1 {
|
||||
return nil
|
||||
}
|
||||
|
||||
if created, err = configuration.EnsureConfigurationExists(configs[0]); err != nil {
|
||||
ctx.log.Fatal(err)
|
||||
}
|
||||
|
||||
if created {
|
||||
ctx.log.Warnf("Configuration did not exist so a default one has been generated at %s, you will need to configure this", configs[0])
|
||||
os.Exit(0)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// ConfigLoadRunE loads the configuration into the CmdCtx.
|
||||
func (ctx *CmdCtx) ConfigLoadRunE(cmd *cobra.Command, _ []string) (err error) {
|
||||
var (
|
||||
configs, filterNames []string
|
||||
|
||||
filters []configuration.FileFilter
|
||||
)
|
||||
|
||||
if configs, _, err = loadEnvCLIStringSliceValue(cmd, "", cmdFlagNameConfig); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if filterNames, _, err = loadEnvCLIStringSliceValue(cmd, "", cmdFlagNameConfigExpFilters); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if filters, err = configuration.NewFileFilters(filterNames); err != nil {
|
||||
return fmt.Errorf("error occurred loading configuration: flag '--%s' is invalid: %w", cmdFlagNameConfigExpFilters, err)
|
||||
}
|
||||
|
||||
if ctx.cconfig == nil {
|
||||
ctx.cconfig = NewCmdCtxConfig()
|
||||
}
|
||||
|
||||
if ctx.cconfig.keys, err = configuration.LoadAdvanced(
|
||||
ctx.cconfig.validator,
|
||||
"",
|
||||
ctx.config,
|
||||
configuration.NewDefaultSourcesWithDefaults(
|
||||
configs,
|
||||
filters,
|
||||
configuration.DefaultEnvPrefix,
|
||||
configuration.DefaultEnvDelimiter,
|
||||
ctx.cconfig.defaults,
|
||||
ctx.cconfig.sources...)...); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func loadEnvCLIStringSliceValue(cmd *cobra.Command, envKey, flagName string) (value []string, explicit bool, err error) { //nolint:unparam
|
||||
if cmd.Flags().Changed(flagName) {
|
||||
value, err = cmd.Flags().GetStringSlice(flagName)
|
||||
|
||||
return value, true, err
|
||||
}
|
||||
|
||||
var (
|
||||
env string
|
||||
ok bool
|
||||
)
|
||||
|
||||
if envKey != "" {
|
||||
env, ok = os.LookupEnv(envKey)
|
||||
}
|
||||
|
||||
switch {
|
||||
case ok && env != "":
|
||||
return strings.Split(env, ","), true, nil
|
||||
default:
|
||||
value, err = cmd.Flags().GetStringSlice(flagName)
|
||||
|
||||
return value, false, err
|
||||
}
|
||||
}
|
|
@ -15,7 +15,7 @@ import (
|
|||
"github.com/authelia/authelia/v4/internal/utils"
|
||||
)
|
||||
|
||||
func newCryptoCmd() (cmd *cobra.Command) {
|
||||
func newCryptoCmd(ctx *CmdCtx) (cmd *cobra.Command) {
|
||||
cmd = &cobra.Command{
|
||||
Use: cmdUseCrypto,
|
||||
Short: cmdAutheliaCryptoShort,
|
||||
|
@ -27,47 +27,35 @@ func newCryptoCmd() (cmd *cobra.Command) {
|
|||
}
|
||||
|
||||
cmd.AddCommand(
|
||||
newCryptoRandCmd(),
|
||||
newCryptoCertificateCmd(),
|
||||
newCryptoHashCmd(),
|
||||
newCryptoPairCmd(),
|
||||
newCryptoRandCmd(ctx),
|
||||
newCryptoCertificateCmd(ctx),
|
||||
newCryptoHashCmd(ctx),
|
||||
newCryptoPairCmd(ctx),
|
||||
)
|
||||
|
||||
return cmd
|
||||
}
|
||||
|
||||
func newCryptoRandCmd() (cmd *cobra.Command) {
|
||||
func newCryptoRandCmd(ctx *CmdCtx) (cmd *cobra.Command) {
|
||||
cmd = &cobra.Command{
|
||||
Use: cmdUseRand,
|
||||
Short: cmdAutheliaCryptoRandShort,
|
||||
Long: cmdAutheliaCryptoRandLong,
|
||||
Example: cmdAutheliaCryptoRandExample,
|
||||
Args: cobra.NoArgs,
|
||||
RunE: func(cmd *cobra.Command, args []string) (err error) {
|
||||
var (
|
||||
random string
|
||||
)
|
||||
|
||||
if random, err = flagsGetRandomCharacters(cmd.Flags(), cmdFlagNameLength, cmdFlagNameCharSet, cmdFlagNameCharacters); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
fmt.Printf("Random Value: %s\n", random)
|
||||
|
||||
return nil
|
||||
},
|
||||
RunE: ctx.CryptoRandRunE,
|
||||
|
||||
DisableAutoGenTag: true,
|
||||
}
|
||||
|
||||
cmd.Flags().StringP(cmdFlagNameCharSet, "c", cmdFlagValueCharSet, cmdFlagUsageCharset)
|
||||
cmd.Flags().StringP(cmdFlagNameCharSet, "x", cmdFlagValueCharSet, cmdFlagUsageCharset)
|
||||
cmd.Flags().String(cmdFlagNameCharacters, "", cmdFlagUsageCharacters)
|
||||
cmd.Flags().IntP(cmdFlagNameLength, "n", 72, cmdFlagUsageLength)
|
||||
|
||||
return cmd
|
||||
}
|
||||
|
||||
func newCryptoCertificateCmd() (cmd *cobra.Command) {
|
||||
func newCryptoCertificateCmd(ctx *CmdCtx) (cmd *cobra.Command) {
|
||||
cmd = &cobra.Command{
|
||||
Use: cmdUseCertificate,
|
||||
Short: cmdAutheliaCryptoCertificateShort,
|
||||
|
@ -79,15 +67,15 @@ func newCryptoCertificateCmd() (cmd *cobra.Command) {
|
|||
}
|
||||
|
||||
cmd.AddCommand(
|
||||
newCryptoCertificateSubCmd(cmdUseRSA),
|
||||
newCryptoCertificateSubCmd(cmdUseECDSA),
|
||||
newCryptoCertificateSubCmd(cmdUseEd25519),
|
||||
newCryptoCertificateSubCmd(ctx, cmdUseRSA),
|
||||
newCryptoCertificateSubCmd(ctx, cmdUseECDSA),
|
||||
newCryptoCertificateSubCmd(ctx, cmdUseEd25519),
|
||||
)
|
||||
|
||||
return cmd
|
||||
}
|
||||
|
||||
func newCryptoCertificateSubCmd(use string) (cmd *cobra.Command) {
|
||||
func newCryptoCertificateSubCmd(ctx *CmdCtx, use string) (cmd *cobra.Command) {
|
||||
useFmt := fmtCryptoCertificateUse(use)
|
||||
|
||||
cmd = &cobra.Command{
|
||||
|
@ -100,16 +88,16 @@ func newCryptoCertificateSubCmd(use string) (cmd *cobra.Command) {
|
|||
DisableAutoGenTag: true,
|
||||
}
|
||||
|
||||
cmd.AddCommand(newCryptoGenerateCmd(cmdUseCertificate, use), newCryptoCertificateRequestCmd(use))
|
||||
cmd.AddCommand(newCryptoGenerateCmd(ctx, cmdUseCertificate, use), newCryptoCertificateRequestCmd(ctx, use))
|
||||
|
||||
return cmd
|
||||
}
|
||||
|
||||
func newCryptoCertificateRequestCmd(algorithm string) (cmd *cobra.Command) {
|
||||
func newCryptoCertificateRequestCmd(ctx *CmdCtx, algorithm string) (cmd *cobra.Command) {
|
||||
cmd = &cobra.Command{
|
||||
Use: cmdUseRequest,
|
||||
Args: cobra.NoArgs,
|
||||
RunE: cryptoCertificateRequestRunE,
|
||||
RunE: ctx.CryptoCertificateRequestRunE,
|
||||
|
||||
DisableAutoGenTag: true,
|
||||
}
|
||||
|
@ -141,7 +129,7 @@ func newCryptoCertificateRequestCmd(algorithm string) (cmd *cobra.Command) {
|
|||
return cmd
|
||||
}
|
||||
|
||||
func newCryptoPairCmd() (cmd *cobra.Command) {
|
||||
func newCryptoPairCmd(ctx *CmdCtx) (cmd *cobra.Command) {
|
||||
cmd = &cobra.Command{
|
||||
Use: cmdUsePair,
|
||||
Short: cmdAutheliaCryptoPairShort,
|
||||
|
@ -153,15 +141,15 @@ func newCryptoPairCmd() (cmd *cobra.Command) {
|
|||
}
|
||||
|
||||
cmd.AddCommand(
|
||||
newCryptoPairSubCmd(cmdUseRSA),
|
||||
newCryptoPairSubCmd(cmdUseECDSA),
|
||||
newCryptoPairSubCmd(cmdUseEd25519),
|
||||
newCryptoPairSubCmd(ctx, cmdUseRSA),
|
||||
newCryptoPairSubCmd(ctx, cmdUseECDSA),
|
||||
newCryptoPairSubCmd(ctx, cmdUseEd25519),
|
||||
)
|
||||
|
||||
return cmd
|
||||
}
|
||||
|
||||
func newCryptoPairSubCmd(use string) (cmd *cobra.Command) {
|
||||
func newCryptoPairSubCmd(ctx *CmdCtx, use string) (cmd *cobra.Command) {
|
||||
var (
|
||||
example, useFmt string
|
||||
)
|
||||
|
@ -183,21 +171,21 @@ func newCryptoPairSubCmd(use string) (cmd *cobra.Command) {
|
|||
Long: fmt.Sprintf(cmdAutheliaCryptoPairSubLong, useFmt, useFmt),
|
||||
Example: example,
|
||||
Args: cobra.NoArgs,
|
||||
RunE: cryptoGenerateRunE,
|
||||
RunE: ctx.CryptoGenerateRunE,
|
||||
|
||||
DisableAutoGenTag: true,
|
||||
}
|
||||
|
||||
cmd.AddCommand(newCryptoGenerateCmd(cmdUsePair, use))
|
||||
cmd.AddCommand(newCryptoGenerateCmd(ctx, cmdUsePair, use))
|
||||
|
||||
return cmd
|
||||
}
|
||||
|
||||
func newCryptoGenerateCmd(category, algorithm string) (cmd *cobra.Command) {
|
||||
func newCryptoGenerateCmd(ctx *CmdCtx, category, algorithm string) (cmd *cobra.Command) {
|
||||
cmd = &cobra.Command{
|
||||
Use: cmdUseGenerate,
|
||||
Args: cobra.NoArgs,
|
||||
RunE: cryptoGenerateRunE,
|
||||
RunE: ctx.CryptoGenerateRunE,
|
||||
|
||||
DisableAutoGenTag: true,
|
||||
}
|
||||
|
@ -253,7 +241,23 @@ func newCryptoGenerateCmd(category, algorithm string) (cmd *cobra.Command) {
|
|||
return cmd
|
||||
}
|
||||
|
||||
func cryptoGenerateRunE(cmd *cobra.Command, args []string) (err error) {
|
||||
// CryptoRandRunE is the RunE for the authelia crypto rand command.
|
||||
func (ctx *CmdCtx) CryptoRandRunE(cmd *cobra.Command, args []string) (err error) {
|
||||
var (
|
||||
random string
|
||||
)
|
||||
|
||||
if random, err = flagsGetRandomCharacters(cmd.Flags(), cmdFlagNameLength, cmdFlagNameCharSet, cmdFlagNameCharacters); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
fmt.Printf("Random Value: %s\n", random)
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// CryptoGenerateRunE is the RunE for the authelia crypto [pair|certificate] [rsa|ecdsa|ed25519] commands.
|
||||
func (ctx *CmdCtx) CryptoGenerateRunE(cmd *cobra.Command, args []string) (err error) {
|
||||
var (
|
||||
privateKey any
|
||||
)
|
||||
|
@ -263,13 +267,14 @@ func cryptoGenerateRunE(cmd *cobra.Command, args []string) (err error) {
|
|||
}
|
||||
|
||||
if cmd.Parent().Parent().Use == cmdUseCertificate {
|
||||
return cryptoCertificateGenerateRunE(cmd, args, privateKey)
|
||||
return ctx.CryptoCertificateGenerateRunE(cmd, args, privateKey)
|
||||
}
|
||||
|
||||
return cryptoPairGenerateRunE(cmd, args, privateKey)
|
||||
return ctx.CryptoPairGenerateRunE(cmd, args, privateKey)
|
||||
}
|
||||
|
||||
func cryptoCertificateRequestRunE(cmd *cobra.Command, _ []string) (err error) {
|
||||
// CryptoCertificateRequestRunE is the RunE for the authelia crypto certificate request command.
|
||||
func (ctx *CmdCtx) CryptoCertificateRequestRunE(cmd *cobra.Command, _ []string) (err error) {
|
||||
var (
|
||||
privateKey any
|
||||
)
|
||||
|
@ -340,7 +345,8 @@ func cryptoCertificateRequestRunE(cmd *cobra.Command, _ []string) (err error) {
|
|||
return nil
|
||||
}
|
||||
|
||||
func cryptoCertificateGenerateRunE(cmd *cobra.Command, _ []string, privateKey any) (err error) {
|
||||
// CryptoCertificateGenerateRunE is the RunE for the authelia crypto certificate [rsa|ecdsa|ed25519] commands.
|
||||
func (ctx *CmdCtx) CryptoCertificateGenerateRunE(cmd *cobra.Command, _ []string, privateKey any) (err error) {
|
||||
var (
|
||||
template, caCertificate, parent *x509.Certificate
|
||||
publicKey, caPrivateKey, signatureKey any
|
||||
|
@ -432,7 +438,8 @@ func cryptoCertificateGenerateRunE(cmd *cobra.Command, _ []string, privateKey an
|
|||
return nil
|
||||
}
|
||||
|
||||
func cryptoPairGenerateRunE(cmd *cobra.Command, _ []string, privateKey any) (err error) {
|
||||
// CryptoPairGenerateRunE is the RunE for the authelia crypto pair [rsa|ecdsa|ed25519] commands.
|
||||
func (ctx *CmdCtx) CryptoPairGenerateRunE(cmd *cobra.Command, _ []string, privateKey any) (err error) {
|
||||
var (
|
||||
privateKeyPath, publicKeyPath string
|
||||
pkcs8 bool
|
||||
|
|
|
@ -7,69 +7,13 @@ import (
|
|||
"github.com/go-crypt/crypt"
|
||||
"github.com/go-crypt/crypt/algorithm"
|
||||
"github.com/spf13/cobra"
|
||||
"github.com/spf13/pflag"
|
||||
|
||||
"github.com/authelia/authelia/v4/internal/authentication"
|
||||
"github.com/authelia/authelia/v4/internal/configuration"
|
||||
"github.com/authelia/authelia/v4/internal/configuration/schema"
|
||||
"github.com/authelia/authelia/v4/internal/configuration/validator"
|
||||
)
|
||||
|
||||
func newHashPasswordCmd() (cmd *cobra.Command) {
|
||||
cmd = &cobra.Command{
|
||||
Use: cmdUseHashPassword,
|
||||
Short: cmdAutheliaHashPasswordShort,
|
||||
Long: cmdAutheliaHashPasswordLong,
|
||||
Example: cmdAutheliaHashPasswordExample,
|
||||
Args: cobra.MaximumNArgs(1),
|
||||
RunE: cmdHashPasswordRunE,
|
||||
|
||||
DisableAutoGenTag: true,
|
||||
}
|
||||
|
||||
cmdFlagConfig(cmd)
|
||||
|
||||
cmd.Flags().BoolP(cmdFlagNameSHA512, "z", false, fmt.Sprintf("use sha512 as the algorithm (changes iterations to %d, change with -i)", schema.DefaultPasswordConfig.SHA2Crypt.Iterations))
|
||||
cmd.Flags().IntP(cmdFlagNameIterations, "i", schema.DefaultPasswordConfig.Argon2.Iterations, "set the number of hashing iterations")
|
||||
cmd.Flags().IntP(cmdFlagNameMemory, "m", schema.DefaultPasswordConfig.Argon2.Memory, "[argon2id] set the amount of memory param (in MB)")
|
||||
cmd.Flags().IntP(cmdFlagNameParallelism, "p", schema.DefaultPasswordConfig.Argon2.Parallelism, "[argon2id] set the parallelism param")
|
||||
cmd.Flags().IntP("key-length", "k", schema.DefaultPasswordConfig.Argon2.KeyLength, "[argon2id] set the key length param")
|
||||
cmd.Flags().IntP("salt-length", "l", schema.DefaultPasswordConfig.Argon2.SaltLength, "set the auto-generated salt length")
|
||||
cmd.Flags().Bool(cmdFlagNameNoConfirm, false, "skip the password confirmation prompt")
|
||||
|
||||
return cmd
|
||||
}
|
||||
|
||||
func cmdHashPasswordRunE(cmd *cobra.Command, args []string) (err error) {
|
||||
var (
|
||||
flagsMap map[string]string
|
||||
sha512 bool
|
||||
)
|
||||
|
||||
if sha512, err = cmd.Flags().GetBool(cmdFlagNameSHA512); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
switch {
|
||||
case sha512:
|
||||
flagsMap = map[string]string{
|
||||
cmdFlagNameIterations: prefixFilePassword + ".sha2crypt.iterations",
|
||||
"salt-length": prefixFilePassword + ".sha2crypt.salt_length",
|
||||
}
|
||||
default:
|
||||
flagsMap = map[string]string{
|
||||
cmdFlagNameIterations: prefixFilePassword + ".argon2.iterations",
|
||||
"key-length": prefixFilePassword + ".argon2.key_length",
|
||||
"salt-length": prefixFilePassword + ".argon2.salt_length",
|
||||
cmdFlagNameParallelism: prefixFilePassword + ".argon2.parallelism",
|
||||
cmdFlagNameMemory: prefixFilePassword + ".argon2.memory",
|
||||
}
|
||||
}
|
||||
|
||||
return cmdCryptoHashGenerateFinish(cmd, args, flagsMap)
|
||||
}
|
||||
|
||||
func newCryptoHashCmd() (cmd *cobra.Command) {
|
||||
func newCryptoHashCmd(ctx *CmdCtx) (cmd *cobra.Command) {
|
||||
cmd = &cobra.Command{
|
||||
Use: cmdUseHash,
|
||||
Short: cmdAutheliaCryptoHashShort,
|
||||
|
@ -81,270 +25,15 @@ func newCryptoHashCmd() (cmd *cobra.Command) {
|
|||
}
|
||||
|
||||
cmd.AddCommand(
|
||||
newCryptoHashValidateCmd(),
|
||||
newCryptoHashGenerateCmd(),
|
||||
newCryptoHashValidateCmd(ctx),
|
||||
newCryptoHashGenerateCmd(ctx),
|
||||
)
|
||||
|
||||
return cmd
|
||||
}
|
||||
|
||||
func newCryptoHashGenerateCmd() (cmd *cobra.Command) {
|
||||
cmd = &cobra.Command{
|
||||
Use: cmdUseGenerate,
|
||||
Short: cmdAutheliaCryptoHashGenerateShort,
|
||||
Long: cmdAutheliaCryptoHashGenerateLong,
|
||||
Example: cmdAutheliaCryptoHashGenerateExample,
|
||||
RunE: func(cmd *cobra.Command, args []string) error {
|
||||
return cmdCryptoHashGenerateFinish(cmd, args, map[string]string{})
|
||||
},
|
||||
|
||||
DisableAutoGenTag: true,
|
||||
}
|
||||
|
||||
cmdFlagConfig(cmd)
|
||||
cmdFlagPassword(cmd, true)
|
||||
cmdFlagRandomPassword(cmd)
|
||||
|
||||
for _, use := range []string{cmdUseHashArgon2, cmdUseHashSHA2Crypt, cmdUseHashPBKDF2, cmdUseHashBCrypt, cmdUseHashSCrypt} {
|
||||
cmd.AddCommand(newCryptoHashGenerateSubCmd(use))
|
||||
}
|
||||
|
||||
return cmd
|
||||
}
|
||||
|
||||
func newCryptoHashGenerateSubCmd(use string) (cmd *cobra.Command) {
|
||||
useFmt := fmtCryptoHashUse(use)
|
||||
|
||||
cmd = &cobra.Command{
|
||||
Use: use,
|
||||
Short: fmt.Sprintf(fmtCmdAutheliaCryptoHashGenerateSubShort, useFmt),
|
||||
Long: fmt.Sprintf(fmtCmdAutheliaCryptoHashGenerateSubLong, useFmt, useFmt),
|
||||
Example: fmt.Sprintf(fmtCmdAutheliaCryptoHashGenerateSubExample, use),
|
||||
Args: cobra.NoArgs,
|
||||
RunE: func(cmd *cobra.Command, args []string) error {
|
||||
return nil
|
||||
},
|
||||
|
||||
DisableAutoGenTag: true,
|
||||
}
|
||||
|
||||
switch use {
|
||||
case cmdUseHashArgon2:
|
||||
cmdFlagIterations(cmd, schema.DefaultPasswordConfig.Argon2.Iterations)
|
||||
cmdFlagParallelism(cmd, schema.DefaultPasswordConfig.Argon2.Parallelism)
|
||||
cmdFlagKeySize(cmd, schema.DefaultPasswordConfig.Argon2.KeyLength)
|
||||
cmdFlagSaltSize(cmd, schema.DefaultPasswordConfig.Argon2.SaltLength)
|
||||
|
||||
cmd.Flags().StringP(cmdFlagNameVariant, "v", schema.DefaultPasswordConfig.Argon2.Variant, "variant, options are 'argon2id', 'argon2i', and 'argon2d'")
|
||||
cmd.Flags().IntP(cmdFlagNameMemory, "m", schema.DefaultPasswordConfig.Argon2.Memory, "memory in kibibytes")
|
||||
cmd.Flags().String(cmdFlagNameProfile, "", "profile to use, options are low-memory and recommended")
|
||||
|
||||
cmd.RunE = cryptoHashGenerateArgon2RunE
|
||||
case cmdUseHashSHA2Crypt:
|
||||
cmdFlagIterations(cmd, schema.DefaultPasswordConfig.SHA2Crypt.Iterations)
|
||||
cmdFlagSaltSize(cmd, schema.DefaultPasswordConfig.SHA2Crypt.SaltLength)
|
||||
|
||||
cmd.Flags().StringP(cmdFlagNameVariant, "v", schema.DefaultPasswordConfig.SHA2Crypt.Variant, "variant, options are sha256 and sha512")
|
||||
|
||||
cmd.RunE = cryptoHashGenerateSHA2CryptRunE
|
||||
case cmdUseHashPBKDF2:
|
||||
cmdFlagIterations(cmd, schema.DefaultPasswordConfig.PBKDF2.Iterations)
|
||||
cmdFlagSaltSize(cmd, schema.DefaultPasswordConfig.PBKDF2.SaltLength)
|
||||
|
||||
cmd.Flags().StringP(cmdFlagNameVariant, "v", schema.DefaultPasswordConfig.PBKDF2.Variant, "variant, options are 'sha1', 'sha224', 'sha256', 'sha384', and 'sha512'")
|
||||
|
||||
cmd.RunE = cryptoHashGeneratePBKDF2RunE
|
||||
case cmdUseHashBCrypt:
|
||||
cmd.Flags().StringP(cmdFlagNameVariant, "v", schema.DefaultPasswordConfig.BCrypt.Variant, "variant, options are 'standard' and 'sha256'")
|
||||
cmd.Flags().IntP(cmdFlagNameCost, "i", schema.DefaultPasswordConfig.BCrypt.Cost, "hashing cost")
|
||||
|
||||
cmd.RunE = cryptoHashGenerateBCryptRunE
|
||||
case cmdUseHashSCrypt:
|
||||
cmdFlagIterations(cmd, schema.DefaultPasswordConfig.SCrypt.Iterations)
|
||||
cmdFlagKeySize(cmd, schema.DefaultPasswordConfig.SCrypt.KeyLength)
|
||||
cmdFlagSaltSize(cmd, schema.DefaultPasswordConfig.SCrypt.SaltLength)
|
||||
cmdFlagParallelism(cmd, schema.DefaultPasswordConfig.SCrypt.Parallelism)
|
||||
|
||||
cmd.Flags().IntP(cmdFlagNameBlockSize, "r", schema.DefaultPasswordConfig.SCrypt.BlockSize, "block size")
|
||||
|
||||
cmd.RunE = cryptoHashGenerateSCryptRunE
|
||||
}
|
||||
|
||||
return cmd
|
||||
}
|
||||
|
||||
func cryptoHashGenerateArgon2RunE(cmd *cobra.Command, args []string) (err error) {
|
||||
flagsMap := map[string]string{
|
||||
cmdFlagNameVariant: prefixFilePassword + ".argon2.variant",
|
||||
cmdFlagNameIterations: prefixFilePassword + ".argon2.iterations",
|
||||
cmdFlagNameMemory: prefixFilePassword + ".argon2.memory",
|
||||
cmdFlagNameParallelism: prefixFilePassword + ".argon2.parallelism",
|
||||
cmdFlagNameKeySize: prefixFilePassword + ".argon2.key_length",
|
||||
cmdFlagNameSaltSize: prefixFilePassword + ".argon2.salt_length",
|
||||
}
|
||||
|
||||
return cmdCryptoHashGenerateFinish(cmd, args, flagsMap)
|
||||
}
|
||||
|
||||
func cryptoHashGenerateSHA2CryptRunE(cmd *cobra.Command, args []string) (err error) {
|
||||
flagsMap := map[string]string{
|
||||
cmdFlagNameVariant: prefixFilePassword + ".sha2crypt.variant",
|
||||
cmdFlagNameIterations: prefixFilePassword + ".sha2crypt.iterations",
|
||||
cmdFlagNameSaltSize: prefixFilePassword + ".sha2crypt.salt_length",
|
||||
}
|
||||
|
||||
return cmdCryptoHashGenerateFinish(cmd, args, flagsMap)
|
||||
}
|
||||
|
||||
func cryptoHashGeneratePBKDF2RunE(cmd *cobra.Command, args []string) (err error) {
|
||||
flagsMap := map[string]string{
|
||||
cmdFlagNameVariant: prefixFilePassword + ".pbkdf2.variant",
|
||||
cmdFlagNameIterations: prefixFilePassword + ".pbkdf2.iterations",
|
||||
cmdFlagNameKeySize: prefixFilePassword + ".pbkdf2.key_length",
|
||||
cmdFlagNameSaltSize: prefixFilePassword + ".pbkdf2.salt_length",
|
||||
}
|
||||
|
||||
return cmdCryptoHashGenerateFinish(cmd, args, flagsMap)
|
||||
}
|
||||
|
||||
func cryptoHashGenerateBCryptRunE(cmd *cobra.Command, args []string) (err error) {
|
||||
flagsMap := map[string]string{
|
||||
cmdFlagNameVariant: prefixFilePassword + ".bcrypt.variant",
|
||||
cmdFlagNameCost: prefixFilePassword + ".bcrypt.cost",
|
||||
}
|
||||
|
||||
return cmdCryptoHashGenerateFinish(cmd, args, flagsMap)
|
||||
}
|
||||
|
||||
func cryptoHashGenerateSCryptRunE(cmd *cobra.Command, args []string) (err error) {
|
||||
flagsMap := map[string]string{
|
||||
cmdFlagNameIterations: prefixFilePassword + ".scrypt.iterations",
|
||||
cmdFlagNameBlockSize: prefixFilePassword + ".scrypt.block_size",
|
||||
cmdFlagNameParallelism: prefixFilePassword + ".scrypt.parallelism",
|
||||
cmdFlagNameKeySize: prefixFilePassword + ".scrypt.key_length",
|
||||
cmdFlagNameSaltSize: prefixFilePassword + ".scrypt.salt_length",
|
||||
}
|
||||
|
||||
return cmdCryptoHashGenerateFinish(cmd, args, flagsMap)
|
||||
}
|
||||
|
||||
func newCryptoHashValidateCmd() (cmd *cobra.Command) {
|
||||
cmd = &cobra.Command{
|
||||
Use: fmt.Sprintf(cmdUseFmtValidate, cmdUseValidate),
|
||||
Short: cmdAutheliaCryptoHashValidateShort,
|
||||
Long: cmdAutheliaCryptoHashValidateLong,
|
||||
Example: cmdAutheliaCryptoHashValidateExample,
|
||||
Args: cobra.ExactArgs(1),
|
||||
RunE: func(cmd *cobra.Command, args []string) (err error) {
|
||||
var (
|
||||
password string
|
||||
valid bool
|
||||
)
|
||||
|
||||
if password, _, err = cmdCryptoHashGetPassword(cmd, args, false, false); err != nil {
|
||||
return fmt.Errorf("error occurred trying to obtain the password: %w", err)
|
||||
}
|
||||
|
||||
if len(password) == 0 {
|
||||
return fmt.Errorf("no password provided")
|
||||
}
|
||||
|
||||
if valid, err = crypt.CheckPassword(password, args[0]); err != nil {
|
||||
return fmt.Errorf("error occurred trying to validate the password against the digest: %w", err)
|
||||
}
|
||||
|
||||
switch {
|
||||
case valid:
|
||||
fmt.Println("The password matches the digest.")
|
||||
default:
|
||||
fmt.Println("The password does not match the digest.")
|
||||
}
|
||||
|
||||
return nil
|
||||
},
|
||||
|
||||
DisableAutoGenTag: true,
|
||||
}
|
||||
|
||||
cmdFlagPassword(cmd, false)
|
||||
|
||||
return cmd
|
||||
}
|
||||
|
||||
func cmdCryptoHashGenerateFinish(cmd *cobra.Command, args []string, flagsMap map[string]string) (err error) {
|
||||
var (
|
||||
algName string
|
||||
configs []string
|
||||
|
||||
c schema.Password
|
||||
)
|
||||
|
||||
if configs, err = cmd.Flags().GetStringSlice(cmdFlagNameConfig); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// Skip config if the flag wasn't set and the default is non-existent.
|
||||
if !cmd.Flags().Changed(cmdFlagNameConfig) {
|
||||
configs = configFilterExisting(configs)
|
||||
}
|
||||
|
||||
legacy := cmd.Use == cmdUseHashPassword
|
||||
|
||||
switch {
|
||||
case cmd.Use == cmdUseGenerate:
|
||||
break
|
||||
case legacy:
|
||||
if sha512, _ := cmd.Flags().GetBool(cmdFlagNameSHA512); sha512 {
|
||||
algName = cmdUseHashSHA2Crypt
|
||||
} else {
|
||||
algName = cmdUseHashArgon2
|
||||
}
|
||||
default:
|
||||
algName = cmd.Use
|
||||
}
|
||||
|
||||
if c, err = cmdCryptoHashGetConfig(algName, configs, cmd.Flags(), flagsMap); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if legacy && algName == cmdUseHashArgon2 && cmd.Flags().Changed(cmdFlagNameMemory) {
|
||||
c.Argon2.Memory *= 1024
|
||||
}
|
||||
|
||||
var (
|
||||
hash algorithm.Hash
|
||||
digest algorithm.Digest
|
||||
password string
|
||||
random bool
|
||||
)
|
||||
|
||||
if password, random, err = cmdCryptoHashGetPassword(cmd, args, legacy, !legacy); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if len(password) == 0 {
|
||||
return fmt.Errorf("no password provided")
|
||||
}
|
||||
|
||||
if hash, err = authentication.NewFileCryptoHashFromConfig(c); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if digest, err = hash.Hash(password); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if random {
|
||||
fmt.Printf("Random Password: %s\n", password)
|
||||
}
|
||||
|
||||
fmt.Printf("Digest: %s\n", digest.Encode())
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func cmdCryptoHashGetConfig(algorithm string, configs []string, flags *pflag.FlagSet, flagsMap map[string]string) (c schema.Password, err error) {
|
||||
mapDefaults := map[string]interface{}{
|
||||
func newCryptoHashGenerateCmd(ctx *CmdCtx) (cmd *cobra.Command) {
|
||||
defaults := map[string]any{
|
||||
prefixFilePassword + ".algorithm": schema.DefaultPasswordConfig.Algorithm,
|
||||
prefixFilePassword + ".argon2.variant": schema.DefaultPasswordConfig.Argon2.Variant,
|
||||
prefixFilePassword + ".argon2.iterations": schema.DefaultPasswordConfig.Argon2.Iterations,
|
||||
|
@ -367,44 +56,246 @@ func cmdCryptoHashGetConfig(algorithm string, configs []string, flags *pflag.Fla
|
|||
prefixFilePassword + ".scrypt.salt_length": schema.DefaultPasswordConfig.SCrypt.SaltLength,
|
||||
}
|
||||
|
||||
sources := configuration.NewDefaultSourcesWithDefaults(
|
||||
configs,
|
||||
nil,
|
||||
configuration.DefaultEnvPrefix, configuration.DefaultEnvDelimiter,
|
||||
configuration.NewMapSource(mapDefaults),
|
||||
configuration.NewCommandLineSourceWithMapping(flags, flagsMap, false, false),
|
||||
cmd = &cobra.Command{
|
||||
Use: cmdUseGenerate,
|
||||
Short: cmdAutheliaCryptoHashGenerateShort,
|
||||
Long: cmdAutheliaCryptoHashGenerateLong,
|
||||
Example: cmdAutheliaCryptoHashGenerateExample,
|
||||
PreRunE: ctx.ChainRunE(
|
||||
ctx.ConfigSetDefaultsRunE(defaults),
|
||||
ctx.CryptoHashGenerateMapFlagsPreRunE,
|
||||
ctx.ConfigLoadRunE,
|
||||
ctx.ConfigValidateSectionPasswordRunE,
|
||||
),
|
||||
RunE: ctx.CryptoHashGenerateRunE,
|
||||
|
||||
DisableAutoGenTag: true,
|
||||
}
|
||||
|
||||
cmdFlagPassword(cmd, true)
|
||||
cmdFlagRandomPassword(cmd)
|
||||
|
||||
for _, use := range []string{cmdUseHashArgon2, cmdUseHashSHA2Crypt, cmdUseHashPBKDF2, cmdUseHashBCrypt, cmdUseHashSCrypt} {
|
||||
cmd.AddCommand(newCryptoHashGenerateSubCmd(ctx, use))
|
||||
}
|
||||
|
||||
return cmd
|
||||
}
|
||||
|
||||
func newCryptoHashGenerateSubCmd(ctx *CmdCtx, use string) (cmd *cobra.Command) {
|
||||
defaults := map[string]any{
|
||||
prefixFilePassword + ".algorithm": schema.DefaultPasswordConfig.Algorithm,
|
||||
prefixFilePassword + ".argon2.variant": schema.DefaultPasswordConfig.Argon2.Variant,
|
||||
prefixFilePassword + ".argon2.iterations": schema.DefaultPasswordConfig.Argon2.Iterations,
|
||||
prefixFilePassword + ".argon2.memory": schema.DefaultPasswordConfig.Argon2.Memory,
|
||||
prefixFilePassword + ".argon2.parallelism": schema.DefaultPasswordConfig.Argon2.Parallelism,
|
||||
prefixFilePassword + ".argon2.key_length": schema.DefaultPasswordConfig.Argon2.KeyLength,
|
||||
prefixFilePassword + ".argon2.salt_length": schema.DefaultPasswordConfig.Argon2.SaltLength,
|
||||
prefixFilePassword + ".sha2crypt.variant": schema.DefaultPasswordConfig.SHA2Crypt.Variant,
|
||||
prefixFilePassword + ".sha2crypt.iterations": schema.DefaultPasswordConfig.SHA2Crypt.Iterations,
|
||||
prefixFilePassword + ".sha2crypt.salt_length": schema.DefaultPasswordConfig.SHA2Crypt.SaltLength,
|
||||
prefixFilePassword + ".pbkdf2.variant": schema.DefaultPasswordConfig.PBKDF2.Variant,
|
||||
prefixFilePassword + ".pbkdf2.iterations": schema.DefaultPasswordConfig.PBKDF2.Iterations,
|
||||
prefixFilePassword + ".pbkdf2.salt_length": schema.DefaultPasswordConfig.PBKDF2.SaltLength,
|
||||
prefixFilePassword + ".bcrypt.variant": schema.DefaultPasswordConfig.BCrypt.Variant,
|
||||
prefixFilePassword + ".bcrypt.cost": schema.DefaultPasswordConfig.BCrypt.Cost,
|
||||
prefixFilePassword + ".scrypt.iterations": schema.DefaultPasswordConfig.SCrypt.Iterations,
|
||||
prefixFilePassword + ".scrypt.block_size": schema.DefaultPasswordConfig.SCrypt.BlockSize,
|
||||
prefixFilePassword + ".scrypt.parallelism": schema.DefaultPasswordConfig.SCrypt.Parallelism,
|
||||
prefixFilePassword + ".scrypt.key_length": schema.DefaultPasswordConfig.SCrypt.KeyLength,
|
||||
prefixFilePassword + ".scrypt.salt_length": schema.DefaultPasswordConfig.SCrypt.SaltLength,
|
||||
}
|
||||
|
||||
useFmt := fmtCryptoHashUse(use)
|
||||
|
||||
cmd = &cobra.Command{
|
||||
Use: use,
|
||||
Short: fmt.Sprintf(fmtCmdAutheliaCryptoHashGenerateSubShort, useFmt),
|
||||
Long: fmt.Sprintf(fmtCmdAutheliaCryptoHashGenerateSubLong, useFmt, useFmt),
|
||||
Example: fmt.Sprintf(fmtCmdAutheliaCryptoHashGenerateSubExample, use),
|
||||
Args: cobra.NoArgs,
|
||||
PersistentPreRunE: ctx.ChainRunE(
|
||||
ctx.ConfigSetDefaultsRunE(defaults),
|
||||
ctx.CryptoHashGenerateMapFlagsPreRunE,
|
||||
ctx.ConfigLoadRunE,
|
||||
ctx.ConfigValidateSectionPasswordRunE,
|
||||
),
|
||||
RunE: ctx.CryptoHashGenerateRunE,
|
||||
|
||||
DisableAutoGenTag: true,
|
||||
}
|
||||
|
||||
switch use {
|
||||
case cmdUseHashArgon2:
|
||||
cmdFlagIterations(cmd, schema.DefaultPasswordConfig.Argon2.Iterations)
|
||||
cmdFlagParallelism(cmd, schema.DefaultPasswordConfig.Argon2.Parallelism)
|
||||
cmdFlagKeySize(cmd, schema.DefaultPasswordConfig.Argon2.KeyLength)
|
||||
cmdFlagSaltSize(cmd, schema.DefaultPasswordConfig.Argon2.SaltLength)
|
||||
|
||||
cmd.Flags().StringP(cmdFlagNameVariant, "v", schema.DefaultPasswordConfig.Argon2.Variant, "variant, options are 'argon2id', 'argon2i', and 'argon2d'")
|
||||
cmd.Flags().IntP(cmdFlagNameMemory, "m", schema.DefaultPasswordConfig.Argon2.Memory, "memory in kibibytes")
|
||||
cmd.Flags().String(cmdFlagNameProfile, "", "profile to use, options are low-memory and recommended")
|
||||
case cmdUseHashSHA2Crypt:
|
||||
cmdFlagIterations(cmd, schema.DefaultPasswordConfig.SHA2Crypt.Iterations)
|
||||
cmdFlagSaltSize(cmd, schema.DefaultPasswordConfig.SHA2Crypt.SaltLength)
|
||||
|
||||
cmd.Flags().StringP(cmdFlagNameVariant, "v", schema.DefaultPasswordConfig.SHA2Crypt.Variant, "variant, options are sha256 and sha512")
|
||||
cmd.PreRunE = ctx.ChainRunE()
|
||||
case cmdUseHashPBKDF2:
|
||||
cmdFlagIterations(cmd, schema.DefaultPasswordConfig.PBKDF2.Iterations)
|
||||
cmdFlagSaltSize(cmd, schema.DefaultPasswordConfig.PBKDF2.SaltLength)
|
||||
|
||||
cmd.Flags().StringP(cmdFlagNameVariant, "v", schema.DefaultPasswordConfig.PBKDF2.Variant, "variant, options are 'sha1', 'sha224', 'sha256', 'sha384', and 'sha512'")
|
||||
case cmdUseHashBCrypt:
|
||||
cmd.Flags().StringP(cmdFlagNameVariant, "v", schema.DefaultPasswordConfig.BCrypt.Variant, "variant, options are 'standard' and 'sha256'")
|
||||
cmd.Flags().IntP(cmdFlagNameCost, "i", schema.DefaultPasswordConfig.BCrypt.Cost, "hashing cost")
|
||||
case cmdUseHashSCrypt:
|
||||
cmdFlagIterations(cmd, schema.DefaultPasswordConfig.SCrypt.Iterations)
|
||||
cmdFlagKeySize(cmd, schema.DefaultPasswordConfig.SCrypt.KeyLength)
|
||||
cmdFlagSaltSize(cmd, schema.DefaultPasswordConfig.SCrypt.SaltLength)
|
||||
cmdFlagParallelism(cmd, schema.DefaultPasswordConfig.SCrypt.Parallelism)
|
||||
|
||||
cmd.Flags().IntP(cmdFlagNameBlockSize, "r", schema.DefaultPasswordConfig.SCrypt.BlockSize, "block size")
|
||||
}
|
||||
|
||||
return cmd
|
||||
}
|
||||
|
||||
func newCryptoHashValidateCmd(ctx *CmdCtx) (cmd *cobra.Command) {
|
||||
cmd = &cobra.Command{
|
||||
Use: fmt.Sprintf(cmdUseFmtValidate, cmdUseValidate),
|
||||
Short: cmdAutheliaCryptoHashValidateShort,
|
||||
Long: cmdAutheliaCryptoHashValidateLong,
|
||||
Example: cmdAutheliaCryptoHashValidateExample,
|
||||
Args: cobra.ExactArgs(1),
|
||||
RunE: ctx.CryptoHashValidateRunE,
|
||||
|
||||
DisableAutoGenTag: true,
|
||||
}
|
||||
|
||||
cmdFlagPassword(cmd, false)
|
||||
|
||||
return cmd
|
||||
}
|
||||
|
||||
// CryptoHashValidateRunE is the RunE for the authelia crypto hash validate command.
|
||||
func (ctx *CmdCtx) CryptoHashValidateRunE(cmd *cobra.Command, args []string) (err error) {
|
||||
var (
|
||||
password string
|
||||
valid bool
|
||||
)
|
||||
|
||||
if algorithm != "" {
|
||||
alg := map[string]interface{}{prefixFilePassword + ".algorithm": algorithm}
|
||||
|
||||
sources = append(sources, configuration.NewMapSource(alg))
|
||||
if password, _, err = cmdCryptoHashGetPassword(cmd, args, false, false); err != nil {
|
||||
return fmt.Errorf("error occurred trying to obtain the password: %w", err)
|
||||
}
|
||||
|
||||
val := schema.NewStructValidator()
|
||||
|
||||
if _, err = configuration.LoadAdvanced(val, prefixFilePassword, &c, sources...); err != nil {
|
||||
return schema.Password{}, fmt.Errorf("error occurred loading configuration: %w", err)
|
||||
if len(password) == 0 {
|
||||
return fmt.Errorf("no password provided")
|
||||
}
|
||||
|
||||
validator.ValidatePasswordConfiguration(&c, val)
|
||||
if valid, err = crypt.CheckPassword(password, args[0]); err != nil {
|
||||
return fmt.Errorf("error occurred trying to validate the password against the digest: %w", err)
|
||||
}
|
||||
|
||||
errs := val.Errors()
|
||||
switch {
|
||||
case valid:
|
||||
fmt.Println("The password matches the digest.")
|
||||
default:
|
||||
fmt.Println("The password does not match the digest.")
|
||||
}
|
||||
|
||||
if len(errs) != 0 {
|
||||
for i, e := range errs {
|
||||
if i == 0 {
|
||||
err = e
|
||||
continue
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
err = fmt.Errorf("%v, %w", err, e)
|
||||
// CryptoHashGenerateMapFlagsPreRunE is the RunE which configures the flags map configuration source for the
|
||||
// authelia crypto hash generate commands.
|
||||
func (ctx *CmdCtx) CryptoHashGenerateMapFlagsPreRunE(cmd *cobra.Command, args []string) (err error) {
|
||||
var flagsMap map[string]string
|
||||
|
||||
switch cmd.Use {
|
||||
case cmdUseHashArgon2:
|
||||
flagsMap = map[string]string{
|
||||
cmdFlagNameVariant: prefixFilePassword + ".argon2.variant",
|
||||
cmdFlagNameIterations: prefixFilePassword + ".argon2.iterations",
|
||||
cmdFlagNameMemory: prefixFilePassword + ".argon2.memory",
|
||||
cmdFlagNameParallelism: prefixFilePassword + ".argon2.parallelism",
|
||||
cmdFlagNameKeySize: prefixFilePassword + ".argon2.key_length",
|
||||
cmdFlagNameSaltSize: prefixFilePassword + ".argon2.salt_length",
|
||||
}
|
||||
case cmdUseHashSHA2Crypt:
|
||||
flagsMap = map[string]string{
|
||||
cmdFlagNameVariant: prefixFilePassword + ".sha2crypt.variant",
|
||||
cmdFlagNameIterations: prefixFilePassword + ".sha2crypt.iterations",
|
||||
cmdFlagNameSaltSize: prefixFilePassword + ".sha2crypt.salt_length",
|
||||
}
|
||||
case cmdUseHashPBKDF2:
|
||||
flagsMap = map[string]string{
|
||||
cmdFlagNameVariant: prefixFilePassword + ".pbkdf2.variant",
|
||||
cmdFlagNameIterations: prefixFilePassword + ".pbkdf2.iterations",
|
||||
cmdFlagNameKeySize: prefixFilePassword + ".pbkdf2.key_length",
|
||||
cmdFlagNameSaltSize: prefixFilePassword + ".pbkdf2.salt_length",
|
||||
}
|
||||
case cmdUseHashBCrypt:
|
||||
flagsMap = map[string]string{
|
||||
cmdFlagNameVariant: prefixFilePassword + ".bcrypt.variant",
|
||||
cmdFlagNameCost: prefixFilePassword + ".bcrypt.cost",
|
||||
}
|
||||
case cmdUseHashSCrypt:
|
||||
flagsMap = map[string]string{
|
||||
cmdFlagNameIterations: prefixFilePassword + ".scrypt.iterations",
|
||||
cmdFlagNameBlockSize: prefixFilePassword + ".scrypt.block_size",
|
||||
cmdFlagNameParallelism: prefixFilePassword + ".scrypt.parallelism",
|
||||
cmdFlagNameKeySize: prefixFilePassword + ".scrypt.key_length",
|
||||
cmdFlagNameSaltSize: prefixFilePassword + ".scrypt.salt_length",
|
||||
}
|
||||
|
||||
return schema.Password{}, fmt.Errorf("errors occurred validating the password configuration: %w", err)
|
||||
}
|
||||
|
||||
return c, nil
|
||||
if flagsMap != nil {
|
||||
ctx.cconfig.sources = append(ctx.cconfig.sources, configuration.NewCommandLineSourceWithMapping(cmd.Flags(), flagsMap, false, false))
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// CryptoHashGenerateRunE is the RunE for the authelia crypto hash generate commands.
|
||||
func (ctx *CmdCtx) CryptoHashGenerateRunE(cmd *cobra.Command, args []string) (err error) {
|
||||
var (
|
||||
hash algorithm.Hash
|
||||
digest algorithm.Digest
|
||||
password string
|
||||
random bool
|
||||
)
|
||||
|
||||
if password, random, err = cmdCryptoHashGetPassword(cmd, args, false, true); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if len(password) == 0 {
|
||||
return fmt.Errorf("no password provided")
|
||||
}
|
||||
|
||||
switch cmd.Use {
|
||||
case cmdUseGenerate:
|
||||
break
|
||||
default:
|
||||
ctx.config.AuthenticationBackend.File.Password.Algorithm = cmd.Use
|
||||
}
|
||||
|
||||
if hash, err = authentication.NewFileCryptoHashFromConfig(ctx.config.AuthenticationBackend.File.Password); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if digest, err = hash.Hash(password); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if random {
|
||||
fmt.Printf("Random Password: %s\n", password)
|
||||
}
|
||||
|
||||
fmt.Printf("Digest: %s\n", digest.Encode())
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func cmdCryptoHashGetPassword(cmd *cobra.Command, args []string, useArgs, useRandom bool) (password string, random bool, err error) {
|
||||
|
@ -430,18 +321,15 @@ func cmdCryptoHashGetPassword(cmd *cobra.Command, args []string, useArgs, useRan
|
|||
}
|
||||
|
||||
var (
|
||||
data []byte
|
||||
noConfirm bool
|
||||
)
|
||||
|
||||
if data, err = termReadPasswordWithPrompt("Enter Password: ", "password"); err != nil {
|
||||
if password, err = termReadPasswordWithPrompt("Enter Password: ", "password"); err != nil {
|
||||
err = fmt.Errorf("failed to read the password from the terminal: %w", err)
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
password = string(data)
|
||||
|
||||
if cmd.Use == fmt.Sprintf(cmdUseFmtValidate, cmdUseValidate) {
|
||||
fmt.Println("")
|
||||
|
||||
|
@ -449,11 +337,13 @@ func cmdCryptoHashGetPassword(cmd *cobra.Command, args []string, useArgs, useRan
|
|||
}
|
||||
|
||||
if noConfirm, err = cmd.Flags().GetBool(cmdFlagNameNoConfirm); err == nil && !noConfirm {
|
||||
if data, err = termReadPasswordWithPrompt("Confirm Password: ", ""); err != nil {
|
||||
var confirm string
|
||||
|
||||
if confirm, err = termReadPasswordWithPrompt("Confirm Password: ", ""); err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
if password != string(data) {
|
||||
if password != confirm {
|
||||
fmt.Println("")
|
||||
|
||||
err = fmt.Errorf("the password did not match the confirmation password")
|
||||
|
@ -467,10 +357,6 @@ func cmdCryptoHashGetPassword(cmd *cobra.Command, args []string, useArgs, useRan
|
|||
return
|
||||
}
|
||||
|
||||
func cmdFlagConfig(cmd *cobra.Command) {
|
||||
cmd.PersistentFlags().StringSliceP(cmdFlagNameConfig, "c", []string{"configuration.yml"}, "configuration files to load")
|
||||
}
|
||||
|
||||
func cmdFlagPassword(cmd *cobra.Command, noConfirm bool) {
|
||||
cmd.PersistentFlags().String(cmdFlagNamePassword, "", "manually supply the password rather than using the terminal prompt")
|
||||
|
||||
|
|
|
@ -24,7 +24,7 @@ import (
|
|||
func cmdFlagsCryptoCertificateCommon(cmd *cobra.Command) {
|
||||
cmd.Flags().String(cmdFlagNameSignature, "SHA256", "signature algorithm for the certificate")
|
||||
|
||||
cmd.Flags().StringP(cmdFlagNameCommonName, "c", "", "certificate common name")
|
||||
cmd.Flags().StringP(cmdFlagNameCommonName, "n", "", "certificate common name")
|
||||
cmd.Flags().StringSliceP(cmdFlagNameOrganization, "o", []string{"Authelia"}, "certificate organization")
|
||||
cmd.Flags().StringSlice(cmdFlagNameOrganizationalUnit, nil, "certificate organizational unit")
|
||||
cmd.Flags().StringSlice(cmdFlagNameCountry, nil, "certificate country")
|
||||
|
|
|
@ -6,3 +6,6 @@ import (
|
|||
|
||||
// ErrStdinIsNotTerminal is returned when Stdin is not an interactive terminal.
|
||||
var ErrStdinIsNotTerminal = errors.New("stdin is not a terminal")
|
||||
|
||||
// ErrConfirmationMismatch is returned when user input does not match the confirmation prompt.
|
||||
var ErrConfirmationMismatch = errors.New("user input didn't match the confirmation prompt")
|
||||
|
|
|
@ -1,115 +1,121 @@
|
|||
package commands
|
||||
|
||||
import (
|
||||
"crypto/x509"
|
||||
"encoding/base32"
|
||||
"errors"
|
||||
"fmt"
|
||||
|
||||
"github.com/authelia/authelia/v4/internal/authentication"
|
||||
"github.com/authelia/authelia/v4/internal/authorization"
|
||||
"github.com/authelia/authelia/v4/internal/metrics"
|
||||
"github.com/authelia/authelia/v4/internal/middlewares"
|
||||
"github.com/authelia/authelia/v4/internal/notification"
|
||||
"github.com/authelia/authelia/v4/internal/ntp"
|
||||
"github.com/authelia/authelia/v4/internal/oidc"
|
||||
"github.com/authelia/authelia/v4/internal/regulation"
|
||||
"github.com/authelia/authelia/v4/internal/session"
|
||||
"github.com/spf13/pflag"
|
||||
|
||||
"github.com/authelia/authelia/v4/internal/configuration/schema"
|
||||
"github.com/authelia/authelia/v4/internal/model"
|
||||
"github.com/authelia/authelia/v4/internal/storage"
|
||||
"github.com/authelia/authelia/v4/internal/templates"
|
||||
"github.com/authelia/authelia/v4/internal/totp"
|
||||
"github.com/authelia/authelia/v4/internal/utils"
|
||||
)
|
||||
|
||||
func getStorageProvider() (provider storage.Provider) {
|
||||
func getStorageProvider(ctx *CmdCtx) (provider storage.Provider) {
|
||||
switch {
|
||||
case config.Storage.Local == nil:
|
||||
return getStorageProviderWithPool(nil)
|
||||
default:
|
||||
caCertPool, _, _ := utils.NewX509CertPool(config.CertificatesDirectory)
|
||||
|
||||
return getStorageProviderWithPool(caCertPool)
|
||||
}
|
||||
}
|
||||
|
||||
func getStorageProviderWithPool(caCertPool *x509.CertPool) (provider storage.Provider) {
|
||||
switch {
|
||||
case config.Storage.PostgreSQL != nil:
|
||||
return storage.NewPostgreSQLProvider(config, caCertPool)
|
||||
case config.Storage.MySQL != nil:
|
||||
return storage.NewMySQLProvider(config, caCertPool)
|
||||
case config.Storage.Local != nil:
|
||||
return storage.NewSQLiteProvider(config)
|
||||
case ctx.config.Storage.PostgreSQL != nil:
|
||||
return storage.NewPostgreSQLProvider(ctx.config, ctx.trusted)
|
||||
case ctx.config.Storage.MySQL != nil:
|
||||
return storage.NewMySQLProvider(ctx.config, ctx.trusted)
|
||||
case ctx.config.Storage.Local != nil:
|
||||
return storage.NewSQLiteProvider(ctx.config)
|
||||
default:
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
func getProviders() (providers middlewares.Providers, warnings []error, errors []error) {
|
||||
// TODO: Adjust this so the CertPool can be used like a provider.
|
||||
caCertPool, warnings, errors := utils.NewX509CertPool(config.CertificatesDirectory)
|
||||
if len(warnings) != 0 || len(errors) != 0 {
|
||||
return providers, warnings, errors
|
||||
func containsIdentifier(identifier model.UserOpaqueIdentifier, identifiers []model.UserOpaqueIdentifier) bool {
|
||||
for i := 0; i < len(identifiers); i++ {
|
||||
if identifier.Service == identifiers[i].Service && identifier.SectorID == identifiers[i].SectorID && identifier.Username == identifiers[i].Username {
|
||||
return true
|
||||
}
|
||||
}
|
||||
|
||||
storageProvider := getStorageProviderWithPool(caCertPool)
|
||||
|
||||
var (
|
||||
userProvider authentication.UserProvider
|
||||
err error
|
||||
)
|
||||
|
||||
switch {
|
||||
case config.AuthenticationBackend.File != nil:
|
||||
userProvider = authentication.NewFileUserProvider(config.AuthenticationBackend.File)
|
||||
case config.AuthenticationBackend.LDAP != nil:
|
||||
userProvider = authentication.NewLDAPUserProvider(config.AuthenticationBackend, caCertPool)
|
||||
}
|
||||
|
||||
templatesProvider, err := templates.New(templates.Config{EmailTemplatesPath: config.Notifier.TemplatePath})
|
||||
if err != nil {
|
||||
errors = append(errors, err)
|
||||
}
|
||||
|
||||
var notifier notification.Notifier
|
||||
|
||||
switch {
|
||||
case config.Notifier.SMTP != nil:
|
||||
notifier = notification.NewSMTPNotifier(config.Notifier.SMTP, caCertPool, templatesProvider)
|
||||
case config.Notifier.FileSystem != nil:
|
||||
notifier = notification.NewFileNotifier(*config.Notifier.FileSystem)
|
||||
}
|
||||
|
||||
ntpProvider := ntp.NewProvider(&config.NTP)
|
||||
|
||||
clock := utils.RealClock{}
|
||||
authorizer := authorization.NewAuthorizer(config)
|
||||
sessionProvider := session.NewProvider(config.Session, caCertPool)
|
||||
regulator := regulation.NewRegulator(config.Regulation, storageProvider, clock)
|
||||
|
||||
oidcProvider, err := oidc.NewOpenIDConnectProvider(config.IdentityProviders.OIDC, storageProvider)
|
||||
if err != nil {
|
||||
errors = append(errors, err)
|
||||
}
|
||||
|
||||
totpProvider := totp.NewTimeBasedProvider(config.TOTP)
|
||||
|
||||
ppolicyProvider := middlewares.NewPasswordPolicyProvider(config.PasswordPolicy)
|
||||
|
||||
var metricsProvider metrics.Provider
|
||||
if config.Telemetry.Metrics.Enabled {
|
||||
metricsProvider = metrics.NewPrometheus()
|
||||
}
|
||||
|
||||
return middlewares.Providers{
|
||||
Authorizer: authorizer,
|
||||
UserProvider: userProvider,
|
||||
Regulator: regulator,
|
||||
OpenIDConnect: oidcProvider,
|
||||
StorageProvider: storageProvider,
|
||||
Metrics: metricsProvider,
|
||||
NTP: ntpProvider,
|
||||
Notifier: notifier,
|
||||
SessionProvider: sessionProvider,
|
||||
Templates: templatesProvider,
|
||||
TOTP: totpProvider,
|
||||
PasswordPolicy: ppolicyProvider,
|
||||
}, warnings, errors
|
||||
return false
|
||||
}
|
||||
|
||||
func storageWrapCheckSchemaErr(err error) error {
|
||||
switch {
|
||||
case errors.Is(err, errStorageSchemaIncompatible):
|
||||
return fmt.Errorf("command requires the use of a compatibe schema version: %w", err)
|
||||
case errors.Is(err, errStorageSchemaOutdated):
|
||||
return fmt.Errorf("command requires the use of a up to date schema version: %w", err)
|
||||
default:
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
func storageTOTPGenerateRunEOptsFromFlags(flags *pflag.FlagSet) (force bool, filename, secret string, err error) {
|
||||
if force, err = flags.GetBool("force"); err != nil {
|
||||
return force, filename, secret, err
|
||||
}
|
||||
|
||||
if filename, err = flags.GetString("path"); err != nil {
|
||||
return force, filename, secret, err
|
||||
}
|
||||
|
||||
if secret, err = flags.GetString("secret"); err != nil {
|
||||
return force, filename, secret, err
|
||||
}
|
||||
|
||||
secretLength := base32.StdEncoding.WithPadding(base32.NoPadding).DecodedLen(len(secret))
|
||||
if secret != "" && secretLength < schema.TOTPSecretSizeMinimum {
|
||||
return force, filename, secret, fmt.Errorf("decoded length of the base32 secret must have "+
|
||||
"a length of more than %d but '%s' has a decoded length of %d", schema.TOTPSecretSizeMinimum, secret, secretLength)
|
||||
}
|
||||
|
||||
return force, filename, secret, nil
|
||||
}
|
||||
|
||||
func storageWebauthnDeleteRunEOptsFromFlags(flags *pflag.FlagSet, args []string) (all, byKID bool, description, kid, user string, err error) {
|
||||
if len(args) != 0 {
|
||||
user = args[0]
|
||||
}
|
||||
|
||||
f := 0
|
||||
|
||||
if flags.Changed(cmdFlagNameAll) {
|
||||
if all, err = flags.GetBool(cmdFlagNameAll); err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
f++
|
||||
}
|
||||
|
||||
if flags.Changed(cmdFlagNameDescription) {
|
||||
if description, err = flags.GetString(cmdFlagNameDescription); err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
f++
|
||||
}
|
||||
|
||||
if byKID = flags.Changed(cmdFlagNameKeyID); byKID {
|
||||
if kid, err = flags.GetString(cmdFlagNameKeyID); err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
f++
|
||||
}
|
||||
|
||||
if f > 1 {
|
||||
err = fmt.Errorf("must only supply one of the flags --all, --description, and --kid but %d were specified", f)
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
if f == 0 {
|
||||
err = fmt.Errorf("must supply one of the flags --all, --description, or --kid")
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
if !byKID && len(user) == 0 {
|
||||
err = fmt.Errorf("must supply the username or the --kid flag")
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
return
|
||||
}
|
||||
|
|
|
@ -4,12 +4,8 @@ import (
|
|||
"testing"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
|
||||
"github.com/authelia/authelia/v4/internal/configuration/schema"
|
||||
)
|
||||
|
||||
func TestGetStorageProvider(t *testing.T) {
|
||||
config = &schema.Configuration{}
|
||||
|
||||
assert.Nil(t, getStorageProvider())
|
||||
assert.Nil(t, getStorageProvider(NewCmdCtx()))
|
||||
}
|
||||
|
|
|
@ -1,7 +1,6 @@
|
|||
package commands
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"net"
|
||||
"os"
|
||||
|
@ -11,15 +10,11 @@ import (
|
|||
"syscall"
|
||||
|
||||
"github.com/fsnotify/fsnotify"
|
||||
"github.com/sirupsen/logrus"
|
||||
"github.com/spf13/cobra"
|
||||
"github.com/valyala/fasthttp"
|
||||
"golang.org/x/sync/errgroup"
|
||||
|
||||
"github.com/authelia/authelia/v4/internal/authentication"
|
||||
"github.com/authelia/authelia/v4/internal/configuration/schema"
|
||||
"github.com/authelia/authelia/v4/internal/logging"
|
||||
"github.com/authelia/authelia/v4/internal/middlewares"
|
||||
"github.com/authelia/authelia/v4/internal/model"
|
||||
"github.com/authelia/authelia/v4/internal/server"
|
||||
"github.com/authelia/authelia/v4/internal/utils"
|
||||
|
@ -27,6 +22,8 @@ import (
|
|||
|
||||
// NewRootCmd returns a new Root Cmd.
|
||||
func NewRootCmd() (cmd *cobra.Command) {
|
||||
ctx := NewCmdCtx()
|
||||
|
||||
version := utils.Version()
|
||||
|
||||
cmd = &cobra.Command{
|
||||
|
@ -36,68 +33,70 @@ func NewRootCmd() (cmd *cobra.Command) {
|
|||
Example: cmdAutheliaExample,
|
||||
Version: version,
|
||||
Args: cobra.NoArgs,
|
||||
PreRun: newCmdWithConfigPreRun(true, true, true),
|
||||
Run: cmdRootRun,
|
||||
PreRunE: ctx.ChainRunE(
|
||||
ctx.ConfigEnsureExistsRunE,
|
||||
ctx.ConfigLoadRunE,
|
||||
ctx.ConfigValidateKeysRunE,
|
||||
ctx.ConfigValidateRunE,
|
||||
ctx.ConfigValidateLogRunE,
|
||||
),
|
||||
RunE: ctx.RootRunE,
|
||||
|
||||
DisableAutoGenTag: true,
|
||||
}
|
||||
|
||||
cmdWithConfigFlags(cmd, false, []string{})
|
||||
cmd.PersistentFlags().StringSliceP(cmdFlagNameConfig, "c", []string{"configuration.yml"}, "configuration files to load")
|
||||
|
||||
cmd.Flags().StringSlice(cmdFlagNameConfigExpFilters, nil, "Applies filters in order to the configuration file before the YAML parser. Options are 'template', 'expand-env'")
|
||||
cmd.PersistentFlags().StringSlice(cmdFlagNameConfigExpFilters, nil, "applies filters in order to the configuration file before the YAML parser, options are 'template', 'expand-env'")
|
||||
|
||||
cmd.AddCommand(
|
||||
newAccessControlCommand(),
|
||||
newBuildInfoCmd(),
|
||||
newCryptoCmd(),
|
||||
newHashPasswordCmd(),
|
||||
newStorageCmd(),
|
||||
newValidateConfigCmd(),
|
||||
newAccessControlCommand(ctx),
|
||||
newBuildInfoCmd(ctx),
|
||||
newCryptoCmd(ctx),
|
||||
newStorageCmd(ctx),
|
||||
newValidateConfigCmd(ctx),
|
||||
)
|
||||
|
||||
return cmd
|
||||
}
|
||||
|
||||
func cmdRootRun(_ *cobra.Command, _ []string) {
|
||||
logger := logging.Logger()
|
||||
|
||||
logger.Infof("Authelia %s is starting", utils.Version())
|
||||
func (ctx *CmdCtx) RootRunE(_ *cobra.Command, _ []string) (err error) {
|
||||
ctx.log.Infof("Authelia %s is starting", utils.Version())
|
||||
|
||||
if os.Getenv("ENVIRONMENT") == "dev" {
|
||||
logger.Info("===> Authelia is running in development mode. <===")
|
||||
ctx.log.Info("===> Authelia is running in development mode. <===")
|
||||
}
|
||||
|
||||
if err := logging.InitializeLogger(config.Log, true); err != nil {
|
||||
logger.Fatalf("Cannot initialize logger: %v", err)
|
||||
if err = logging.InitializeLogger(ctx.config.Log, true); err != nil {
|
||||
ctx.log.Fatalf("Cannot initialize logger: %v", err)
|
||||
}
|
||||
|
||||
providers, warnings, errors := getProviders()
|
||||
if len(warnings) != 0 {
|
||||
for _, err := range warnings {
|
||||
logger.Warn(err)
|
||||
warns, errs := ctx.LoadProviders()
|
||||
|
||||
if len(warns) != 0 {
|
||||
for _, err = range warns {
|
||||
ctx.log.Warn(err)
|
||||
}
|
||||
}
|
||||
|
||||
if len(errors) != 0 {
|
||||
for _, err := range errors {
|
||||
logger.Error(err)
|
||||
if len(errs) != 0 {
|
||||
for _, err = range errs {
|
||||
ctx.log.Error(err)
|
||||
}
|
||||
|
||||
logger.Fatalf("Errors occurred provisioning providers.")
|
||||
ctx.log.Fatalf("Errors occurred provisioning providers.")
|
||||
}
|
||||
|
||||
doStartupChecks(config, &providers, logger)
|
||||
doStartupChecks(ctx)
|
||||
|
||||
runServices(config, providers, logger)
|
||||
runServices(ctx)
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
//nolint:gocyclo // Complexity is required in this function.
|
||||
func runServices(config *schema.Configuration, providers middlewares.Providers, log *logrus.Logger) {
|
||||
ctx := context.Background()
|
||||
|
||||
ctx, cancel := context.WithCancel(ctx)
|
||||
|
||||
defer cancel()
|
||||
func runServices(ctx *CmdCtx) {
|
||||
defer ctx.cancel()
|
||||
|
||||
quit := make(chan os.Signal, 1)
|
||||
|
||||
|
@ -105,26 +104,24 @@ func runServices(config *schema.Configuration, providers middlewares.Providers,
|
|||
|
||||
defer signal.Stop(quit)
|
||||
|
||||
g, ctx := errgroup.WithContext(ctx)
|
||||
|
||||
var (
|
||||
mainServer, metricsServer *fasthttp.Server
|
||||
mainListener, metricsListener net.Listener
|
||||
)
|
||||
|
||||
g.Go(func() (err error) {
|
||||
ctx.group.Go(func() (err error) {
|
||||
defer func() {
|
||||
if r := recover(); r != nil {
|
||||
log.WithError(recoverErr(r)).Errorf("Critical error in server caught (recovered)")
|
||||
ctx.log.WithError(recoverErr(r)).Errorf("Critical error in server caught (recovered)")
|
||||
}
|
||||
}()
|
||||
|
||||
if mainServer, mainListener, err = server.CreateDefaultServer(*config, providers); err != nil {
|
||||
if mainServer, mainListener, err = server.CreateDefaultServer(*ctx.config, ctx.providers); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if err = mainServer.Serve(mainListener); err != nil {
|
||||
log.WithError(err).Error("Server (main) returned error")
|
||||
ctx.log.WithError(err).Error("Server (main) returned error")
|
||||
|
||||
return err
|
||||
}
|
||||
|
@ -132,23 +129,23 @@ func runServices(config *schema.Configuration, providers middlewares.Providers,
|
|||
return nil
|
||||
})
|
||||
|
||||
g.Go(func() (err error) {
|
||||
if providers.Metrics == nil {
|
||||
ctx.group.Go(func() (err error) {
|
||||
if ctx.providers.Metrics == nil {
|
||||
return nil
|
||||
}
|
||||
|
||||
defer func() {
|
||||
if r := recover(); r != nil {
|
||||
log.WithError(recoverErr(r)).Errorf("Critical error in metrics server caught (recovered)")
|
||||
ctx.log.WithError(recoverErr(r)).Errorf("Critical error in metrics server caught (recovered)")
|
||||
}
|
||||
}()
|
||||
|
||||
if metricsServer, metricsListener, err = server.CreateMetricsServer(config.Telemetry.Metrics); err != nil {
|
||||
if metricsServer, metricsListener, err = server.CreateMetricsServer(ctx.config.Telemetry.Metrics); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if err = metricsServer.Serve(metricsListener); err != nil {
|
||||
log.WithError(err).Error("Server (metrics) returned error")
|
||||
ctx.log.WithError(err).Error("Server (metrics) returned error")
|
||||
|
||||
return err
|
||||
}
|
||||
|
@ -156,10 +153,10 @@ func runServices(config *schema.Configuration, providers middlewares.Providers,
|
|||
return nil
|
||||
})
|
||||
|
||||
if config.AuthenticationBackend.File != nil && config.AuthenticationBackend.File.Watch {
|
||||
provider := providers.UserProvider.(*authentication.FileUserProvider)
|
||||
if watcher, err := runServiceFileWatcher(g, log, config.AuthenticationBackend.File.Path, provider); err != nil {
|
||||
log.WithError(err).Errorf("Error opening file watcher")
|
||||
if ctx.config.AuthenticationBackend.File != nil && ctx.config.AuthenticationBackend.File.Watch {
|
||||
provider := ctx.providers.UserProvider.(*authentication.FileUserProvider)
|
||||
if watcher, err := runServiceFileWatcher(ctx, ctx.config.AuthenticationBackend.File.Path, provider); err != nil {
|
||||
ctx.log.WithError(err).Errorf("Error opening file watcher")
|
||||
} else {
|
||||
defer watcher.Close()
|
||||
}
|
||||
|
@ -169,38 +166,38 @@ func runServices(config *schema.Configuration, providers middlewares.Providers,
|
|||
case s := <-quit:
|
||||
switch s {
|
||||
case syscall.SIGINT:
|
||||
log.Debugf("Shutdown started due to SIGINT")
|
||||
ctx.log.Debugf("Shutdown started due to SIGINT")
|
||||
case syscall.SIGQUIT:
|
||||
log.Debugf("Shutdown started due to SIGQUIT")
|
||||
ctx.log.Debugf("Shutdown started due to SIGQUIT")
|
||||
}
|
||||
case <-ctx.Done():
|
||||
log.Debugf("Shutdown started due to context completion")
|
||||
ctx.log.Debugf("Shutdown started due to context completion")
|
||||
}
|
||||
|
||||
cancel()
|
||||
ctx.cancel()
|
||||
|
||||
log.Infof("Shutting down")
|
||||
ctx.log.Infof("Shutting down")
|
||||
|
||||
var err error
|
||||
|
||||
if mainServer != nil {
|
||||
if err = mainServer.Shutdown(); err != nil {
|
||||
log.WithError(err).Errorf("Error occurred shutting down the server")
|
||||
ctx.log.WithError(err).Errorf("Error occurred shutting down the server")
|
||||
}
|
||||
}
|
||||
|
||||
if metricsServer != nil {
|
||||
if err = metricsServer.Shutdown(); err != nil {
|
||||
log.WithError(err).Errorf("Error occurred shutting down the metrics server")
|
||||
ctx.log.WithError(err).Errorf("Error occurred shutting down the metrics server")
|
||||
}
|
||||
}
|
||||
|
||||
if err = providers.StorageProvider.Close(); err != nil {
|
||||
log.WithError(err).Errorf("Error occurred closing the database connection")
|
||||
if err = ctx.providers.StorageProvider.Close(); err != nil {
|
||||
ctx.log.WithError(err).Errorf("Error occurred closing the database connection")
|
||||
}
|
||||
|
||||
if err = g.Wait(); err != nil {
|
||||
log.WithError(err).Errorf("Error occurred waiting for shutdown")
|
||||
if err = ctx.group.Wait(); err != nil {
|
||||
ctx.log.WithError(err).Errorf("Error occurred waiting for shutdown")
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -210,7 +207,7 @@ type ProviderReload interface {
|
|||
Reload() (reloaded bool, err error)
|
||||
}
|
||||
|
||||
func runServiceFileWatcher(g *errgroup.Group, log *logrus.Logger, path string, reload ProviderReload) (watcher *fsnotify.Watcher, err error) {
|
||||
func runServiceFileWatcher(ctx *CmdCtx, path string, reload ProviderReload) (watcher *fsnotify.Watcher, err error) {
|
||||
if watcher, err = fsnotify.NewWatcher(); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
@ -223,7 +220,7 @@ func runServiceFileWatcher(g *errgroup.Group, log *logrus.Logger, path string, r
|
|||
directory, filename = filepath.Dir(path), filepath.Base(path)
|
||||
}
|
||||
|
||||
g.Go(func() error {
|
||||
ctx.group.Go(func() error {
|
||||
for {
|
||||
select {
|
||||
case <-failed:
|
||||
|
@ -234,30 +231,30 @@ func runServiceFileWatcher(g *errgroup.Group, log *logrus.Logger, path string, r
|
|||
}
|
||||
|
||||
if filename != filepath.Base(event.Name) {
|
||||
log.WithField("file", event.Name).WithField("op", event.Op).Tracef("File modification detected to irrelevant file")
|
||||
ctx.log.WithField("file", event.Name).WithField("op", event.Op).Tracef("File modification detected to irrelevant file")
|
||||
break
|
||||
}
|
||||
|
||||
switch {
|
||||
case event.Op&fsnotify.Write == fsnotify.Write, event.Op&fsnotify.Create == fsnotify.Create:
|
||||
log.WithField("file", event.Name).WithField("op", event.Op).Debug("File modification detected")
|
||||
ctx.log.WithField("file", event.Name).WithField("op", event.Op).Debug("File modification detected")
|
||||
|
||||
switch reloaded, err := reload.Reload(); {
|
||||
case err != nil:
|
||||
log.WithField("file", event.Name).WithField("op", event.Op).WithError(err).Error("Error occurred reloading file")
|
||||
ctx.log.WithField("file", event.Name).WithField("op", event.Op).WithError(err).Error("Error occurred reloading file")
|
||||
case reloaded:
|
||||
log.WithField("file", event.Name).Info("Reloaded file successfully")
|
||||
ctx.log.WithField("file", event.Name).Info("Reloaded file successfully")
|
||||
default:
|
||||
log.WithField("file", event.Name).Debug("Reload of file was triggered but it was skipped")
|
||||
ctx.log.WithField("file", event.Name).Debug("Reload of file was triggered but it was skipped")
|
||||
}
|
||||
case event.Op&fsnotify.Remove == fsnotify.Remove:
|
||||
log.WithField("file", event.Name).WithField("op", event.Op).Debug("Remove of file was detected")
|
||||
ctx.log.WithField("file", event.Name).WithField("op", event.Op).Debug("Remove of file was detected")
|
||||
}
|
||||
case err, ok := <-watcher.Errors:
|
||||
if !ok {
|
||||
return nil
|
||||
}
|
||||
log.WithError(err).Errorf("Error while watching files")
|
||||
ctx.log.WithError(err).Errorf("Error while watching files")
|
||||
}
|
||||
}
|
||||
})
|
||||
|
@ -268,53 +265,53 @@ func runServiceFileWatcher(g *errgroup.Group, log *logrus.Logger, path string, r
|
|||
return nil, err
|
||||
}
|
||||
|
||||
log.WithField("directory", directory).WithField("file", filename).Debug("Directory is being watched for changes to the file")
|
||||
ctx.log.WithField("directory", directory).WithField("file", filename).Debug("Directory is being watched for changes to the file")
|
||||
|
||||
return watcher, nil
|
||||
}
|
||||
|
||||
func doStartupChecks(config *schema.Configuration, providers *middlewares.Providers, log *logrus.Logger) {
|
||||
func doStartupChecks(ctx *CmdCtx) {
|
||||
var (
|
||||
failures []string
|
||||
err error
|
||||
)
|
||||
|
||||
if err = doStartupCheck(log, "storage", providers.StorageProvider, false); err != nil {
|
||||
log.Errorf("Failure running the storage provider startup check: %+v", err)
|
||||
if err = doStartupCheck(ctx, "storage", ctx.providers.StorageProvider, false); err != nil {
|
||||
ctx.log.Errorf("Failure running the storage provider startup check: %+v", err)
|
||||
|
||||
failures = append(failures, "storage")
|
||||
}
|
||||
|
||||
if err = doStartupCheck(log, "user", providers.UserProvider, false); err != nil {
|
||||
log.Errorf("Failure running the user provider startup check: %+v", err)
|
||||
if err = doStartupCheck(ctx, "user", ctx.providers.UserProvider, false); err != nil {
|
||||
ctx.log.Errorf("Failure running the user provider startup check: %+v", err)
|
||||
|
||||
failures = append(failures, "user")
|
||||
}
|
||||
|
||||
if err = doStartupCheck(log, "notification", providers.Notifier, config.Notifier.DisableStartupCheck); err != nil {
|
||||
log.Errorf("Failure running the notification provider startup check: %+v", err)
|
||||
if err = doStartupCheck(ctx, "notification", ctx.providers.Notifier, ctx.config.Notifier.DisableStartupCheck); err != nil {
|
||||
ctx.log.Errorf("Failure running the notification provider startup check: %+v", err)
|
||||
|
||||
failures = append(failures, "notification")
|
||||
}
|
||||
|
||||
if !config.NTP.DisableStartupCheck && !providers.Authorizer.IsSecondFactorEnabled() {
|
||||
log.Debug("The NTP startup check was skipped due to there being no configured 2FA access control rules")
|
||||
} else if err = doStartupCheck(log, "ntp", providers.NTP, config.NTP.DisableStartupCheck); err != nil {
|
||||
log.Errorf("Failure running the ntp provider startup check: %+v", err)
|
||||
if !ctx.config.NTP.DisableStartupCheck && !ctx.providers.Authorizer.IsSecondFactorEnabled() {
|
||||
ctx.log.Debug("The NTP startup check was skipped due to there being no configured 2FA access control rules")
|
||||
} else if err = doStartupCheck(ctx, "ntp", ctx.providers.NTP, ctx.config.NTP.DisableStartupCheck); err != nil {
|
||||
ctx.log.Errorf("Failure running the ntp provider startup check: %+v", err)
|
||||
|
||||
if !config.NTP.DisableFailure {
|
||||
if !ctx.config.NTP.DisableFailure {
|
||||
failures = append(failures, "ntp")
|
||||
}
|
||||
}
|
||||
|
||||
if len(failures) != 0 {
|
||||
log.Fatalf("The following providers had fatal failures during startup: %s", strings.Join(failures, ", "))
|
||||
ctx.log.Fatalf("The following providers had fatal failures during startup: %s", strings.Join(failures, ", "))
|
||||
}
|
||||
}
|
||||
|
||||
func doStartupCheck(logger *logrus.Logger, name string, provider model.StartupCheck, disabled bool) error {
|
||||
func doStartupCheck(ctx *CmdCtx, name string, provider model.StartupCheck, disabled bool) error {
|
||||
if disabled {
|
||||
logger.Debugf("%s provider: startup check skipped as it is disabled", name)
|
||||
ctx.log.Debugf("%s provider: startup check skipped as it is disabled", name)
|
||||
return nil
|
||||
}
|
||||
|
||||
|
|
|
@ -9,20 +9,23 @@ import (
|
|||
"github.com/authelia/authelia/v4/internal/configuration/schema"
|
||||
)
|
||||
|
||||
func newStorageCmd() (cmd *cobra.Command) {
|
||||
func newStorageCmd(ctx *CmdCtx) (cmd *cobra.Command) {
|
||||
cmd = &cobra.Command{
|
||||
Use: "storage",
|
||||
Short: cmdAutheliaStorageShort,
|
||||
Long: cmdAutheliaStorageLong,
|
||||
Example: cmdAutheliaStorageExample,
|
||||
Args: cobra.NoArgs,
|
||||
PersistentPreRunE: storagePersistentPreRunE,
|
||||
Use: "storage",
|
||||
Short: cmdAutheliaStorageShort,
|
||||
Long: cmdAutheliaStorageLong,
|
||||
Example: cmdAutheliaStorageExample,
|
||||
Args: cobra.NoArgs,
|
||||
PersistentPreRunE: ctx.ChainRunE(
|
||||
ctx.ConfigStorageCommandLineConfigPersistentPreRunE,
|
||||
ctx.ConfigLoadRunE,
|
||||
ctx.ConfigValidateStoragePersistentPreRunE,
|
||||
ctx.LoadProvidersStorageRunE,
|
||||
),
|
||||
|
||||
DisableAutoGenTag: true,
|
||||
}
|
||||
|
||||
cmdWithConfigFlags(cmd, true, []string{"configuration.yml"})
|
||||
|
||||
cmd.PersistentFlags().String(cmdFlagNameEncryptionKey, "", "the storage encryption key to use")
|
||||
|
||||
cmd.PersistentFlags().String(cmdFlagNameSQLite3Path, "", "the SQLite database path")
|
||||
|
@ -45,16 +48,16 @@ func newStorageCmd() (cmd *cobra.Command) {
|
|||
cmd.PersistentFlags().String("postgres.ssl.key", "", "the PostgreSQL ssl key file location")
|
||||
|
||||
cmd.AddCommand(
|
||||
newStorageMigrateCmd(),
|
||||
newStorageSchemaInfoCmd(),
|
||||
newStorageEncryptionCmd(),
|
||||
newStorageUserCmd(),
|
||||
newStorageMigrateCmd(ctx),
|
||||
newStorageSchemaInfoCmd(ctx),
|
||||
newStorageEncryptionCmd(ctx),
|
||||
newStorageUserCmd(ctx),
|
||||
)
|
||||
|
||||
return cmd
|
||||
}
|
||||
|
||||
func newStorageEncryptionCmd() (cmd *cobra.Command) {
|
||||
func newStorageEncryptionCmd(ctx *CmdCtx) (cmd *cobra.Command) {
|
||||
cmd = &cobra.Command{
|
||||
Use: "encryption",
|
||||
Short: cmdAutheliaStorageEncryptionShort,
|
||||
|
@ -65,20 +68,20 @@ func newStorageEncryptionCmd() (cmd *cobra.Command) {
|
|||
}
|
||||
|
||||
cmd.AddCommand(
|
||||
newStorageEncryptionChangeKeyCmd(),
|
||||
newStorageEncryptionCheckCmd(),
|
||||
newStorageEncryptionChangeKeyCmd(ctx),
|
||||
newStorageEncryptionCheckCmd(ctx),
|
||||
)
|
||||
|
||||
return cmd
|
||||
}
|
||||
|
||||
func newStorageEncryptionCheckCmd() (cmd *cobra.Command) {
|
||||
func newStorageEncryptionCheckCmd(ctx *CmdCtx) (cmd *cobra.Command) {
|
||||
cmd = &cobra.Command{
|
||||
Use: "check",
|
||||
Short: cmdAutheliaStorageEncryptionCheckShort,
|
||||
Long: cmdAutheliaStorageEncryptionCheckLong,
|
||||
Example: cmdAutheliaStorageEncryptionCheckExample,
|
||||
RunE: storageSchemaEncryptionCheckRunE,
|
||||
RunE: ctx.StorageSchemaEncryptionCheckRunE,
|
||||
|
||||
DisableAutoGenTag: true,
|
||||
}
|
||||
|
@ -88,13 +91,13 @@ func newStorageEncryptionCheckCmd() (cmd *cobra.Command) {
|
|||
return cmd
|
||||
}
|
||||
|
||||
func newStorageEncryptionChangeKeyCmd() (cmd *cobra.Command) {
|
||||
func newStorageEncryptionChangeKeyCmd(ctx *CmdCtx) (cmd *cobra.Command) {
|
||||
cmd = &cobra.Command{
|
||||
Use: "change-key",
|
||||
Short: cmdAutheliaStorageEncryptionChangeKeyShort,
|
||||
Long: cmdAutheliaStorageEncryptionChangeKeyLong,
|
||||
Example: cmdAutheliaStorageEncryptionChangeKeyExample,
|
||||
RunE: storageSchemaEncryptionChangeKeyRunE,
|
||||
RunE: ctx.StorageSchemaEncryptionChangeKeyRunE,
|
||||
|
||||
DisableAutoGenTag: true,
|
||||
}
|
||||
|
@ -104,7 +107,7 @@ func newStorageEncryptionChangeKeyCmd() (cmd *cobra.Command) {
|
|||
return cmd
|
||||
}
|
||||
|
||||
func newStorageUserCmd() (cmd *cobra.Command) {
|
||||
func newStorageUserCmd(ctx *CmdCtx) (cmd *cobra.Command) {
|
||||
cmd = &cobra.Command{
|
||||
Use: "user",
|
||||
Short: cmdAutheliaStorageUserShort,
|
||||
|
@ -115,15 +118,15 @@ func newStorageUserCmd() (cmd *cobra.Command) {
|
|||
}
|
||||
|
||||
cmd.AddCommand(
|
||||
newStorageUserIdentifiersCmd(),
|
||||
newStorageUserTOTPCmd(),
|
||||
newStorageUserWebAuthnCmd(),
|
||||
newStorageUserIdentifiersCmd(ctx),
|
||||
newStorageUserTOTPCmd(ctx),
|
||||
newStorageUserWebAuthnCmd(ctx),
|
||||
)
|
||||
|
||||
return cmd
|
||||
}
|
||||
|
||||
func newStorageUserIdentifiersCmd() (cmd *cobra.Command) {
|
||||
func newStorageUserIdentifiersCmd(ctx *CmdCtx) (cmd *cobra.Command) {
|
||||
cmd = &cobra.Command{
|
||||
Use: "identifiers",
|
||||
Short: cmdAutheliaStorageUserIdentifiersShort,
|
||||
|
@ -134,22 +137,22 @@ func newStorageUserIdentifiersCmd() (cmd *cobra.Command) {
|
|||
}
|
||||
|
||||
cmd.AddCommand(
|
||||
newStorageUserIdentifiersExportCmd(),
|
||||
newStorageUserIdentifiersImportCmd(),
|
||||
newStorageUserIdentifiersGenerateCmd(),
|
||||
newStorageUserIdentifiersAddCmd(),
|
||||
newStorageUserIdentifiersExportCmd(ctx),
|
||||
newStorageUserIdentifiersImportCmd(ctx),
|
||||
newStorageUserIdentifiersGenerateCmd(ctx),
|
||||
newStorageUserIdentifiersAddCmd(ctx),
|
||||
)
|
||||
|
||||
return cmd
|
||||
}
|
||||
|
||||
func newStorageUserIdentifiersExportCmd() (cmd *cobra.Command) {
|
||||
func newStorageUserIdentifiersExportCmd(ctx *CmdCtx) (cmd *cobra.Command) {
|
||||
cmd = &cobra.Command{
|
||||
Use: "export",
|
||||
Short: cmdAutheliaStorageUserIdentifiersExportShort,
|
||||
Long: cmdAutheliaStorageUserIdentifiersExportLong,
|
||||
Example: cmdAutheliaStorageUserIdentifiersExportExample,
|
||||
RunE: storageUserIdentifiersExport,
|
||||
RunE: ctx.StorageUserIdentifiersExportRunE,
|
||||
|
||||
DisableAutoGenTag: true,
|
||||
}
|
||||
|
@ -159,13 +162,13 @@ func newStorageUserIdentifiersExportCmd() (cmd *cobra.Command) {
|
|||
return cmd
|
||||
}
|
||||
|
||||
func newStorageUserIdentifiersImportCmd() (cmd *cobra.Command) {
|
||||
func newStorageUserIdentifiersImportCmd(ctx *CmdCtx) (cmd *cobra.Command) {
|
||||
cmd = &cobra.Command{
|
||||
Use: "import",
|
||||
Short: cmdAutheliaStorageUserIdentifiersImportShort,
|
||||
Long: cmdAutheliaStorageUserIdentifiersImportLong,
|
||||
Example: cmdAutheliaStorageUserIdentifiersImportExample,
|
||||
RunE: storageUserIdentifiersImport,
|
||||
RunE: ctx.StorageUserIdentifiersImportRunE,
|
||||
|
||||
DisableAutoGenTag: true,
|
||||
}
|
||||
|
@ -175,13 +178,13 @@ func newStorageUserIdentifiersImportCmd() (cmd *cobra.Command) {
|
|||
return cmd
|
||||
}
|
||||
|
||||
func newStorageUserIdentifiersGenerateCmd() (cmd *cobra.Command) {
|
||||
func newStorageUserIdentifiersGenerateCmd(ctx *CmdCtx) (cmd *cobra.Command) {
|
||||
cmd = &cobra.Command{
|
||||
Use: "generate",
|
||||
Short: cmdAutheliaStorageUserIdentifiersGenerateShort,
|
||||
Long: cmdAutheliaStorageUserIdentifiersGenerateLong,
|
||||
Example: cmdAutheliaStorageUserIdentifiersGenerateExample,
|
||||
RunE: storageUserIdentifiersGenerate,
|
||||
RunE: ctx.StorageUserIdentifiersGenerateRunE,
|
||||
|
||||
DisableAutoGenTag: true,
|
||||
}
|
||||
|
@ -193,14 +196,14 @@ func newStorageUserIdentifiersGenerateCmd() (cmd *cobra.Command) {
|
|||
return cmd
|
||||
}
|
||||
|
||||
func newStorageUserIdentifiersAddCmd() (cmd *cobra.Command) {
|
||||
func newStorageUserIdentifiersAddCmd(ctx *CmdCtx) (cmd *cobra.Command) {
|
||||
cmd = &cobra.Command{
|
||||
Use: "add <username>",
|
||||
Short: cmdAutheliaStorageUserIdentifiersAddShort,
|
||||
Long: cmdAutheliaStorageUserIdentifiersAddLong,
|
||||
Example: cmdAutheliaStorageUserIdentifiersAddExample,
|
||||
Args: cobra.ExactArgs(1),
|
||||
RunE: storageUserIdentifiersAdd,
|
||||
RunE: ctx.StorageUserIdentifiersAddRunE,
|
||||
|
||||
DisableAutoGenTag: true,
|
||||
}
|
||||
|
@ -212,7 +215,7 @@ func newStorageUserIdentifiersAddCmd() (cmd *cobra.Command) {
|
|||
return cmd
|
||||
}
|
||||
|
||||
func newStorageUserWebAuthnCmd() (cmd *cobra.Command) {
|
||||
func newStorageUserWebAuthnCmd(ctx *CmdCtx) (cmd *cobra.Command) {
|
||||
cmd = &cobra.Command{
|
||||
Use: "webauthn",
|
||||
Short: cmdAutheliaStorageUserWebAuthnShort,
|
||||
|
@ -223,20 +226,20 @@ func newStorageUserWebAuthnCmd() (cmd *cobra.Command) {
|
|||
}
|
||||
|
||||
cmd.AddCommand(
|
||||
newStorageUserWebAuthnListCmd(),
|
||||
newStorageUserWebAuthnDeleteCmd(),
|
||||
newStorageUserWebAuthnListCmd(ctx),
|
||||
newStorageUserWebAuthnDeleteCmd(ctx),
|
||||
)
|
||||
|
||||
return cmd
|
||||
}
|
||||
|
||||
func newStorageUserWebAuthnListCmd() (cmd *cobra.Command) {
|
||||
func newStorageUserWebAuthnListCmd(ctx *CmdCtx) (cmd *cobra.Command) {
|
||||
cmd = &cobra.Command{
|
||||
Use: "list [username]",
|
||||
Short: cmdAutheliaStorageUserWebAuthnListShort,
|
||||
Long: cmdAutheliaStorageUserWebAuthnListLong,
|
||||
Example: cmdAutheliaStorageUserWebAuthnListExample,
|
||||
RunE: storageWebAuthnListRunE,
|
||||
RunE: ctx.StorageWebauthnListRunE,
|
||||
Args: cobra.MaximumNArgs(1),
|
||||
|
||||
DisableAutoGenTag: true,
|
||||
|
@ -245,13 +248,13 @@ func newStorageUserWebAuthnListCmd() (cmd *cobra.Command) {
|
|||
return cmd
|
||||
}
|
||||
|
||||
func newStorageUserWebAuthnDeleteCmd() (cmd *cobra.Command) {
|
||||
func newStorageUserWebAuthnDeleteCmd(ctx *CmdCtx) (cmd *cobra.Command) {
|
||||
cmd = &cobra.Command{
|
||||
Use: "delete [username]",
|
||||
Short: cmdAutheliaStorageUserWebAuthnDeleteShort,
|
||||
Long: cmdAutheliaStorageUserWebAuthnDeleteLong,
|
||||
Example: cmdAutheliaStorageUserWebAuthnDeleteExample,
|
||||
RunE: storageWebAuthnDeleteRunE,
|
||||
RunE: ctx.StorageWebauthnDeleteRunE,
|
||||
Args: cobra.MaximumNArgs(1),
|
||||
|
||||
DisableAutoGenTag: true,
|
||||
|
@ -264,7 +267,7 @@ func newStorageUserWebAuthnDeleteCmd() (cmd *cobra.Command) {
|
|||
return cmd
|
||||
}
|
||||
|
||||
func newStorageUserTOTPCmd() (cmd *cobra.Command) {
|
||||
func newStorageUserTOTPCmd(ctx *CmdCtx) (cmd *cobra.Command) {
|
||||
cmd = &cobra.Command{
|
||||
Use: "totp",
|
||||
Short: cmdAutheliaStorageUserTOTPShort,
|
||||
|
@ -275,21 +278,21 @@ func newStorageUserTOTPCmd() (cmd *cobra.Command) {
|
|||
}
|
||||
|
||||
cmd.AddCommand(
|
||||
newStorageUserTOTPGenerateCmd(),
|
||||
newStorageUserTOTPDeleteCmd(),
|
||||
newStorageUserTOTPExportCmd(),
|
||||
newStorageUserTOTPGenerateCmd(ctx),
|
||||
newStorageUserTOTPDeleteCmd(ctx),
|
||||
newStorageUserTOTPExportCmd(ctx),
|
||||
)
|
||||
|
||||
return cmd
|
||||
}
|
||||
|
||||
func newStorageUserTOTPGenerateCmd() (cmd *cobra.Command) {
|
||||
func newStorageUserTOTPGenerateCmd(ctx *CmdCtx) (cmd *cobra.Command) {
|
||||
cmd = &cobra.Command{
|
||||
Use: "generate <username>",
|
||||
Short: cmdAutheliaStorageUserTOTPGenerateShort,
|
||||
Long: cmdAutheliaStorageUserTOTPGenerateLong,
|
||||
Example: cmdAutheliaStorageUserTOTPGenerateExample,
|
||||
RunE: storageTOTPGenerateRunE,
|
||||
RunE: ctx.StorageTOTPGenerateRunE,
|
||||
Args: cobra.ExactArgs(1),
|
||||
|
||||
DisableAutoGenTag: true,
|
||||
|
@ -307,13 +310,13 @@ func newStorageUserTOTPGenerateCmd() (cmd *cobra.Command) {
|
|||
return cmd
|
||||
}
|
||||
|
||||
func newStorageUserTOTPDeleteCmd() (cmd *cobra.Command) {
|
||||
func newStorageUserTOTPDeleteCmd(ctx *CmdCtx) (cmd *cobra.Command) {
|
||||
cmd = &cobra.Command{
|
||||
Use: "delete <username>",
|
||||
Short: cmdAutheliaStorageUserTOTPDeleteShort,
|
||||
Long: cmdAutheliaStorageUserTOTPDeleteLong,
|
||||
Example: cmdAutheliaStorageUserTOTPDeleteExample,
|
||||
RunE: storageTOTPDeleteRunE,
|
||||
RunE: ctx.StorageTOTPDeleteRunE,
|
||||
Args: cobra.ExactArgs(1),
|
||||
|
||||
DisableAutoGenTag: true,
|
||||
|
@ -322,13 +325,13 @@ func newStorageUserTOTPDeleteCmd() (cmd *cobra.Command) {
|
|||
return cmd
|
||||
}
|
||||
|
||||
func newStorageUserTOTPExportCmd() (cmd *cobra.Command) {
|
||||
func newStorageUserTOTPExportCmd(ctx *CmdCtx) (cmd *cobra.Command) {
|
||||
cmd = &cobra.Command{
|
||||
Use: "export",
|
||||
Short: cmdAutheliaStorageUserTOTPExportShort,
|
||||
Long: cmdAutheliaStorageUserTOTPExportLong,
|
||||
Example: cmdAutheliaStorageUserTOTPExportExample,
|
||||
RunE: storageTOTPExportRunE,
|
||||
RunE: ctx.StorageTOTPExportRunE,
|
||||
|
||||
DisableAutoGenTag: true,
|
||||
}
|
||||
|
@ -339,13 +342,13 @@ func newStorageUserTOTPExportCmd() (cmd *cobra.Command) {
|
|||
return cmd
|
||||
}
|
||||
|
||||
func newStorageSchemaInfoCmd() (cmd *cobra.Command) {
|
||||
func newStorageSchemaInfoCmd(ctx *CmdCtx) (cmd *cobra.Command) {
|
||||
cmd = &cobra.Command{
|
||||
Use: "schema-info",
|
||||
Short: cmdAutheliaStorageSchemaInfoShort,
|
||||
Long: cmdAutheliaStorageSchemaInfoLong,
|
||||
Example: cmdAutheliaStorageSchemaInfoExample,
|
||||
RunE: storageSchemaInfoRunE,
|
||||
RunE: ctx.StorageSchemaInfoRunE,
|
||||
|
||||
DisableAutoGenTag: true,
|
||||
}
|
||||
|
@ -354,7 +357,7 @@ func newStorageSchemaInfoCmd() (cmd *cobra.Command) {
|
|||
}
|
||||
|
||||
// NewMigrationCmd returns a new Migration Cmd.
|
||||
func newStorageMigrateCmd() (cmd *cobra.Command) {
|
||||
func newStorageMigrateCmd(ctx *CmdCtx) (cmd *cobra.Command) {
|
||||
cmd = &cobra.Command{
|
||||
Use: "migrate",
|
||||
Short: cmdAutheliaStorageMigrateShort,
|
||||
|
@ -366,22 +369,22 @@ func newStorageMigrateCmd() (cmd *cobra.Command) {
|
|||
}
|
||||
|
||||
cmd.AddCommand(
|
||||
newStorageMigrateUpCmd(), newStorageMigrateDownCmd(),
|
||||
newStorageMigrateListUpCmd(), newStorageMigrateListDownCmd(),
|
||||
newStorageMigrateHistoryCmd(),
|
||||
newStorageMigrateUpCmd(ctx), newStorageMigrateDownCmd(ctx),
|
||||
newStorageMigrateListUpCmd(ctx), newStorageMigrateListDownCmd(ctx),
|
||||
newStorageMigrateHistoryCmd(ctx),
|
||||
)
|
||||
|
||||
return cmd
|
||||
}
|
||||
|
||||
func newStorageMigrateHistoryCmd() (cmd *cobra.Command) {
|
||||
func newStorageMigrateHistoryCmd(ctx *CmdCtx) (cmd *cobra.Command) {
|
||||
cmd = &cobra.Command{
|
||||
Use: "history",
|
||||
Short: cmdAutheliaStorageMigrateHistoryShort,
|
||||
Long: cmdAutheliaStorageMigrateHistoryLong,
|
||||
Example: cmdAutheliaStorageMigrateHistoryExample,
|
||||
Args: cobra.NoArgs,
|
||||
RunE: storageMigrateHistoryRunE,
|
||||
RunE: ctx.StorageMigrateHistoryRunE,
|
||||
|
||||
DisableAutoGenTag: true,
|
||||
}
|
||||
|
@ -389,14 +392,14 @@ func newStorageMigrateHistoryCmd() (cmd *cobra.Command) {
|
|||
return cmd
|
||||
}
|
||||
|
||||
func newStorageMigrateListUpCmd() (cmd *cobra.Command) {
|
||||
func newStorageMigrateListUpCmd(ctx *CmdCtx) (cmd *cobra.Command) {
|
||||
cmd = &cobra.Command{
|
||||
Use: "list-up",
|
||||
Short: cmdAutheliaStorageMigrateListUpShort,
|
||||
Long: cmdAutheliaStorageMigrateListUpLong,
|
||||
Example: cmdAutheliaStorageMigrateListUpExample,
|
||||
Args: cobra.NoArgs,
|
||||
RunE: newStorageMigrateListRunE(true),
|
||||
RunE: ctx.NewStorageMigrateListRunE(true),
|
||||
|
||||
DisableAutoGenTag: true,
|
||||
}
|
||||
|
@ -404,14 +407,14 @@ func newStorageMigrateListUpCmd() (cmd *cobra.Command) {
|
|||
return cmd
|
||||
}
|
||||
|
||||
func newStorageMigrateListDownCmd() (cmd *cobra.Command) {
|
||||
func newStorageMigrateListDownCmd(ctx *CmdCtx) (cmd *cobra.Command) {
|
||||
cmd = &cobra.Command{
|
||||
Use: "list-down",
|
||||
Short: cmdAutheliaStorageMigrateListDownShort,
|
||||
Long: cmdAutheliaStorageMigrateListDownLong,
|
||||
Example: cmdAutheliaStorageMigrateListDownExample,
|
||||
Args: cobra.NoArgs,
|
||||
RunE: newStorageMigrateListRunE(false),
|
||||
RunE: ctx.NewStorageMigrateListRunE(false),
|
||||
|
||||
DisableAutoGenTag: true,
|
||||
}
|
||||
|
@ -419,14 +422,14 @@ func newStorageMigrateListDownCmd() (cmd *cobra.Command) {
|
|||
return cmd
|
||||
}
|
||||
|
||||
func newStorageMigrateUpCmd() (cmd *cobra.Command) {
|
||||
func newStorageMigrateUpCmd(ctx *CmdCtx) (cmd *cobra.Command) {
|
||||
cmd = &cobra.Command{
|
||||
Use: storageMigrateDirectionUp,
|
||||
Short: cmdAutheliaStorageMigrateUpShort,
|
||||
Long: cmdAutheliaStorageMigrateUpLong,
|
||||
Example: cmdAutheliaStorageMigrateUpExample,
|
||||
Args: cobra.NoArgs,
|
||||
RunE: newStorageMigrationRunE(true),
|
||||
RunE: ctx.NewStorageMigrationRunE(true),
|
||||
|
||||
DisableAutoGenTag: true,
|
||||
}
|
||||
|
@ -436,14 +439,14 @@ func newStorageMigrateUpCmd() (cmd *cobra.Command) {
|
|||
return cmd
|
||||
}
|
||||
|
||||
func newStorageMigrateDownCmd() (cmd *cobra.Command) {
|
||||
func newStorageMigrateDownCmd(ctx *CmdCtx) (cmd *cobra.Command) {
|
||||
cmd = &cobra.Command{
|
||||
Use: storageMigrateDirectionDown,
|
||||
Short: cmdAutheliaStorageMigrateDownShort,
|
||||
Long: cmdAutheliaStorageMigrateDownLong,
|
||||
Example: cmdAutheliaStorageMigrateDownExample,
|
||||
Args: cobra.NoArgs,
|
||||
RunE: newStorageMigrationRunE(false),
|
||||
RunE: ctx.NewStorageMigrationRunE(false),
|
||||
|
||||
DisableAutoGenTag: true,
|
||||
}
|
||||
|
|
|
@ -1,9 +1,7 @@
|
|||
package commands
|
||||
|
||||
import (
|
||||
"context"
|
||||
"database/sql"
|
||||
"encoding/base32"
|
||||
"errors"
|
||||
"fmt"
|
||||
"image"
|
||||
|
@ -15,11 +13,8 @@ import (
|
|||
|
||||
"github.com/google/uuid"
|
||||
"github.com/spf13/cobra"
|
||||
"github.com/spf13/pflag"
|
||||
"gopkg.in/yaml.v3"
|
||||
|
||||
"github.com/authelia/authelia/v4/internal/configuration"
|
||||
"github.com/authelia/authelia/v4/internal/configuration/schema"
|
||||
"github.com/authelia/authelia/v4/internal/configuration/validator"
|
||||
"github.com/authelia/authelia/v4/internal/model"
|
||||
"github.com/authelia/authelia/v4/internal/storage"
|
||||
|
@ -27,28 +22,35 @@ import (
|
|||
"github.com/authelia/authelia/v4/internal/utils"
|
||||
)
|
||||
|
||||
func storagePersistentPreRunE(cmd *cobra.Command, _ []string) (err error) {
|
||||
var configs []string
|
||||
// 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")
|
||||
|
||||
if configs, err = cmd.Flags().GetStringSlice(cmdFlagNameConfig); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
sources := make([]configuration.Source, 0, len(configs)+3)
|
||||
|
||||
if cmd.Flags().Changed(cmdFlagNameConfig) {
|
||||
for _, configFile := range configs {
|
||||
if _, err := os.Stat(configFile); os.IsNotExist(err) {
|
||||
return fmt.Errorf("could not load the provided configuration file %s: %w", configFile, err)
|
||||
}
|
||||
|
||||
sources = append(sources, configuration.NewYAMLFileSource(configFile))
|
||||
for _, e := range errs {
|
||||
err = fmt.Errorf("%+v: %w", err, e)
|
||||
}
|
||||
} else if _, err := os.Stat(configs[0]); err == nil {
|
||||
sources = append(sources, configuration.NewYAMLFileSource(configs[0]))
|
||||
}
|
||||
|
||||
mapping := map[string]string{
|
||||
return err
|
||||
case len(warns) != 0:
|
||||
err = fmt.Errorf("had the following warnings loading the trusted certificates")
|
||||
|
||||
for _, e := range errs {
|
||||
err = fmt.Errorf("%+v: %w", err, e)
|
||||
}
|
||||
|
||||
return err
|
||||
default:
|
||||
ctx.providers.StorageProvider = getStorageProvider(ctx)
|
||||
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
// ConfigStorageCommandLineConfigPersistentPreRunE configures the storage command mapping.
|
||||
func (ctx *CmdCtx) ConfigStorageCommandLineConfigPersistentPreRunE(cmd *cobra.Command, _ []string) (err error) {
|
||||
flagsMap := map[string]string{
|
||||
cmdFlagNameEncryptionKey: "storage.encryption_key",
|
||||
|
||||
cmdFlagNameSQLite3Path: "storage.local.path",
|
||||
|
@ -77,75 +79,73 @@ func storagePersistentPreRunE(cmd *cobra.Command, _ []string) (err error) {
|
|||
cmdFlagNameSecretSize: "totp.secret_size",
|
||||
}
|
||||
|
||||
sources = append(sources, configuration.NewEnvironmentSource(configuration.DefaultEnvPrefix, configuration.DefaultEnvDelimiter))
|
||||
sources = append(sources, configuration.NewSecretsSource(configuration.DefaultEnvPrefix, configuration.DefaultEnvDelimiter))
|
||||
sources = append(sources, configuration.NewCommandLineSourceWithMapping(cmd.Flags(), mapping, true, false))
|
||||
return ctx.ConfigSetFlagsMapRunE(cmd.Flags(), flagsMap, true, false)
|
||||
}
|
||||
|
||||
val := schema.NewStructValidator()
|
||||
// ConfigValidateStoragePersistentPreRunE validates the storage config before running commands using it.
|
||||
func (ctx *CmdCtx) ConfigValidateStoragePersistentPreRunE(_ *cobra.Command, _ []string) (err error) {
|
||||
if errs := ctx.cconfig.validator.Errors(); len(errs) != 0 {
|
||||
var (
|
||||
i int
|
||||
e error
|
||||
)
|
||||
|
||||
config = &schema.Configuration{}
|
||||
for i, e = range errs {
|
||||
if i == 0 {
|
||||
err = e
|
||||
continue
|
||||
}
|
||||
|
||||
err = fmt.Errorf("%w, %v", err, e)
|
||||
}
|
||||
|
||||
if _, err = configuration.LoadAdvanced(val, "", &config, sources...); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if val.HasErrors() {
|
||||
var finalErr error
|
||||
validator.ValidateStorage(ctx.config.Storage, ctx.cconfig.validator)
|
||||
|
||||
for i, err := range val.Errors() {
|
||||
validator.ValidateTOTP(ctx.config, ctx.cconfig.validator)
|
||||
|
||||
if errs := ctx.cconfig.validator.Errors(); len(errs) != 0 {
|
||||
var (
|
||||
i int
|
||||
e error
|
||||
)
|
||||
|
||||
for i, e = range errs {
|
||||
if i == 0 {
|
||||
finalErr = err
|
||||
err = e
|
||||
continue
|
||||
}
|
||||
|
||||
finalErr = fmt.Errorf("%w, %v", finalErr, err)
|
||||
err = fmt.Errorf("%w, %v", err, e)
|
||||
}
|
||||
|
||||
return finalErr
|
||||
}
|
||||
|
||||
validator.ValidateStorage(config.Storage, val)
|
||||
|
||||
validator.ValidateTOTP(config, val)
|
||||
|
||||
if val.HasErrors() {
|
||||
var finalErr error
|
||||
|
||||
for i, err := range val.Errors() {
|
||||
if i == 0 {
|
||||
finalErr = err
|
||||
continue
|
||||
}
|
||||
|
||||
finalErr = fmt.Errorf("%w, %v", finalErr, err)
|
||||
}
|
||||
|
||||
return finalErr
|
||||
return err
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func storageSchemaEncryptionCheckRunE(cmd *cobra.Command, args []string) (err error) {
|
||||
func (ctx *CmdCtx) StorageSchemaEncryptionCheckRunE(cmd *cobra.Command, args []string) (err error) {
|
||||
var (
|
||||
provider storage.Provider
|
||||
verbose bool
|
||||
result storage.EncryptionValidationResult
|
||||
|
||||
ctx = context.Background()
|
||||
verbose bool
|
||||
result storage.EncryptionValidationResult
|
||||
)
|
||||
|
||||
provider = getStorageProvider()
|
||||
|
||||
defer func() {
|
||||
_ = provider.Close()
|
||||
_ = ctx.providers.StorageProvider.Close()
|
||||
}()
|
||||
|
||||
if err = ctx.CheckSchemaVersion(); err != nil {
|
||||
return storageWrapCheckSchemaErr(err)
|
||||
}
|
||||
|
||||
if verbose, err = cmd.Flags().GetBool(cmdFlagNameVerbose); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if result, err = provider.SchemaEncryptionCheckKey(ctx, verbose); err != nil {
|
||||
if result, err = ctx.providers.StorageProvider.SchemaEncryptionCheckKey(ctx, verbose); err != nil {
|
||||
switch {
|
||||
case errors.Is(err, storage.ErrSchemaEncryptionVersionUnsupported):
|
||||
fmt.Printf("Storage Encryption Key Validation: FAILURE\n\n\tCause: The schema version doesn't support encryption.\n")
|
||||
|
@ -183,26 +183,22 @@ func storageSchemaEncryptionCheckRunE(cmd *cobra.Command, args []string) (err er
|
|||
return nil
|
||||
}
|
||||
|
||||
func storageSchemaEncryptionChangeKeyRunE(cmd *cobra.Command, args []string) (err error) {
|
||||
// StorageSchemaEncryptionChangeKeyRunE is the RunE for the authelia storage encryption change-key command.
|
||||
func (ctx *CmdCtx) StorageSchemaEncryptionChangeKeyRunE(cmd *cobra.Command, args []string) (err error) {
|
||||
var (
|
||||
provider storage.Provider
|
||||
key string
|
||||
version int
|
||||
|
||||
ctx = context.Background()
|
||||
key string
|
||||
version int
|
||||
)
|
||||
|
||||
provider = getStorageProvider()
|
||||
|
||||
defer func() {
|
||||
_ = provider.Close()
|
||||
_ = ctx.providers.StorageProvider.Close()
|
||||
}()
|
||||
|
||||
if err = checkStorageSchemaUpToDate(ctx, provider); err != nil {
|
||||
return err
|
||||
if err = ctx.CheckSchemaVersion(); err != nil {
|
||||
return storageWrapCheckSchemaErr(err)
|
||||
}
|
||||
|
||||
if version, err = provider.SchemaVersion(ctx); err != nil {
|
||||
if version, err = ctx.providers.StorageProvider.SchemaVersion(ctx); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
|
@ -218,7 +214,7 @@ func storageSchemaEncryptionChangeKeyRunE(cmd *cobra.Command, args []string) (er
|
|||
}
|
||||
|
||||
if !useFlag || key == "" {
|
||||
if key, err = termReadPasswordStrWithPrompt("Enter New Storage Encryption Key: ", cmdFlagNameNewEncryptionKey); err != nil {
|
||||
if key, err = termReadPasswordWithPrompt("Enter New Storage Encryption Key: ", cmdFlagNameNewEncryptionKey); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
@ -230,7 +226,7 @@ func storageSchemaEncryptionChangeKeyRunE(cmd *cobra.Command, args []string) (er
|
|||
return errors.New("the new encryption key must be at least 20 characters")
|
||||
}
|
||||
|
||||
if err = provider.SchemaEncryptionChangeKey(ctx, key); err != nil {
|
||||
if err = ctx.providers.StorageProvider.SchemaEncryptionChangeKey(ctx, key); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
|
@ -239,27 +235,25 @@ func storageSchemaEncryptionChangeKeyRunE(cmd *cobra.Command, args []string) (er
|
|||
return nil
|
||||
}
|
||||
|
||||
func storageWebAuthnListRunE(cmd *cobra.Command, args []string) (err error) {
|
||||
// StorageWebauthnListRunE is the RunE for the authelia storage user webauthn list command.
|
||||
func (ctx *CmdCtx) StorageWebauthnListRunE(cmd *cobra.Command, args []string) (err error) {
|
||||
if len(args) == 0 || args[0] == "" {
|
||||
return storageWebAuthnListAllRunE(cmd, args)
|
||||
return ctx.StorageWebauthnListAllRunE(cmd, args)
|
||||
}
|
||||
|
||||
var (
|
||||
provider storage.Provider
|
||||
ctx = context.Background()
|
||||
)
|
||||
|
||||
provider = getStorageProvider()
|
||||
|
||||
defer func() {
|
||||
_ = provider.Close()
|
||||
_ = ctx.providers.StorageProvider.Close()
|
||||
}()
|
||||
|
||||
if err = ctx.CheckSchemaVersion(); err != nil {
|
||||
return storageWrapCheckSchemaErr(err)
|
||||
}
|
||||
|
||||
var devices []model.WebauthnDevice
|
||||
|
||||
user := args[0]
|
||||
|
||||
devices, err = provider.LoadWebauthnDevicesByUsername(ctx, user)
|
||||
devices, err = ctx.providers.StorageProvider.LoadWebauthnDevicesByUsername(ctx, user)
|
||||
|
||||
switch {
|
||||
case len(devices) == 0 || (err != nil && errors.Is(err, storage.ErrNoWebauthnDevice)):
|
||||
|
@ -278,18 +272,16 @@ func storageWebAuthnListRunE(cmd *cobra.Command, args []string) (err error) {
|
|||
return nil
|
||||
}
|
||||
|
||||
func storageWebAuthnListAllRunE(_ *cobra.Command, _ []string) (err error) {
|
||||
var (
|
||||
provider storage.Provider
|
||||
ctx = context.Background()
|
||||
)
|
||||
|
||||
provider = getStorageProvider()
|
||||
|
||||
// StorageWebauthnListAllRunE is the RunE for the authelia storage user webauthn list command when no args are specified.
|
||||
func (ctx *CmdCtx) StorageWebauthnListAllRunE(_ *cobra.Command, _ []string) (err error) {
|
||||
defer func() {
|
||||
_ = provider.Close()
|
||||
_ = ctx.providers.StorageProvider.Close()
|
||||
}()
|
||||
|
||||
if err = ctx.CheckSchemaVersion(); err != nil {
|
||||
return storageWrapCheckSchemaErr(err)
|
||||
}
|
||||
|
||||
var devices []model.WebauthnDevice
|
||||
|
||||
limit := 10
|
||||
|
@ -297,7 +289,7 @@ func storageWebAuthnListAllRunE(_ *cobra.Command, _ []string) (err error) {
|
|||
output := strings.Builder{}
|
||||
|
||||
for page := 0; true; page++ {
|
||||
if devices, err = provider.LoadWebauthnDevices(ctx, limit, page); err != nil {
|
||||
if devices, err = ctx.providers.StorageProvider.LoadWebauthnDevices(ctx, limit, page); err != nil {
|
||||
return fmt.Errorf("failed to list devices: %w", err)
|
||||
}
|
||||
|
||||
|
@ -320,35 +312,33 @@ func storageWebAuthnListAllRunE(_ *cobra.Command, _ []string) (err error) {
|
|||
return nil
|
||||
}
|
||||
|
||||
func storageWebAuthnDeleteRunE(cmd *cobra.Command, args []string) (err error) {
|
||||
var (
|
||||
provider storage.Provider
|
||||
ctx = context.Background()
|
||||
)
|
||||
|
||||
provider = getStorageProvider()
|
||||
|
||||
// StorageWebauthnDeleteRunE is the RunE for the authelia storage user webauthn delete command.
|
||||
func (ctx *CmdCtx) StorageWebauthnDeleteRunE(cmd *cobra.Command, args []string) (err error) {
|
||||
defer func() {
|
||||
_ = provider.Close()
|
||||
_ = ctx.providers.StorageProvider.Close()
|
||||
}()
|
||||
|
||||
if err = ctx.CheckSchemaVersion(); err != nil {
|
||||
return storageWrapCheckSchemaErr(err)
|
||||
}
|
||||
|
||||
var (
|
||||
all, byKID bool
|
||||
description, kid, user string
|
||||
)
|
||||
|
||||
if all, byKID, description, kid, user, err = storageWebAuthnDeleteGetAndValidateConfig(cmd, args); err != nil {
|
||||
if all, byKID, description, kid, user, err = storageWebauthnDeleteRunEOptsFromFlags(cmd.Flags(), args); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if byKID {
|
||||
if err = provider.DeleteWebauthnDevice(ctx, kid); err != nil {
|
||||
if err = ctx.providers.StorageProvider.DeleteWebauthnDevice(ctx, kid); err != nil {
|
||||
return fmt.Errorf("failed to delete WebAuthn device with kid '%s': %w", kid, err)
|
||||
}
|
||||
|
||||
fmt.Printf("Deleted WebAuthn device with kid '%s'", kid)
|
||||
} else {
|
||||
err = provider.DeleteWebauthnDeviceByUsername(ctx, user, description)
|
||||
err = ctx.providers.StorageProvider.DeleteWebauthnDeviceByUsername(ctx, user, description)
|
||||
|
||||
if all {
|
||||
if err != nil {
|
||||
|
@ -368,62 +358,9 @@ func storageWebAuthnDeleteRunE(cmd *cobra.Command, args []string) (err error) {
|
|||
return nil
|
||||
}
|
||||
|
||||
func storageWebAuthnDeleteGetAndValidateConfig(cmd *cobra.Command, args []string) (all, byKID bool, description, kid, user string, err error) {
|
||||
if len(args) != 0 {
|
||||
user = args[0]
|
||||
}
|
||||
|
||||
flags := 0
|
||||
|
||||
if cmd.Flags().Changed(cmdFlagNameAll) {
|
||||
if all, err = cmd.Flags().GetBool(cmdFlagNameAll); err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
flags++
|
||||
}
|
||||
|
||||
if cmd.Flags().Changed(cmdFlagNameDescription) {
|
||||
if description, err = cmd.Flags().GetString(cmdFlagNameDescription); err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
flags++
|
||||
}
|
||||
|
||||
if byKID = cmd.Flags().Changed(cmdFlagNameKeyID); byKID {
|
||||
if kid, err = cmd.Flags().GetString(cmdFlagNameKeyID); err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
flags++
|
||||
}
|
||||
|
||||
if flags > 1 {
|
||||
err = fmt.Errorf("must only supply one of the flags --all, --description, and --kid but %d were specified", flags)
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
if flags == 0 {
|
||||
err = fmt.Errorf("must supply one of the flags --all, --description, or --kid")
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
if !byKID && len(user) == 0 {
|
||||
err = fmt.Errorf("must supply the username or the --kid flag")
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
func storageTOTPGenerateRunE(cmd *cobra.Command, args []string) (err error) {
|
||||
// StorageTOTPGenerateRunE is the RunE for the authelia storage user totp generate command.
|
||||
func (ctx *CmdCtx) StorageTOTPGenerateRunE(cmd *cobra.Command, args []string) (err error) {
|
||||
var (
|
||||
provider storage.Provider
|
||||
ctx = context.Background()
|
||||
c *model.TOTPConfiguration
|
||||
force bool
|
||||
filename, secret string
|
||||
|
@ -431,25 +368,27 @@ func storageTOTPGenerateRunE(cmd *cobra.Command, args []string) (err error) {
|
|||
img image.Image
|
||||
)
|
||||
|
||||
provider = getStorageProvider()
|
||||
|
||||
defer func() {
|
||||
_ = provider.Close()
|
||||
_ = ctx.providers.StorageProvider.Close()
|
||||
}()
|
||||
|
||||
if err = ctx.CheckSchemaVersion(); err != nil {
|
||||
return storageWrapCheckSchemaErr(err)
|
||||
}
|
||||
|
||||
if force, filename, secret, err = storageTOTPGenerateRunEOptsFromFlags(cmd.Flags()); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if _, err = provider.LoadTOTPConfiguration(ctx, args[0]); err == nil && !force {
|
||||
if _, err = ctx.providers.StorageProvider.LoadTOTPConfiguration(ctx, args[0]); err == nil && !force {
|
||||
return fmt.Errorf("%s already has a TOTP configuration, use --force to overwrite", args[0])
|
||||
} else if err != nil && !errors.Is(err, storage.ErrNoTOTPConfiguration) {
|
||||
return err
|
||||
}
|
||||
|
||||
totpProvider := totp.NewTimeBasedProvider(config.TOTP)
|
||||
totpProvider := totp.NewTimeBasedProvider(ctx.config.TOTP)
|
||||
|
||||
if c, err = totpProvider.GenerateCustom(args[0], config.TOTP.Algorithm, secret, config.TOTP.Digits, config.TOTP.Period, config.TOTP.SecretSize); err != nil {
|
||||
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 {
|
||||
return err
|
||||
}
|
||||
|
||||
|
@ -477,7 +416,7 @@ func storageTOTPGenerateRunE(cmd *cobra.Command, args []string) (err error) {
|
|||
extraInfo = fmt.Sprintf(" and saved it as a PNG image at the path '%s'", filename)
|
||||
}
|
||||
|
||||
if err = provider.SaveTOTPConfiguration(ctx, *c); err != nil {
|
||||
if err = ctx.providers.StorageProvider.SaveTOTPConfiguration(ctx, *c); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
|
@ -486,47 +425,23 @@ func storageTOTPGenerateRunE(cmd *cobra.Command, args []string) (err error) {
|
|||
return nil
|
||||
}
|
||||
|
||||
func storageTOTPGenerateRunEOptsFromFlags(flags *pflag.FlagSet) (force bool, filename, secret string, err error) {
|
||||
if force, err = flags.GetBool("force"); err != nil {
|
||||
return force, filename, secret, err
|
||||
}
|
||||
|
||||
if filename, err = flags.GetString("path"); err != nil {
|
||||
return force, filename, secret, err
|
||||
}
|
||||
|
||||
if secret, err = flags.GetString("secret"); err != nil {
|
||||
return force, filename, secret, err
|
||||
}
|
||||
|
||||
secretLength := base32.StdEncoding.WithPadding(base32.NoPadding).DecodedLen(len(secret))
|
||||
if secret != "" && secretLength < schema.TOTPSecretSizeMinimum {
|
||||
return force, filename, secret, fmt.Errorf("decoded length of the base32 secret must have "+
|
||||
"a length of more than %d but '%s' has a decoded length of %d", schema.TOTPSecretSizeMinimum, secret, secretLength)
|
||||
}
|
||||
|
||||
return force, filename, secret, nil
|
||||
}
|
||||
|
||||
func storageTOTPDeleteRunE(cmd *cobra.Command, args []string) (err error) {
|
||||
var (
|
||||
provider storage.Provider
|
||||
ctx = context.Background()
|
||||
)
|
||||
|
||||
// StorageTOTPDeleteRunE is the RunE for the authelia storage user totp delete command.
|
||||
func (ctx *CmdCtx) StorageTOTPDeleteRunE(cmd *cobra.Command, args []string) (err error) {
|
||||
user := args[0]
|
||||
|
||||
provider = getStorageProvider()
|
||||
|
||||
defer func() {
|
||||
_ = provider.Close()
|
||||
_ = ctx.providers.StorageProvider.Close()
|
||||
}()
|
||||
|
||||
if _, err = provider.LoadTOTPConfiguration(ctx, user); err != nil {
|
||||
if err = ctx.CheckSchemaVersion(); err != nil {
|
||||
return storageWrapCheckSchemaErr(err)
|
||||
}
|
||||
|
||||
if _, err = ctx.providers.StorageProvider.LoadTOTPConfiguration(ctx, user); err != nil {
|
||||
return fmt.Errorf("can't delete configuration for user '%s': %+v", user, err)
|
||||
}
|
||||
|
||||
if err = provider.DeleteTOTPConfiguration(ctx, user); err != nil {
|
||||
if err = ctx.providers.StorageProvider.DeleteTOTPConfiguration(ctx, user); err != nil {
|
||||
return fmt.Errorf("can't delete configuration for user '%s': %+v", user, err)
|
||||
}
|
||||
|
||||
|
@ -535,34 +450,30 @@ func storageTOTPDeleteRunE(cmd *cobra.Command, args []string) (err error) {
|
|||
return nil
|
||||
}
|
||||
|
||||
func storageTOTPExportRunE(cmd *cobra.Command, args []string) (err error) {
|
||||
// StorageTOTPExportRunE is the RunE for the authelia storage user totp export command.
|
||||
func (ctx *CmdCtx) StorageTOTPExportRunE(cmd *cobra.Command, args []string) (err error) {
|
||||
var (
|
||||
provider storage.Provider
|
||||
format, dir string
|
||||
configurations []model.TOTPConfiguration
|
||||
img image.Image
|
||||
|
||||
ctx = context.Background()
|
||||
)
|
||||
|
||||
provider = getStorageProvider()
|
||||
|
||||
defer func() {
|
||||
_ = provider.Close()
|
||||
_ = ctx.providers.StorageProvider.Close()
|
||||
}()
|
||||
|
||||
if err = checkStorageSchemaUpToDate(ctx, provider); err != nil {
|
||||
return err
|
||||
if err = ctx.CheckSchemaVersion(); err != nil {
|
||||
return storageWrapCheckSchemaErr(err)
|
||||
}
|
||||
|
||||
if format, dir, err = storageTOTPExportGetConfigFromFlags(cmd); err != nil {
|
||||
if format, dir, err = flagsGetTOTPExportOptions(cmd.Flags()); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
limit := 10
|
||||
|
||||
for page := 0; true; page++ {
|
||||
if configurations, err = provider.LoadTOTPConfigurations(ctx, limit, page); err != nil {
|
||||
if configurations, err = ctx.providers.StorageProvider.LoadTOTPConfigurations(ctx, limit, page); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
|
@ -607,56 +518,18 @@ func storageTOTPExportRunE(cmd *cobra.Command, args []string) (err error) {
|
|||
return nil
|
||||
}
|
||||
|
||||
func storageTOTPExportGetConfigFromFlags(cmd *cobra.Command) (format, dir string, err error) {
|
||||
if format, err = cmd.Flags().GetString(cmdFlagNameFormat); err != nil {
|
||||
return "", "", err
|
||||
}
|
||||
|
||||
if dir, err = cmd.Flags().GetString("dir"); err != nil {
|
||||
return "", "", err
|
||||
}
|
||||
|
||||
switch format {
|
||||
case storageTOTPExportFormatCSV, storageTOTPExportFormatURI:
|
||||
break
|
||||
case storageTOTPExportFormatPNG:
|
||||
if dir == "" {
|
||||
dir = utils.RandomString(8, utils.CharSetAlphaNumeric, false)
|
||||
}
|
||||
|
||||
if _, err = os.Stat(dir); !os.IsNotExist(err) {
|
||||
return "", "", errors.New("output directory must not exist")
|
||||
}
|
||||
|
||||
if err = os.MkdirAll(dir, 0700); err != nil {
|
||||
return "", "", err
|
||||
}
|
||||
default:
|
||||
return "", "", errors.New("format must be csv, uri, or png")
|
||||
}
|
||||
|
||||
return format, dir, nil
|
||||
}
|
||||
|
||||
func storageMigrateHistoryRunE(_ *cobra.Command, _ []string) (err error) {
|
||||
// StorageMigrateHistoryRunE is the RunE for the authelia storage migrate history command.
|
||||
func (ctx *CmdCtx) StorageMigrateHistoryRunE(_ *cobra.Command, _ []string) (err error) {
|
||||
var (
|
||||
provider storage.Provider
|
||||
version int
|
||||
migrations []model.Migration
|
||||
|
||||
ctx = context.Background()
|
||||
)
|
||||
|
||||
provider = getStorageProvider()
|
||||
if provider == nil {
|
||||
return errNoStorageProvider
|
||||
}
|
||||
|
||||
defer func() {
|
||||
_ = provider.Close()
|
||||
_ = ctx.providers.StorageProvider.Close()
|
||||
}()
|
||||
|
||||
if version, err = provider.SchemaVersion(ctx); err != nil {
|
||||
if version, err = ctx.providers.StorageProvider.SchemaVersion(ctx); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
|
@ -665,7 +538,7 @@ func storageMigrateHistoryRunE(_ *cobra.Command, _ []string) (err error) {
|
|||
return
|
||||
}
|
||||
|
||||
if migrations, err = provider.SchemaMigrationHistory(ctx); err != nil {
|
||||
if migrations, err = ctx.providers.StorageProvider.SchemaMigrationHistory(ctx); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
|
@ -682,26 +555,23 @@ func storageMigrateHistoryRunE(_ *cobra.Command, _ []string) (err error) {
|
|||
return nil
|
||||
}
|
||||
|
||||
func newStorageMigrateListRunE(up bool) func(cmd *cobra.Command, args []string) (err error) {
|
||||
// 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) {
|
||||
var (
|
||||
provider storage.Provider
|
||||
ctx = context.Background()
|
||||
migrations []model.SchemaMigration
|
||||
directionStr string
|
||||
)
|
||||
|
||||
provider = getStorageProvider()
|
||||
|
||||
defer func() {
|
||||
_ = provider.Close()
|
||||
_ = ctx.providers.StorageProvider.Close()
|
||||
}()
|
||||
|
||||
if up {
|
||||
migrations, err = provider.SchemaMigrationsUp(ctx, 0)
|
||||
migrations, err = ctx.providers.StorageProvider.SchemaMigrationsUp(ctx, 0)
|
||||
directionStr = "Up"
|
||||
} else {
|
||||
migrations, err = provider.SchemaMigrationsDown(ctx, 0)
|
||||
migrations, err = ctx.providers.StorageProvider.SchemaMigrationsDown(ctx, 0)
|
||||
directionStr = "Down"
|
||||
}
|
||||
|
||||
|
@ -723,19 +593,15 @@ func newStorageMigrateListRunE(up bool) func(cmd *cobra.Command, args []string)
|
|||
}
|
||||
}
|
||||
|
||||
func newStorageMigrationRunE(up bool) func(cmd *cobra.Command, args []string) (err error) {
|
||||
// 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) {
|
||||
var (
|
||||
provider storage.Provider
|
||||
target int
|
||||
|
||||
ctx = context.Background()
|
||||
target int
|
||||
)
|
||||
|
||||
provider = getStorageProvider()
|
||||
|
||||
defer func() {
|
||||
_ = provider.Close()
|
||||
_ = ctx.providers.StorageProvider.Close()
|
||||
}()
|
||||
|
||||
if target, err = cmd.Flags().GetInt(cmdFlagNameTarget); err != nil {
|
||||
|
@ -746,68 +612,48 @@ func newStorageMigrationRunE(up bool) func(cmd *cobra.Command, args []string) (e
|
|||
case up:
|
||||
switch cmd.Flags().Changed(cmdFlagNameTarget) {
|
||||
case true:
|
||||
return provider.SchemaMigrate(ctx, true, target)
|
||||
return ctx.providers.StorageProvider.SchemaMigrate(ctx, true, target)
|
||||
default:
|
||||
return provider.SchemaMigrate(ctx, true, storage.SchemaLatest)
|
||||
return ctx.providers.StorageProvider.SchemaMigrate(ctx, true, storage.SchemaLatest)
|
||||
}
|
||||
default:
|
||||
if !cmd.Flags().Changed(cmdFlagNameTarget) {
|
||||
return errors.New("you must set a target version")
|
||||
}
|
||||
|
||||
if err = storageMigrateDownConfirmDestroy(cmd); err != nil {
|
||||
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
|
||||
}
|
||||
|
||||
return provider.SchemaMigrate(ctx, false, target)
|
||||
if !confirmed {
|
||||
return errors.New("cancelling down migration due to user not accepting data destruction")
|
||||
}
|
||||
|
||||
return ctx.providers.StorageProvider.SchemaMigrate(ctx, false, target)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func storageMigrateDownConfirmDestroy(cmd *cobra.Command) (err error) {
|
||||
var destroy bool
|
||||
|
||||
if destroy, err = cmd.Flags().GetBool(cmdFlagNameDestroyData); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if !destroy {
|
||||
fmt.Printf("Schema Down Migrations may DESTROY data, type 'DESTROY' and press return to continue: ")
|
||||
|
||||
var text string
|
||||
|
||||
_, _ = fmt.Scanln(&text)
|
||||
|
||||
if text != "DESTROY" {
|
||||
return errors.New("cancelling down migration due to user not accepting data destruction")
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func storageSchemaInfoRunE(_ *cobra.Command, _ []string) (err error) {
|
||||
// StorageSchemaInfoRunE is the RunE for the authelia storage schema info command.
|
||||
func (ctx *CmdCtx) StorageSchemaInfoRunE(_ *cobra.Command, _ []string) (err error) {
|
||||
var (
|
||||
upgradeStr, tablesStr string
|
||||
|
||||
provider storage.Provider
|
||||
tables []string
|
||||
version, latest int
|
||||
|
||||
ctx = context.Background()
|
||||
)
|
||||
|
||||
provider = getStorageProvider()
|
||||
|
||||
defer func() {
|
||||
_ = provider.Close()
|
||||
_ = ctx.providers.StorageProvider.Close()
|
||||
}()
|
||||
|
||||
if version, err = provider.SchemaVersion(ctx); err != nil && err.Error() != "unknown schema state" {
|
||||
if version, err = ctx.providers.StorageProvider.SchemaVersion(ctx); err != nil && err.Error() != "unknown schema state" {
|
||||
return err
|
||||
}
|
||||
|
||||
if tables, err = provider.SchemaTables(ctx); err != nil {
|
||||
if tables, err = ctx.providers.StorageProvider.SchemaTables(ctx); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
|
@ -817,7 +663,7 @@ func storageSchemaInfoRunE(_ *cobra.Command, _ []string) (err error) {
|
|||
tablesStr = strings.Join(tables, ", ")
|
||||
}
|
||||
|
||||
if latest, err = provider.SchemaLatestVersion(); err != nil {
|
||||
if latest, err = ctx.providers.StorageProvider.SchemaLatestVersion(); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
|
@ -832,7 +678,7 @@ func storageSchemaInfoRunE(_ *cobra.Command, _ []string) (err error) {
|
|||
result storage.EncryptionValidationResult
|
||||
)
|
||||
|
||||
switch result, err = provider.SchemaEncryptionCheckKey(ctx, false); {
|
||||
switch result, err = ctx.providers.StorageProvider.SchemaEncryptionCheckKey(ctx, false); {
|
||||
case err != nil:
|
||||
if errors.Is(err, storage.ErrSchemaEncryptionVersionUnsupported) {
|
||||
encryption = "unsupported (schema version)"
|
||||
|
@ -850,30 +696,9 @@ func storageSchemaInfoRunE(_ *cobra.Command, _ []string) (err error) {
|
|||
return nil
|
||||
}
|
||||
|
||||
func checkStorageSchemaUpToDate(ctx context.Context, provider storage.Provider) (err error) {
|
||||
var version, latest int
|
||||
|
||||
if version, err = provider.SchemaVersion(ctx); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if latest, err = provider.SchemaLatestVersion(); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if version != latest {
|
||||
return fmt.Errorf("schema is version %d which is outdated please migrate to version %d in order to use this command or use an older binary", version, latest)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func storageUserIdentifiersExport(cmd *cobra.Command, _ []string) (err error) {
|
||||
// StorageUserIdentifiersExportRunE is the RunE for the authelia storage user identifiers export command.
|
||||
func (ctx *CmdCtx) StorageUserIdentifiersExportRunE(cmd *cobra.Command, _ []string) (err error) {
|
||||
var (
|
||||
provider storage.Provider
|
||||
|
||||
ctx = context.Background()
|
||||
|
||||
file string
|
||||
)
|
||||
|
||||
|
@ -890,7 +715,13 @@ func storageUserIdentifiersExport(cmd *cobra.Command, _ []string) (err error) {
|
|||
return fmt.Errorf("error occurred opening '%s': %w", file, err)
|
||||
}
|
||||
|
||||
provider = getStorageProvider()
|
||||
defer func() {
|
||||
_ = ctx.providers.StorageProvider.Close()
|
||||
}()
|
||||
|
||||
if err = ctx.CheckSchemaVersion(); err != nil {
|
||||
return storageWrapCheckSchemaErr(err)
|
||||
}
|
||||
|
||||
var (
|
||||
export model.UserOpaqueIdentifiersExport
|
||||
|
@ -898,7 +729,7 @@ func storageUserIdentifiersExport(cmd *cobra.Command, _ []string) (err error) {
|
|||
data []byte
|
||||
)
|
||||
|
||||
if export.Identifiers, err = provider.LoadUserOpaqueIdentifiers(ctx); err != nil {
|
||||
if export.Identifiers, err = ctx.providers.StorageProvider.LoadUserOpaqueIdentifiers(ctx); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
|
@ -919,12 +750,9 @@ func storageUserIdentifiersExport(cmd *cobra.Command, _ []string) (err error) {
|
|||
return nil
|
||||
}
|
||||
|
||||
func storageUserIdentifiersImport(cmd *cobra.Command, _ []string) (err error) {
|
||||
// StorageUserIdentifiersImportRunE is the RunE for the authelia storage user identifiers import command.
|
||||
func (ctx *CmdCtx) StorageUserIdentifiersImportRunE(cmd *cobra.Command, _ []string) (err error) {
|
||||
var (
|
||||
provider storage.Provider
|
||||
|
||||
ctx = context.Background()
|
||||
|
||||
file string
|
||||
stat os.FileInfo
|
||||
)
|
||||
|
@ -958,10 +786,16 @@ func storageUserIdentifiersImport(cmd *cobra.Command, _ []string) (err error) {
|
|||
return fmt.Errorf("can't import a file with no data")
|
||||
}
|
||||
|
||||
provider = getStorageProvider()
|
||||
defer func() {
|
||||
_ = ctx.providers.StorageProvider.Close()
|
||||
}()
|
||||
|
||||
if err = ctx.CheckSchemaVersion(); err != nil {
|
||||
return storageWrapCheckSchemaErr(err)
|
||||
}
|
||||
|
||||
for _, opaqueID := range export.Identifiers {
|
||||
if err = provider.SaveUserOpaqueIdentifier(ctx, opaqueID); err != nil {
|
||||
if err = ctx.providers.StorageProvider.SaveUserOpaqueIdentifier(ctx, opaqueID); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
@ -971,41 +805,26 @@ func storageUserIdentifiersImport(cmd *cobra.Command, _ []string) (err error) {
|
|||
return nil
|
||||
}
|
||||
|
||||
func containsIdentifier(identifier model.UserOpaqueIdentifier, identifiers []model.UserOpaqueIdentifier) bool {
|
||||
for i := 0; i < len(identifiers); i++ {
|
||||
if identifier.Service == identifiers[i].Service && identifier.SectorID == identifiers[i].SectorID && identifier.Username == identifiers[i].Username {
|
||||
return true
|
||||
}
|
||||
}
|
||||
|
||||
return false
|
||||
}
|
||||
|
||||
func storageUserIdentifiersGenerate(cmd *cobra.Command, _ []string) (err error) {
|
||||
// StorageUserIdentifiersGenerateRunE is the RunE for the authelia storage user identifiers generate command.
|
||||
func (ctx *CmdCtx) StorageUserIdentifiersGenerateRunE(cmd *cobra.Command, _ []string) (err error) {
|
||||
var (
|
||||
provider storage.Provider
|
||||
|
||||
ctx = context.Background()
|
||||
|
||||
users, services, sectors []string
|
||||
)
|
||||
|
||||
provider = getStorageProvider()
|
||||
defer func() {
|
||||
_ = ctx.providers.StorageProvider.Close()
|
||||
}()
|
||||
|
||||
identifiers, err := provider.LoadUserOpaqueIdentifiers(ctx)
|
||||
if err = ctx.CheckSchemaVersion(); err != nil {
|
||||
return storageWrapCheckSchemaErr(err)
|
||||
}
|
||||
|
||||
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)
|
||||
}
|
||||
|
||||
if users, err = cmd.Flags().GetStringSlice(cmdFlagNameUsers); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if services, err = cmd.Flags().GetStringSlice(cmdFlagNameServices); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if sectors, err = cmd.Flags().GetStringSlice(cmdFlagNameSectors); err != nil {
|
||||
if users, services, sectors, err = flagsGetUserIdentifiersGenerateOptions(cmd.Flags()); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
|
@ -1043,7 +862,7 @@ func storageUserIdentifiersGenerate(cmd *cobra.Command, _ []string) (err error)
|
|||
return fmt.Errorf("failed to generate a uuid: %w", err)
|
||||
}
|
||||
|
||||
if err = provider.SaveUserOpaqueIdentifier(ctx, identifier); err != nil {
|
||||
if err = ctx.providers.StorageProvider.SaveUserOpaqueIdentifier(ctx, identifier); err != nil {
|
||||
return fmt.Errorf("failed to save identifier: %w", err)
|
||||
}
|
||||
|
||||
|
@ -1057,12 +876,9 @@ func storageUserIdentifiersGenerate(cmd *cobra.Command, _ []string) (err error)
|
|||
return nil
|
||||
}
|
||||
|
||||
func storageUserIdentifiersAdd(cmd *cobra.Command, args []string) (err error) {
|
||||
// StorageUserIdentifiersAddRunE is the RunE for the authelia storage user identifiers add command.
|
||||
func (ctx *CmdCtx) StorageUserIdentifiersAddRunE(cmd *cobra.Command, args []string) (err error) {
|
||||
var (
|
||||
provider storage.Provider
|
||||
|
||||
ctx = context.Background()
|
||||
|
||||
service, sector string
|
||||
)
|
||||
|
||||
|
@ -1106,9 +922,15 @@ func storageUserIdentifiersAdd(cmd *cobra.Command, args []string) (err error) {
|
|||
}
|
||||
}
|
||||
|
||||
provider = getStorageProvider()
|
||||
defer func() {
|
||||
_ = ctx.providers.StorageProvider.Close()
|
||||
}()
|
||||
|
||||
if err = provider.SaveUserOpaqueIdentifier(ctx, opaqueID); err != nil {
|
||||
if err = ctx.CheckSchemaVersion(); err != nil {
|
||||
return storageWrapCheckSchemaErr(err)
|
||||
}
|
||||
|
||||
if err = ctx.providers.StorageProvider.SaveUserOpaqueIdentifier(ctx, opaqueID); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
|
|
|
@ -1,7 +1,9 @@
|
|||
package commands
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"io"
|
||||
"os"
|
||||
"syscall"
|
||||
|
||||
|
@ -24,16 +26,51 @@ func recoverErr(i any) error {
|
|||
}
|
||||
}
|
||||
|
||||
func configFilterExisting(configs []string) (finalConfigs []string) {
|
||||
var err error
|
||||
|
||||
for _, c := range configs {
|
||||
if _, err = os.Stat(c); err == nil || !os.IsNotExist(err) {
|
||||
finalConfigs = append(finalConfigs, c)
|
||||
}
|
||||
func flagsGetUserIdentifiersGenerateOptions(flags *pflag.FlagSet) (users, services, sectors []string, err error) {
|
||||
if users, err = flags.GetStringSlice(cmdFlagNameUsers); err != nil {
|
||||
return nil, nil, nil, err
|
||||
}
|
||||
|
||||
return finalConfigs
|
||||
if services, err = flags.GetStringSlice(cmdFlagNameServices); err != nil {
|
||||
return nil, nil, nil, err
|
||||
}
|
||||
|
||||
if sectors, err = flags.GetStringSlice(cmdFlagNameSectors); err != nil {
|
||||
return nil, nil, nil, err
|
||||
}
|
||||
|
||||
return users, services, sectors, nil
|
||||
}
|
||||
|
||||
func flagsGetTOTPExportOptions(flags *pflag.FlagSet) (format, dir string, err error) {
|
||||
if format, err = flags.GetString(cmdFlagNameFormat); err != nil {
|
||||
return "", "", err
|
||||
}
|
||||
|
||||
if dir, err = flags.GetString("dir"); err != nil {
|
||||
return "", "", err
|
||||
}
|
||||
|
||||
switch format {
|
||||
case storageTOTPExportFormatCSV, storageTOTPExportFormatURI:
|
||||
break
|
||||
case storageTOTPExportFormatPNG:
|
||||
if dir == "" {
|
||||
dir = utils.RandomString(8, utils.CharSetAlphaNumeric, false)
|
||||
}
|
||||
|
||||
if _, err = os.Stat(dir); !os.IsNotExist(err) {
|
||||
return "", "", errors.New("output directory must not exist")
|
||||
}
|
||||
|
||||
if err = os.MkdirAll(dir, 0700); err != nil {
|
||||
return "", "", err
|
||||
}
|
||||
default:
|
||||
return "", "", errors.New("format must be csv, uri, or png")
|
||||
}
|
||||
|
||||
return format, dir, nil
|
||||
}
|
||||
|
||||
//nolint:gocyclo
|
||||
|
@ -102,37 +139,92 @@ func flagsGetRandomCharacters(flags *pflag.FlagSet, flagNameLength, flagNameChar
|
|||
return utils.RandomString(n, charset, true), nil
|
||||
}
|
||||
|
||||
func termReadPasswordStrWithPrompt(prompt, flag string) (data string, err error) {
|
||||
var d []byte
|
||||
func termReadConfirmation(flags *pflag.FlagSet, name, prompt, confirmation string) (confirmed bool, err error) {
|
||||
if confirmed, _ = flags.GetBool(name); confirmed {
|
||||
return confirmed, nil
|
||||
}
|
||||
|
||||
terminal, fd, state, err := getTerminal(prompt)
|
||||
if err != nil {
|
||||
return false, err
|
||||
}
|
||||
|
||||
defer func(fd int, oldState *term.State) {
|
||||
_ = term.Restore(fd, oldState)
|
||||
}(fd, state)
|
||||
|
||||
var input string
|
||||
|
||||
if input, err = terminal.ReadLine(); err != nil {
|
||||
return false, fmt.Errorf("failed to read from the terminal: %w", err)
|
||||
}
|
||||
|
||||
if input != confirmation {
|
||||
return false, nil
|
||||
}
|
||||
|
||||
return true, nil
|
||||
}
|
||||
|
||||
func getTerminal(prompt string) (terminal *term.Terminal, fd int, state *term.State, err error) {
|
||||
fd = int(syscall.Stdin) //nolint:unconvert,nolintlint
|
||||
|
||||
if !term.IsTerminal(fd) {
|
||||
return nil, -1, nil, ErrStdinIsNotTerminal
|
||||
}
|
||||
|
||||
var width, height int
|
||||
|
||||
if width, height, err = term.GetSize(int(syscall.Stdout)); err != nil { //nolint:unconvert,nolintlint
|
||||
return nil, -1, nil, fmt.Errorf("failed to get terminal size: %w", err)
|
||||
}
|
||||
|
||||
state, err = term.MakeRaw(fd)
|
||||
if err != nil {
|
||||
return nil, -1, nil, fmt.Errorf("failed to get terminal state: %w", err)
|
||||
}
|
||||
|
||||
c := struct {
|
||||
io.Reader
|
||||
io.Writer
|
||||
}{
|
||||
os.Stdin,
|
||||
os.Stdout,
|
||||
}
|
||||
|
||||
terminal = term.NewTerminal(c, prompt)
|
||||
|
||||
if err = terminal.SetSize(width, height); err != nil {
|
||||
return nil, -1, nil, fmt.Errorf("failed to set terminal size: %w", err)
|
||||
}
|
||||
|
||||
return terminal, fd, state, nil
|
||||
}
|
||||
|
||||
func termReadPasswordWithPrompt(prompt, flag string) (password string, err error) {
|
||||
terminal, fd, state, err := getTerminal("")
|
||||
if err != nil {
|
||||
if errors.Is(err, ErrStdinIsNotTerminal) {
|
||||
switch len(flag) {
|
||||
case 0:
|
||||
return "", err
|
||||
case 1:
|
||||
return "", fmt.Errorf("you must either use an interactive terminal or use the -%s flag", flag)
|
||||
default:
|
||||
return "", fmt.Errorf("you must either use an interactive terminal or use the --%s flag", flag)
|
||||
}
|
||||
}
|
||||
|
||||
if d, err = termReadPasswordWithPrompt(prompt, flag); err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
return string(d), nil
|
||||
}
|
||||
defer func(fd int, oldState *term.State) {
|
||||
_ = term.Restore(fd, oldState)
|
||||
}(fd, state)
|
||||
|
||||
func termReadPasswordWithPrompt(prompt, flag string) (data []byte, err error) {
|
||||
fd := int(syscall.Stdin) //nolint:unconvert,nolintlint
|
||||
|
||||
if isTerm := term.IsTerminal(fd); !isTerm {
|
||||
switch len(flag) {
|
||||
case 0:
|
||||
return nil, ErrStdinIsNotTerminal
|
||||
case 1:
|
||||
return nil, fmt.Errorf("you must either use an interactive terminal or use the -%s flag", flag)
|
||||
default:
|
||||
return nil, fmt.Errorf("you must either use an interactive terminal or use the --%s flag", flag)
|
||||
}
|
||||
if password, err = terminal.ReadPassword(prompt); err != nil {
|
||||
return "", fmt.Errorf("failed to read the input from the terminal: %w", err)
|
||||
}
|
||||
|
||||
fmt.Print(prompt)
|
||||
|
||||
if data, err = term.ReadPassword(fd); err != nil {
|
||||
return nil, fmt.Errorf("failed to read the input from the terminal: %w", err)
|
||||
}
|
||||
|
||||
fmt.Println("")
|
||||
|
||||
return data, nil
|
||||
return password, nil
|
||||
}
|
||||
|
|
|
@ -4,64 +4,51 @@ import (
|
|||
"fmt"
|
||||
|
||||
"github.com/spf13/cobra"
|
||||
|
||||
"github.com/authelia/authelia/v4/internal/configuration"
|
||||
"github.com/authelia/authelia/v4/internal/configuration/schema"
|
||||
)
|
||||
|
||||
func newValidateConfigCmd() (cmd *cobra.Command) {
|
||||
func newValidateConfigCmd(ctx *CmdCtx) (cmd *cobra.Command) {
|
||||
cmd = &cobra.Command{
|
||||
Use: "validate-config",
|
||||
Short: cmdAutheliaValidateConfigShort,
|
||||
Long: cmdAutheliaValidateConfigLong,
|
||||
Example: cmdAutheliaValidateConfigExample,
|
||||
Args: cobra.NoArgs,
|
||||
RunE: cmdValidateConfigRunE,
|
||||
PreRunE: ctx.ChainRunE(
|
||||
ctx.ConfigLoadRunE,
|
||||
ctx.ConfigValidateKeysRunE,
|
||||
ctx.ConfigValidateRunE,
|
||||
),
|
||||
RunE: ctx.ValidateConfigRunE,
|
||||
|
||||
DisableAutoGenTag: true,
|
||||
}
|
||||
|
||||
cmdWithConfigFlags(cmd, false, []string{"configuration.yml"})
|
||||
|
||||
return cmd
|
||||
}
|
||||
|
||||
func cmdValidateConfigRunE(cmd *cobra.Command, _ []string) (err error) {
|
||||
var (
|
||||
configs []string
|
||||
val *schema.StructValidator
|
||||
)
|
||||
|
||||
if configs, err = cmd.Flags().GetStringSlice(cmdFlagNameConfig); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
config, val, err = loadConfig(configs, true, true, configuration.NewFileFiltersDefault()...)
|
||||
if err != nil {
|
||||
return fmt.Errorf("error occurred loading configuration: %v", err)
|
||||
}
|
||||
|
||||
// ValidateConfigRunE is the RunE for the authelia validate-config command.
|
||||
func (ctx *CmdCtx) ValidateConfigRunE(_ *cobra.Command, _ []string) (err error) {
|
||||
switch {
|
||||
case val.HasErrors():
|
||||
case ctx.cconfig.validator.HasErrors():
|
||||
fmt.Println("Configuration parsed and loaded with errors:")
|
||||
fmt.Println("")
|
||||
|
||||
for _, err = range val.Errors() {
|
||||
for _, err = range ctx.cconfig.validator.Errors() {
|
||||
fmt.Printf("\t - %v\n", err)
|
||||
}
|
||||
|
||||
fmt.Println("")
|
||||
|
||||
if !val.HasWarnings() {
|
||||
if !ctx.cconfig.validator.HasWarnings() {
|
||||
break
|
||||
}
|
||||
|
||||
fallthrough
|
||||
case val.HasWarnings():
|
||||
case ctx.cconfig.validator.HasWarnings():
|
||||
fmt.Println("Configuration parsed and loaded with warnings:")
|
||||
fmt.Println("")
|
||||
|
||||
for _, err = range val.Warnings() {
|
||||
for _, err = range ctx.cconfig.validator.Warnings() {
|
||||
fmt.Printf("\t - %v\n", err)
|
||||
}
|
||||
|
||||
|
|
|
@ -86,15 +86,13 @@ func loadSources(ko *koanf.Koanf, val *schema.StructValidator, sources ...Source
|
|||
}
|
||||
|
||||
for _, source := range sources {
|
||||
err := source.Load(val)
|
||||
if err != nil {
|
||||
if err = source.Load(val); err != nil {
|
||||
val.Push(fmt.Errorf("failed to load configuration from %s source: %+v", source.Name(), err))
|
||||
|
||||
continue
|
||||
}
|
||||
|
||||
err = source.Merge(ko, val)
|
||||
if err != nil {
|
||||
if err = source.Merge(ko, val); err != nil {
|
||||
val.Push(fmt.Errorf("failed to merge configuration from %s source: %+v", source.Name(), err))
|
||||
|
||||
continue
|
||||
|
|
|
@ -224,7 +224,9 @@ func NewDefaultSourcesFiltered(files []string, filters []FileFilter, prefix, del
|
|||
|
||||
// NewDefaultSourcesWithDefaults returns a slice of Source configured to load from specified YAML files with additional sources.
|
||||
func NewDefaultSourcesWithDefaults(files []string, filters []FileFilter, prefix, delimiter string, defaults Source, additionalSources ...Source) (sources []Source) {
|
||||
sources = []Source{defaults}
|
||||
if defaults != nil {
|
||||
sources = []Source{defaults}
|
||||
}
|
||||
|
||||
if len(filters) == 0 {
|
||||
sources = append(sources, NewDefaultSources(files, prefix, delimiter, additionalSources...)...)
|
||||
|
|
|
@ -21,28 +21,28 @@ type YAMLFileSource struct {
|
|||
filters []FileFilter
|
||||
}
|
||||
|
||||
// EnvironmentSource is a configuration Source which loads values from the environment.
|
||||
// EnvironmentSource is a configuration configuration.Source which loads values from the environment.
|
||||
type EnvironmentSource struct {
|
||||
koanf *koanf.Koanf
|
||||
prefix string
|
||||
delimiter string
|
||||
}
|
||||
|
||||
// SecretsSource loads environment variables that have a value pointing to a file.
|
||||
// SecretsSource is a configuration.Source which loads environment variables that have a value pointing to a file.
|
||||
type SecretsSource struct {
|
||||
koanf *koanf.Koanf
|
||||
prefix string
|
||||
delimiter string
|
||||
}
|
||||
|
||||
// CommandLineSource loads configuration from the command line flags.
|
||||
// CommandLineSource is a configuration.Source which loads configuration from the command line flags.
|
||||
type CommandLineSource struct {
|
||||
koanf *koanf.Koanf
|
||||
flags *pflag.FlagSet
|
||||
callback func(flag *pflag.Flag) (string, any)
|
||||
}
|
||||
|
||||
// MapSource loads configuration from the command line flags.
|
||||
// MapSource is a configuration.Source which loads configuration from the command line flags.
|
||||
type MapSource struct {
|
||||
m map[string]any
|
||||
koanf *koanf.Koanf
|
||||
|
|
|
@ -84,18 +84,6 @@ func (s *CLISuite) TestShouldFailValidateConfig() {
|
|||
s.Assert().Contains(output, "failed to load configuration from yaml file(/config/invalid.yml) source: open /config/invalid.yml: no such file or directory")
|
||||
}
|
||||
|
||||
func (s *CLISuite) TestShouldHashPasswordArgon2idLegacy() {
|
||||
output, err := s.Exec("authelia-backend", []string{"authelia", s.testArg, s.coverageArg, "hash-password", "test", "-m", "32"})
|
||||
s.Assert().NoError(err)
|
||||
s.Assert().Contains(output, "Digest: $argon2id$v=19$m=32768,t=3,p=4$")
|
||||
}
|
||||
|
||||
func (s *CLISuite) TestShouldHashPasswordSHA512Legacy() {
|
||||
output, err := s.Exec("authelia-backend", []string{"authelia", s.testArg, s.coverageArg, "hash-password", "test", "-z"})
|
||||
s.Assert().NoError(err)
|
||||
s.Assert().Contains(output, "Digest: $6$rounds=50000")
|
||||
}
|
||||
|
||||
func (s *CLISuite) TestShouldHashPasswordArgon2() {
|
||||
var (
|
||||
output string
|
||||
|
@ -782,18 +770,6 @@ func (s *CLISuite) TestShouldNotGenerateRSAWithBadCAFileContent() {
|
|||
s.Assert().Contains(output, "Error: could not parse certificate from file '/tmp/ca.public.bad.crt': failed to parse PEM block containing the key\n")
|
||||
}
|
||||
|
||||
func (s *CLISuite) TestStorageShouldShowErrWithoutConfig() {
|
||||
output, err := s.Exec("authelia-backend", []string{"authelia", s.testArg, s.coverageArg, "storage", "schema-info"})
|
||||
s.Assert().EqualError(err, "exit status 1")
|
||||
|
||||
s.Assert().Contains(output, "Error: storage: configuration for a 'local', 'mysql' or 'postgres' database must be provided, storage: option 'encryption_key' is required\n")
|
||||
|
||||
output, err = s.Exec("authelia-backend", []string{"authelia", s.testArg, s.coverageArg, "storage", "migrate", "history"})
|
||||
s.Assert().EqualError(err, "exit status 1")
|
||||
|
||||
s.Assert().Contains(output, "Error: storage: configuration for a 'local', 'mysql' or 'postgres' database must be provided, storage: option 'encryption_key' is required\n")
|
||||
}
|
||||
|
||||
func (s *CLISuite) TestStorage00ShouldShowCorrectPreInitInformation() {
|
||||
_ = os.Remove("/tmp/db.sqlite3")
|
||||
|
||||
|
@ -804,8 +780,7 @@ func (s *CLISuite) TestStorage00ShouldShowCorrectPreInitInformation() {
|
|||
|
||||
s.Assert().Regexp(pattern, output)
|
||||
|
||||
patternOutdated := regexp.MustCompile(`Error: schema is version \d+ which is outdated please migrate to version \d+ in order to use this command or use an older binary`)
|
||||
|
||||
patternOutdated := regexp.MustCompile(`Error: command requires the use of a up to date schema version: storage schema outdated: version \d+ is outdated please migrate to version \d+ in order to use this command or use an older binary`)
|
||||
output, err = s.Exec("authelia-backend", []string{"authelia", s.testArg, s.coverageArg, "storage", "user", "totp", "export", "--config=/config/configuration.storage.yml"})
|
||||
s.Assert().EqualError(err, "exit status 1")
|
||||
s.Assert().Regexp(patternOutdated, output)
|
||||
|
@ -815,8 +790,8 @@ func (s *CLISuite) TestStorage00ShouldShowCorrectPreInitInformation() {
|
|||
s.Assert().Regexp(patternOutdated, output)
|
||||
|
||||
output, err = s.Exec("authelia-backend", []string{"authelia", s.testArg, s.coverageArg, "storage", "encryption", "check", "--config=/config/configuration.storage.yml"})
|
||||
s.Assert().NoError(err)
|
||||
s.Assert().Contains(output, "Storage Encryption Key Validation: FAILURE\n\n\tCause: The schema version doesn't support encryption.\n")
|
||||
s.Assert().EqualError(err, "exit status 1")
|
||||
s.Assert().Contains(output, "Error: command requires the use of a up to date schema version: storage schema outdated: version 0 is outdated please migrate to version 7 in order to use this command or use an older binary\n")
|
||||
|
||||
output, err = s.Exec("authelia-backend", []string{"authelia", s.testArg, s.coverageArg, "storage", "migrate", "down", "--target=0", "--destroy-data", "--config=/config/configuration.storage.yml"})
|
||||
s.Assert().EqualError(err, "exit status 1")
|
||||
|
|
Loading…
Reference in New Issue