feat(storage): postgresql schema and ssl options (#2659)
Adds the schema name and all ssl options for PostgreSQL. Also a significant refactor of the storage validation process.pull/2662/head
parent
252b844b46
commit
f90ca855e3
|
@ -556,11 +556,16 @@ storage:
|
|||
# host: 127.0.0.1
|
||||
# port: 5432
|
||||
# database: authelia
|
||||
# schema: public
|
||||
# username: authelia
|
||||
# ## Password can also be set using a secret: https://www.authelia.com/docs/configuration/secrets.html
|
||||
# password: mypassword
|
||||
# timeout: 5s
|
||||
# sslmode: disable
|
||||
# ssl:
|
||||
# mode: disable
|
||||
# root_certificate: disable
|
||||
# certificate: disable
|
||||
# key: disable
|
||||
|
||||
##
|
||||
## Notification Provider
|
||||
|
|
|
@ -32,10 +32,8 @@ See the [encryption_key docs](./index.md#encryption_key).
|
|||
<div markdown="1">
|
||||
type: string
|
||||
{: .label .label-config .label-purple }
|
||||
default: localhost
|
||||
{: .label .label-config .label-blue }
|
||||
required: no
|
||||
{: .label .label-config .label-green }
|
||||
required: yes
|
||||
{: .label .label-config .label-red }
|
||||
</div>
|
||||
|
||||
The database server host.
|
||||
|
|
|
@ -19,9 +19,14 @@ storage:
|
|||
host: 127.0.0.1
|
||||
port: 5432
|
||||
database: authelia
|
||||
schema: public
|
||||
username: authelia
|
||||
password: mypassword
|
||||
sslmode: disable
|
||||
ssl:
|
||||
mode: disable
|
||||
root_certificate: /path/to/root_cert.pem
|
||||
certificate: /path/to/cert.pem
|
||||
key: /path/to/key.pem
|
||||
```
|
||||
|
||||
## Options
|
||||
|
@ -33,10 +38,8 @@ See the [encryption_key docs](./index.md#encryption_key).
|
|||
<div markdown="1">
|
||||
type: string
|
||||
{: .label .label-config .label-purple }
|
||||
default: localhost
|
||||
{: .label .label-config .label-blue }
|
||||
required: no
|
||||
{: .label .label-config .label-green }
|
||||
required: yes
|
||||
{: .label .label-config .label-red }
|
||||
</div>
|
||||
|
||||
The database server host.
|
||||
|
@ -59,10 +62,29 @@ required: no
|
|||
The port the database server is listening on.
|
||||
|
||||
### database
|
||||
<div markdown="1">
|
||||
type: string
|
||||
{: .label .label-config .label-purple }
|
||||
required: yes
|
||||
{: .label .label-config .label-red }
|
||||
</div>
|
||||
|
||||
The database name on the database server that the assigned [user](#username) has access to for the purpose of
|
||||
**Authelia**.
|
||||
|
||||
### schema
|
||||
<div markdown="1">
|
||||
type: string
|
||||
{: .label .label-config .label-purple }
|
||||
default: public
|
||||
{: .label .label-config .label-blue }
|
||||
required: no
|
||||
{: .label .label-config .label-green }
|
||||
</div>
|
||||
|
||||
The database schema name to use on the database server that the assigned [user](#username) has access to for the purpose
|
||||
of **Authelia**. By default this is the public schema.
|
||||
|
||||
### username
|
||||
<div markdown="1">
|
||||
type: string
|
||||
|
@ -96,7 +118,9 @@ required: no
|
|||
|
||||
The SQL connection timeout.
|
||||
|
||||
### sslmode
|
||||
### ssl
|
||||
|
||||
#### mode
|
||||
<div markdown="1">
|
||||
type: string
|
||||
{: .label .label-config .label-purple }
|
||||
|
@ -111,3 +135,33 @@ Valid options are 'disable', 'require', 'verify-ca', or 'verify-full'.
|
|||
See the [PostgreSQL Documentation](https://www.postgresql.org/docs/12/libpq-ssl.html)
|
||||
or [pgx - PostgreSQL Driver and Toolkit Documentation](https://pkg.go.dev/github.com/jackc/pgx?tab=doc)
|
||||
for more information.
|
||||
|
||||
#### root_certificate
|
||||
<div markdown="1">
|
||||
type: string
|
||||
{: .label .label-config .label-purple }
|
||||
required: no
|
||||
{: .label .label-config .label-green }
|
||||
</div>
|
||||
|
||||
The optional location of the root certificate file encoded in the PEM format for validation purposes.
|
||||
|
||||
#### certificate
|
||||
<div markdown="1">
|
||||
type: string
|
||||
{: .label .label-config .label-purple }
|
||||
required: no
|
||||
{: .label .label-config .label-green }
|
||||
</div>
|
||||
|
||||
The optional location of the certificate file encoded in the PEM format for validation purposes.
|
||||
|
||||
#### key
|
||||
<div markdown="1">
|
||||
type: string
|
||||
{: .label .label-config .label-purple }
|
||||
required: no
|
||||
{: .label .label-config .label-green }
|
||||
</div>
|
||||
|
||||
The optional location of the key file encoded in the PEM format for authentication purposes.
|
||||
|
|
|
@ -28,8 +28,13 @@ func NewStorageCmd() (cmd *cobra.Command) {
|
|||
cmd.PersistentFlags().String("postgres.host", "", "the PostgreSQL hostname")
|
||||
cmd.PersistentFlags().Int("postgres.port", 5432, "the PostgreSQL port")
|
||||
cmd.PersistentFlags().String("postgres.database", "authelia", "the PostgreSQL database name")
|
||||
cmd.PersistentFlags().String("postgres.schema", "public", "the PostgreSQL schema name")
|
||||
cmd.PersistentFlags().String("postgres.username", "authelia", "the PostgreSQL username")
|
||||
cmd.PersistentFlags().String("postgres.password", "", "the PostgreSQL password")
|
||||
cmd.PersistentFlags().String("postgres.ssl.mode", "disable", "the PostgreSQL ssl mode")
|
||||
cmd.PersistentFlags().String("postgres.ssl.root_certificate", "", "the PostgreSQL ssl root certificate file location")
|
||||
cmd.PersistentFlags().String("postgres.ssl.certificate", "", "the PostgreSQL ssl certificate file location")
|
||||
cmd.PersistentFlags().String("postgres.ssl.key", "", "the PostgreSQL ssl key file location")
|
||||
|
||||
cmd.AddCommand(
|
||||
newStorageMigrateCmd(),
|
||||
|
|
|
@ -42,17 +42,24 @@ func storagePersistentPreRunE(cmd *cobra.Command, _ []string) (err error) {
|
|||
mapping := map[string]string{
|
||||
"encryption-key": "storage.encryption_key",
|
||||
"sqlite.path": "storage.local.path",
|
||||
|
||||
"mysql.host": "storage.mysql.host",
|
||||
"mysql.port": "storage.mysql.port",
|
||||
"mysql.database": "storage.mysql.database",
|
||||
"mysql.username": "storage.mysql.username",
|
||||
"mysql.password": "storage.mysql.password",
|
||||
|
||||
"postgres.host": "storage.postgres.host",
|
||||
"postgres.port": "storage.postgres.port",
|
||||
"postgres.database": "storage.postgres.database",
|
||||
"postgres.schema": "storage.postgres.schema",
|
||||
"postgres.username": "storage.postgres.username",
|
||||
"postgres.password": "storage.postgres.password",
|
||||
"postgres.schema": "storage.postgres.schema",
|
||||
"postgres.ssl.mode": "storage.postgres.ssl.mode",
|
||||
"postgres.ssl.root_certificate": "storage.postgres.ssl.root_certificate",
|
||||
"postgres.ssl.certificate": "storage.postgres.ssl.certificate",
|
||||
"postgres.ssl.key": "storage.postgres.ssl.key",
|
||||
|
||||
"period": "totp.period",
|
||||
"digits": "totp.digits",
|
||||
"algorithm": "totp.algorithm",
|
||||
|
|
|
@ -556,11 +556,16 @@ storage:
|
|||
# host: 127.0.0.1
|
||||
# port: 5432
|
||||
# database: authelia
|
||||
# schema: public
|
||||
# username: authelia
|
||||
# ## Password can also be set using a secret: https://www.authelia.com/docs/configuration/secrets.html
|
||||
# password: mypassword
|
||||
# timeout: 5s
|
||||
# sslmode: disable
|
||||
# ssl:
|
||||
# mode: disable
|
||||
# root_certificate: disable
|
||||
# certificate: disable
|
||||
# key: disable
|
||||
|
||||
##
|
||||
## Notification Provider
|
||||
|
|
|
@ -22,12 +22,25 @@ type MySQLStorageConfiguration struct {
|
|||
SQLStorageConfiguration `koanf:",squash"`
|
||||
}
|
||||
|
||||
// PostgreSQLStorageConfiguration represents the configuration of a Postgres database.
|
||||
// PostgreSQLStorageConfiguration represents the configuration of a PostgreSQL database.
|
||||
type PostgreSQLStorageConfiguration struct {
|
||||
SQLStorageConfiguration `koanf:",squash"`
|
||||
Schema string `koanf:"schema"`
|
||||
|
||||
SSL PostgreSQLSSLStorageConfiguration `koanf:"ssl"`
|
||||
|
||||
// Deprecated. TODO: Remove in v4.36.0.
|
||||
SSLMode string `koanf:"sslmode"`
|
||||
}
|
||||
|
||||
// PostgreSQLSSLStorageConfiguration represents the SSL configuration of a PostgreSQL database.
|
||||
type PostgreSQLSSLStorageConfiguration struct {
|
||||
Mode string `koanf:"mode"`
|
||||
RootCertificate string `koanf:"root_certificate"`
|
||||
Certificate string `koanf:"certificate"`
|
||||
Key string `koanf:"key"`
|
||||
}
|
||||
|
||||
// StorageConfiguration represents the configuration of the storage backend.
|
||||
type StorageConfiguration struct {
|
||||
Local *LocalStorageConfiguration `koanf:"local"`
|
||||
|
@ -37,16 +50,7 @@ type StorageConfiguration struct {
|
|||
EncryptionKey string `koanf:"encryption_key"`
|
||||
}
|
||||
|
||||
// DefaultPostgreSQLStorageConfiguration represents the default PostgreSQL configuration.
|
||||
var DefaultPostgreSQLStorageConfiguration = PostgreSQLStorageConfiguration{
|
||||
SQLStorageConfiguration: SQLStorageConfiguration{
|
||||
// DefaultSQLStorageConfiguration represents the default SQL configuration.
|
||||
var DefaultSQLStorageConfiguration = SQLStorageConfiguration{
|
||||
Timeout: 5 * time.Second,
|
||||
},
|
||||
}
|
||||
|
||||
// DefaultMySQLStorageConfiguration represents the default MySQL configuration.
|
||||
var DefaultMySQLStorageConfiguration = MySQLStorageConfiguration{
|
||||
SQLStorageConfiguration: SQLStorageConfiguration{
|
||||
Timeout: 5 * time.Second,
|
||||
},
|
||||
}
|
||||
|
|
|
@ -61,6 +61,18 @@ const (
|
|||
errFmtTOTPInvalidDigits = "totp: digits '%d' is invalid: must be 6 or 8"
|
||||
)
|
||||
|
||||
// Storage Error constants.
|
||||
const (
|
||||
errStrStorage = "storage: configuration for a 'local', 'mysql' or 'postgres' database must be provided"
|
||||
errStrStorageEncryptionKeyMustBeProvided = "storage: 'encryption_key' configuration option must be provided"
|
||||
errStrStorageEncryptionKeyTooShort = "storage: 'encryption_key' configuration option must be 20 characters or longer"
|
||||
errFmtStorageUserPassMustBeProvided = "storage: %s: 'username' and 'password' configuration options must be provided" //nolint: gosec
|
||||
errFmtStorageOptionMustBeProvided = "storage: %s: '%s' configuration option must be provided"
|
||||
errFmtStoragePostgreSQLInvalidSSLMode = "storage: postgres: ssl: 'mode' configuration option '%s' is invalid: must be one of '%s'"
|
||||
)
|
||||
|
||||
var storagePostgreSQLValidSSLModes = []string{testModeDisabled, "require", "verify-ca", "verify-full"}
|
||||
|
||||
// OpenID Error constants.
|
||||
const (
|
||||
errFmtOIDCClientsDuplicateID = "openid connect provider: one or more clients have the same ID"
|
||||
|
@ -235,7 +247,13 @@ var ValidKeys = []string{
|
|||
"storage.postgres.username",
|
||||
"storage.postgres.password",
|
||||
"storage.postgres.timeout",
|
||||
"storage.postgres.sslmode",
|
||||
"storage.postgres.schema",
|
||||
"storage.postgres.ssl.mode",
|
||||
"storage.postgres.ssl.root_certificate",
|
||||
"storage.postgres.ssl.certificate",
|
||||
"storage.postgres.ssl.key",
|
||||
|
||||
"storage.postgres.sslmode", // Deprecated. TODO: Remove in v4.36.0.
|
||||
|
||||
// FileSystem Notifier Keys.
|
||||
"notifier.filesystem.filename",
|
||||
|
|
|
@ -2,19 +2,22 @@ package validator
|
|||
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"strings"
|
||||
|
||||
"github.com/authelia/authelia/v4/internal/configuration/schema"
|
||||
"github.com/authelia/authelia/v4/internal/utils"
|
||||
)
|
||||
|
||||
// ValidateStorage validates storage configuration.
|
||||
func ValidateStorage(configuration schema.StorageConfiguration, validator *schema.StructValidator) {
|
||||
if configuration.Local == nil && configuration.MySQL == nil && configuration.PostgreSQL == nil {
|
||||
validator.Push(errors.New("A storage configuration must be provided. It could be 'local', 'mysql' or 'postgres'"))
|
||||
validator.Push(errors.New(errStrStorage))
|
||||
}
|
||||
|
||||
switch {
|
||||
case configuration.MySQL != nil:
|
||||
validateMySQLConfiguration(&configuration.MySQL.SQLStorageConfiguration, validator)
|
||||
validateSQLConfiguration(&configuration.MySQL.SQLStorageConfiguration, validator, "mysql")
|
||||
case configuration.PostgreSQL != nil:
|
||||
validatePostgreSQLConfiguration(configuration.PostgreSQL, validator)
|
||||
case configuration.Local != nil:
|
||||
|
@ -22,45 +25,47 @@ func ValidateStorage(configuration schema.StorageConfiguration, validator *schem
|
|||
}
|
||||
|
||||
if configuration.EncryptionKey == "" {
|
||||
validator.Push(errors.New("the configuration option storage.encryption_key must be provided"))
|
||||
validator.Push(errors.New(errStrStorageEncryptionKeyMustBeProvided))
|
||||
} else if len(configuration.EncryptionKey) < 20 {
|
||||
validator.Push(errors.New("the configuration option storage.encryption_key must be 20 characters or longer"))
|
||||
validator.Push(errors.New(errStrStorageEncryptionKeyTooShort))
|
||||
}
|
||||
}
|
||||
|
||||
func validateMySQLConfiguration(configuration *schema.SQLStorageConfiguration, validator *schema.StructValidator) {
|
||||
func validateSQLConfiguration(configuration *schema.SQLStorageConfiguration, validator *schema.StructValidator, provider string) {
|
||||
if configuration.Timeout == 0 {
|
||||
configuration.Timeout = schema.DefaultMySQLStorageConfiguration.Timeout
|
||||
configuration.Timeout = schema.DefaultSQLStorageConfiguration.Timeout
|
||||
}
|
||||
|
||||
if configuration.Password == "" || configuration.Username == "" {
|
||||
validator.Push(errors.New("the SQL username and password must be provided"))
|
||||
if configuration.Host == "" {
|
||||
validator.Push(fmt.Errorf(errFmtStorageOptionMustBeProvided, provider, "host"))
|
||||
}
|
||||
|
||||
if configuration.Username == "" || configuration.Password == "" {
|
||||
validator.Push(fmt.Errorf(errFmtStorageUserPassMustBeProvided, provider))
|
||||
}
|
||||
|
||||
if configuration.Database == "" {
|
||||
validator.Push(errors.New("the SQL database must be provided"))
|
||||
validator.Push(fmt.Errorf(errFmtStorageOptionMustBeProvided, provider, "database"))
|
||||
}
|
||||
}
|
||||
|
||||
func validatePostgreSQLConfiguration(configuration *schema.PostgreSQLStorageConfiguration, validator *schema.StructValidator) {
|
||||
validateMySQLConfiguration(&configuration.SQLStorageConfiguration, validator)
|
||||
validateSQLConfiguration(&configuration.SQLStorageConfiguration, validator, "postgres")
|
||||
|
||||
if configuration.Timeout == 0 {
|
||||
configuration.Timeout = schema.DefaultPostgreSQLStorageConfiguration.Timeout
|
||||
// Deprecated. TODO: Remove in v4.36.0.
|
||||
if configuration.SSLMode != "" && configuration.SSL.Mode == "" {
|
||||
configuration.SSL.Mode = configuration.SSLMode
|
||||
}
|
||||
|
||||
if configuration.SSLMode == "" {
|
||||
configuration.SSLMode = testModeDisabled
|
||||
}
|
||||
|
||||
if !(configuration.SSLMode == testModeDisabled || configuration.SSLMode == "require" ||
|
||||
configuration.SSLMode == "verify-ca" || configuration.SSLMode == "verify-full") {
|
||||
validator.Push(errors.New("SSL mode must be 'disable', 'require', 'verify-ca', or 'verify-full'"))
|
||||
if configuration.SSL.Mode == "" {
|
||||
configuration.SSL.Mode = testModeDisabled
|
||||
} else if !utils.IsStringInSlice(configuration.SSL.Mode, storagePostgreSQLValidSSLModes) {
|
||||
validator.Push(fmt.Errorf(errFmtStoragePostgreSQLInvalidSSLMode, configuration.SSL.Mode, strings.Join(storagePostgreSQLValidSSLModes, "', '")))
|
||||
}
|
||||
}
|
||||
|
||||
func validateLocalStorageConfiguration(configuration *schema.LocalStorageConfiguration, validator *schema.StructValidator) {
|
||||
if configuration.Path == "" {
|
||||
validator.Push(errors.New("A file path must be provided with key 'path'"))
|
||||
validator.Push(fmt.Errorf(errFmtStorageOptionMustBeProvided, "local", "path"))
|
||||
}
|
||||
}
|
||||
|
|
|
@ -17,51 +17,57 @@ type StorageSuite struct {
|
|||
func (suite *StorageSuite) SetupTest() {
|
||||
suite.validator = schema.NewStructValidator()
|
||||
suite.configuration.EncryptionKey = testEncryptionKey
|
||||
suite.configuration.Local = &schema.LocalStorageConfiguration{
|
||||
Path: "/this/is/a/path",
|
||||
}
|
||||
suite.configuration.Local = nil
|
||||
suite.configuration.PostgreSQL = nil
|
||||
suite.configuration.MySQL = nil
|
||||
}
|
||||
|
||||
func (suite *StorageSuite) TestShouldValidateOneStorageIsConfigured() {
|
||||
suite.configuration.Local = nil
|
||||
suite.configuration.PostgreSQL = nil
|
||||
suite.configuration.MySQL = nil
|
||||
|
||||
ValidateStorage(suite.configuration, suite.validator)
|
||||
|
||||
suite.Assert().False(suite.validator.HasWarnings())
|
||||
suite.Require().Len(suite.validator.Warnings(), 0)
|
||||
suite.Require().Len(suite.validator.Errors(), 1)
|
||||
suite.Assert().EqualError(suite.validator.Errors()[0], "A storage configuration must be provided. It could be 'local', 'mysql' or 'postgres'")
|
||||
suite.Assert().EqualError(suite.validator.Errors()[0], "storage: configuration for a 'local', 'mysql' or 'postgres' database must be provided")
|
||||
}
|
||||
|
||||
func (suite *StorageSuite) TestShouldValidateLocalPathIsProvided() {
|
||||
suite.configuration.Local.Path = ""
|
||||
suite.configuration.Local = &schema.LocalStorageConfiguration{
|
||||
Path: "",
|
||||
}
|
||||
|
||||
ValidateStorage(suite.configuration, suite.validator)
|
||||
|
||||
suite.Assert().False(suite.validator.HasWarnings())
|
||||
suite.Require().Len(suite.validator.Warnings(), 0)
|
||||
suite.Require().Len(suite.validator.Errors(), 1)
|
||||
|
||||
suite.Assert().EqualError(suite.validator.Errors()[0], "A file path must be provided with key 'path'")
|
||||
suite.Assert().EqualError(suite.validator.Errors()[0], "storage: local: 'path' configuration option must be provided")
|
||||
|
||||
suite.validator.Clear()
|
||||
suite.configuration.Local.Path = "/myapth"
|
||||
|
||||
ValidateStorage(suite.configuration, suite.validator)
|
||||
|
||||
suite.Assert().False(suite.validator.HasWarnings())
|
||||
suite.Assert().False(suite.validator.HasErrors())
|
||||
suite.Require().Len(suite.validator.Warnings(), 0)
|
||||
suite.Require().Len(suite.validator.Errors(), 0)
|
||||
}
|
||||
|
||||
func (suite *StorageSuite) TestShouldValidateSQLUsernamePasswordAndDatabaseAreProvided() {
|
||||
func (suite *StorageSuite) TestShouldValidateMySQLHostUsernamePasswordAndDatabaseAreProvided() {
|
||||
suite.configuration.MySQL = &schema.MySQLStorageConfiguration{}
|
||||
ValidateStorage(suite.configuration, suite.validator)
|
||||
|
||||
suite.Require().Len(suite.validator.Errors(), 2)
|
||||
suite.Assert().EqualError(suite.validator.Errors()[0], "the SQL username and password must be provided")
|
||||
suite.Assert().EqualError(suite.validator.Errors()[1], "the SQL database must be provided")
|
||||
suite.Require().Len(suite.validator.Errors(), 3)
|
||||
suite.Assert().EqualError(suite.validator.Errors()[0], "storage: mysql: 'host' configuration option must be provided")
|
||||
suite.Assert().EqualError(suite.validator.Errors()[1], "storage: mysql: 'username' and 'password' configuration options must be provided")
|
||||
suite.Assert().EqualError(suite.validator.Errors()[2], "storage: mysql: 'database' configuration option must be provided")
|
||||
|
||||
suite.validator.Clear()
|
||||
suite.configuration.MySQL = &schema.MySQLStorageConfiguration{
|
||||
SQLStorageConfiguration: schema.SQLStorageConfiguration{
|
||||
Host: "localhost",
|
||||
Username: "myuser",
|
||||
Password: "pass",
|
||||
Database: "database",
|
||||
|
@ -69,13 +75,39 @@ func (suite *StorageSuite) TestShouldValidateSQLUsernamePasswordAndDatabaseArePr
|
|||
}
|
||||
ValidateStorage(suite.configuration, suite.validator)
|
||||
|
||||
suite.Assert().False(suite.validator.HasWarnings())
|
||||
suite.Assert().False(suite.validator.HasErrors())
|
||||
suite.Require().Len(suite.validator.Warnings(), 0)
|
||||
suite.Require().Len(suite.validator.Errors(), 0)
|
||||
}
|
||||
|
||||
func (suite *StorageSuite) TestShouldValidatePostgreSQLHostUsernamePasswordAndDatabaseAreProvided() {
|
||||
suite.configuration.PostgreSQL = &schema.PostgreSQLStorageConfiguration{}
|
||||
suite.configuration.MySQL = nil
|
||||
ValidateStorage(suite.configuration, suite.validator)
|
||||
|
||||
suite.Require().Len(suite.validator.Errors(), 3)
|
||||
suite.Assert().EqualError(suite.validator.Errors()[0], "storage: postgres: 'host' configuration option must be provided")
|
||||
suite.Assert().EqualError(suite.validator.Errors()[1], "storage: postgres: 'username' and 'password' configuration options must be provided")
|
||||
suite.Assert().EqualError(suite.validator.Errors()[2], "storage: postgres: 'database' configuration option must be provided")
|
||||
|
||||
suite.validator.Clear()
|
||||
suite.configuration.PostgreSQL = &schema.PostgreSQLStorageConfiguration{
|
||||
SQLStorageConfiguration: schema.SQLStorageConfiguration{
|
||||
Host: "postgre",
|
||||
Username: "myuser",
|
||||
Password: "pass",
|
||||
Database: "database",
|
||||
},
|
||||
}
|
||||
ValidateStorage(suite.configuration, suite.validator)
|
||||
|
||||
suite.Assert().Len(suite.validator.Warnings(), 0)
|
||||
suite.Assert().Len(suite.validator.Errors(), 0)
|
||||
}
|
||||
|
||||
func (suite *StorageSuite) TestShouldValidatePostgresSSLModeIsDisableByDefault() {
|
||||
suite.configuration.PostgreSQL = &schema.PostgreSQLStorageConfiguration{
|
||||
SQLStorageConfiguration: schema.SQLStorageConfiguration{
|
||||
Host: "db1",
|
||||
Username: "myuser",
|
||||
Password: "pass",
|
||||
Database: "database",
|
||||
|
@ -84,47 +116,76 @@ func (suite *StorageSuite) TestShouldValidatePostgresSSLModeIsDisableByDefault()
|
|||
|
||||
ValidateStorage(suite.configuration, suite.validator)
|
||||
|
||||
suite.Assert().False(suite.validator.HasWarnings())
|
||||
suite.Assert().False(suite.validator.HasErrors())
|
||||
suite.Assert().Len(suite.validator.Warnings(), 0)
|
||||
suite.Assert().Len(suite.validator.Errors(), 0)
|
||||
|
||||
suite.Assert().Equal("disable", suite.configuration.PostgreSQL.SSLMode)
|
||||
suite.Assert().Equal("disable", suite.configuration.PostgreSQL.SSL.Mode)
|
||||
}
|
||||
|
||||
func (suite *StorageSuite) TestShouldValidatePostgresSSLModeMustBeValid() {
|
||||
suite.configuration.PostgreSQL = &schema.PostgreSQLStorageConfiguration{
|
||||
SQLStorageConfiguration: schema.SQLStorageConfiguration{
|
||||
Host: "db2",
|
||||
Username: "myuser",
|
||||
Password: "pass",
|
||||
Database: "database",
|
||||
},
|
||||
SSLMode: "unknown",
|
||||
SSL: schema.PostgreSQLSSLStorageConfiguration{
|
||||
Mode: "unknown",
|
||||
},
|
||||
}
|
||||
|
||||
ValidateStorage(suite.configuration, suite.validator)
|
||||
|
||||
suite.Assert().False(suite.validator.HasWarnings())
|
||||
suite.Assert().Len(suite.validator.Warnings(), 0)
|
||||
suite.Require().Len(suite.validator.Errors(), 1)
|
||||
suite.Assert().EqualError(suite.validator.Errors()[0], "SSL mode must be 'disable', 'require', 'verify-ca', or 'verify-full'")
|
||||
suite.Assert().EqualError(suite.validator.Errors()[0], "storage: postgres: ssl: 'mode' configuration option 'unknown' is invalid: must be one of 'disable', 'require', 'verify-ca', 'verify-full'")
|
||||
}
|
||||
|
||||
// Deprecated. TODO: Remove in v4.36.0.
|
||||
func (suite *StorageSuite) TestShouldValidatePostgresSSLModeMustBeMappedForDeprecations() {
|
||||
suite.configuration.PostgreSQL = &schema.PostgreSQLStorageConfiguration{
|
||||
SQLStorageConfiguration: schema.SQLStorageConfiguration{
|
||||
Host: "pg",
|
||||
Username: "myuser",
|
||||
Password: "pass",
|
||||
Database: "database",
|
||||
},
|
||||
SSLMode: "require",
|
||||
}
|
||||
|
||||
ValidateStorage(suite.configuration, suite.validator)
|
||||
|
||||
suite.Assert().Len(suite.validator.Warnings(), 0)
|
||||
suite.Assert().Len(suite.validator.Errors(), 0)
|
||||
|
||||
suite.Assert().Equal(suite.configuration.PostgreSQL.SSL.Mode, "require")
|
||||
}
|
||||
|
||||
func (suite *StorageSuite) TestShouldRaiseErrorOnNoEncryptionKey() {
|
||||
suite.configuration.EncryptionKey = ""
|
||||
suite.configuration.Local = &schema.LocalStorageConfiguration{
|
||||
Path: "/this/is/a/path",
|
||||
}
|
||||
|
||||
ValidateStorage(suite.configuration, suite.validator)
|
||||
|
||||
suite.Assert().False(suite.validator.HasWarnings())
|
||||
suite.Require().Len(suite.validator.Warnings(), 0)
|
||||
suite.Require().Len(suite.validator.Errors(), 1)
|
||||
suite.Assert().EqualError(suite.validator.Errors()[0], "the configuration option storage.encryption_key must be provided")
|
||||
suite.Assert().EqualError(suite.validator.Errors()[0], "storage: 'encryption_key' configuration option must be provided")
|
||||
}
|
||||
|
||||
func (suite *StorageSuite) TestShouldRaiseErrorOnShortEncryptionKey() {
|
||||
suite.configuration.EncryptionKey = "abc"
|
||||
suite.configuration.Local = &schema.LocalStorageConfiguration{
|
||||
Path: "/this/is/a/path",
|
||||
}
|
||||
|
||||
ValidateStorage(suite.configuration, suite.validator)
|
||||
|
||||
suite.Assert().False(suite.validator.HasWarnings())
|
||||
suite.Require().Len(suite.validator.Warnings(), 0)
|
||||
suite.Require().Len(suite.validator.Errors(), 1)
|
||||
suite.Assert().EqualError(suite.validator.Errors()[0], "the configuration option storage.encryption_key must be 20 characters or longer")
|
||||
suite.Assert().EqualError(suite.validator.Errors()[0], "storage: 'encryption_key' configuration option must be 20 characters or longer")
|
||||
}
|
||||
|
||||
func TestShouldRunStorageSuite(t *testing.T) {
|
||||
|
|
|
@ -41,10 +41,7 @@ func dataSourceNameMySQL(config schema.MySQLStorageConfiguration) (dataSourceNam
|
|||
address += fmt.Sprintf(":%d", config.Port)
|
||||
}
|
||||
|
||||
dataSourceName += fmt.Sprintf("tcp(%s)", address)
|
||||
if config.Database != "" {
|
||||
dataSourceName += fmt.Sprintf("/%s", config.Database)
|
||||
}
|
||||
dataSourceName += fmt.Sprintf("tcp(%s)/%s", address, config.Database)
|
||||
|
||||
dataSourceName += "?"
|
||||
dataSourceName += fmt.Sprintf("timeout=%ds&multiStatements=true&parseTime=true", int32(config.Timeout/time.Second))
|
||||
|
|
|
@ -56,20 +56,34 @@ func NewPostgreSQLProvider(config *schema.Configuration) (provider *PostgreSQLPr
|
|||
|
||||
func dataSourceNamePostgreSQL(config schema.PostgreSQLStorageConfiguration) (dataSourceName string) {
|
||||
args := []string{
|
||||
fmt.Sprintf("host=%s", config.Host),
|
||||
fmt.Sprintf("user='%s'", config.Username),
|
||||
fmt.Sprintf("password='%s'", config.Password),
|
||||
}
|
||||
|
||||
if config.Host != "" {
|
||||
args = append(args, fmt.Sprintf("host=%s", config.Host))
|
||||
fmt.Sprintf("dbname=%s", config.Database),
|
||||
}
|
||||
|
||||
if config.Port > 0 {
|
||||
args = append(args, fmt.Sprintf("port=%d", config.Port))
|
||||
}
|
||||
|
||||
if config.Database != "" {
|
||||
args = append(args, fmt.Sprintf("dbname=%s", config.Database))
|
||||
if config.Schema != "" {
|
||||
args = append(args, fmt.Sprintf("search_path=%s", config.Schema))
|
||||
}
|
||||
|
||||
if config.SSL.Mode != "" {
|
||||
args = append(args, fmt.Sprintf("sslmode=%s", config.SSL.Mode))
|
||||
}
|
||||
|
||||
if config.SSL.RootCertificate != "" {
|
||||
args = append(args, fmt.Sprintf("sslrootcert=%s", config.SSL.RootCertificate))
|
||||
}
|
||||
|
||||
if config.SSL.Certificate != "" {
|
||||
args = append(args, fmt.Sprintf("sslcert=%s", config.SSL.Certificate))
|
||||
}
|
||||
|
||||
if config.SSL.Key != "" {
|
||||
args = append(args, fmt.Sprintf("sslkey=%s", config.SSL.Key))
|
||||
}
|
||||
|
||||
args = append(args, fmt.Sprintf("connect_timeout=%d", int32(config.Timeout/time.Second)))
|
||||
|
|
|
@ -168,12 +168,12 @@ 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: A storage configuration must be provided. It could be 'local', 'mysql' or 'postgres', the configuration option storage.encryption_key must be provided\n")
|
||||
s.Assert().Contains(output, "Error: storage: configuration for a 'local', 'mysql' or 'postgres' database must be provided, storage: 'encryption_key' configuration option must be provided\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: A storage configuration must be provided. It could be 'local', 'mysql' or 'postgres', the configuration option storage.encryption_key must be provided\n")
|
||||
s.Assert().Contains(output, "Error: storage: configuration for a 'local', 'mysql' or 'postgres' database must be provided, storage: 'encryption_key' configuration option must be provided\n")
|
||||
}
|
||||
|
||||
func (s *CLISuite) TestStorage00ShouldShowCorrectPreInitInformation() {
|
||||
|
|
Loading…
Reference in New Issue