mirror of
https://github.com/0rangebananaspy/authelia.git
synced 2024-09-14 22:47:21 +07:00
8f05846e21
This implements Webauthn. Old devices can be used to authenticate via the appid compatibility layer which should be automatic. New devices will be registered via Webauthn, and devices which do not support FIDO2 will no longer be able to be registered. At this time it does not fully support multiple devices (backend does, frontend doesn't allow registration of additional devices). Does not support passwordless.
436 lines
12 KiB
Go
436 lines
12 KiB
Go
package validator
|
|
|
|
import (
|
|
"fmt"
|
|
"testing"
|
|
|
|
"github.com/stretchr/testify/assert"
|
|
"github.com/stretchr/testify/require"
|
|
|
|
"github.com/authelia/authelia/v4/internal/configuration/schema"
|
|
)
|
|
|
|
func newDefaultSessionConfig() schema.SessionConfiguration {
|
|
config := schema.SessionConfiguration{}
|
|
config.Secret = testJWTSecret
|
|
config.Domain = "example.com"
|
|
|
|
return config
|
|
}
|
|
|
|
func TestShouldSetDefaultSessionValues(t *testing.T) {
|
|
validator := schema.NewStructValidator()
|
|
config := newDefaultSessionConfig()
|
|
|
|
ValidateSession(&config, validator)
|
|
|
|
assert.False(t, validator.HasWarnings())
|
|
assert.False(t, validator.HasErrors())
|
|
assert.Equal(t, schema.DefaultSessionConfiguration.Name, config.Name)
|
|
assert.Equal(t, schema.DefaultSessionConfiguration.Inactivity, config.Inactivity)
|
|
assert.Equal(t, schema.DefaultSessionConfiguration.Expiration, config.Expiration)
|
|
assert.Equal(t, schema.DefaultSessionConfiguration.RememberMeDuration, config.RememberMeDuration)
|
|
assert.Equal(t, schema.DefaultSessionConfiguration.SameSite, config.SameSite)
|
|
}
|
|
|
|
func TestShouldSetDefaultSessionValuesWhenNegative(t *testing.T) {
|
|
validator := schema.NewStructValidator()
|
|
config := newDefaultSessionConfig()
|
|
|
|
config.Expiration, config.Inactivity, config.RememberMeDuration = -1, -1, -1
|
|
|
|
ValidateSession(&config, validator)
|
|
|
|
assert.False(t, validator.HasWarnings())
|
|
assert.False(t, validator.HasErrors())
|
|
assert.Equal(t, schema.DefaultSessionConfiguration.Inactivity, config.Inactivity)
|
|
assert.Equal(t, schema.DefaultSessionConfiguration.Expiration, config.Expiration)
|
|
assert.Equal(t, schema.DefaultSessionConfiguration.RememberMeDuration, config.RememberMeDuration)
|
|
}
|
|
|
|
func TestShouldHandleRedisConfigSuccessfully(t *testing.T) {
|
|
validator := schema.NewStructValidator()
|
|
config := newDefaultSessionConfig()
|
|
|
|
ValidateSession(&config, validator)
|
|
|
|
assert.Len(t, validator.Errors(), 0)
|
|
validator.Clear()
|
|
|
|
// Set redis config because password must be set only when redis is used.
|
|
config.Redis = &schema.RedisSessionConfiguration{
|
|
Host: "redis.localhost",
|
|
Port: 6379,
|
|
Password: "password",
|
|
}
|
|
|
|
ValidateSession(&config, validator)
|
|
|
|
assert.False(t, validator.HasWarnings())
|
|
assert.False(t, validator.HasErrors())
|
|
|
|
assert.Equal(t, 8, config.Redis.MaximumActiveConnections)
|
|
}
|
|
|
|
func TestShouldRaiseErrorWithInvalidRedisPortLow(t *testing.T) {
|
|
validator := schema.NewStructValidator()
|
|
config := newDefaultSessionConfig()
|
|
|
|
config.Redis = &schema.RedisSessionConfiguration{
|
|
Host: "authelia-port-1",
|
|
Port: -1,
|
|
}
|
|
|
|
ValidateSession(&config, validator)
|
|
|
|
assert.False(t, validator.HasWarnings())
|
|
require.Len(t, validator.Errors(), 1)
|
|
|
|
assert.EqualError(t, validator.Errors()[0], fmt.Sprintf(errFmtSessionRedisPortRange, -1))
|
|
}
|
|
|
|
func TestShouldRaiseErrorWithInvalidRedisPortHigh(t *testing.T) {
|
|
validator := schema.NewStructValidator()
|
|
config := newDefaultSessionConfig()
|
|
|
|
config.Redis = &schema.RedisSessionConfiguration{
|
|
Host: "authelia-port-1",
|
|
Port: 65536,
|
|
}
|
|
|
|
ValidateSession(&config, validator)
|
|
|
|
assert.False(t, validator.HasWarnings())
|
|
require.Len(t, validator.Errors(), 1)
|
|
|
|
assert.EqualError(t, validator.Errors()[0], fmt.Sprintf(errFmtSessionRedisPortRange, 65536))
|
|
}
|
|
|
|
func TestShouldRaiseErrorWhenRedisIsUsedAndSecretNotSet(t *testing.T) {
|
|
validator := schema.NewStructValidator()
|
|
config := newDefaultSessionConfig()
|
|
config.Secret = ""
|
|
|
|
ValidateSession(&config, validator)
|
|
|
|
assert.Len(t, validator.Errors(), 0)
|
|
validator.Clear()
|
|
|
|
// Set redis config because password must be set only when redis is used.
|
|
config.Redis = &schema.RedisSessionConfiguration{
|
|
Host: "redis.localhost",
|
|
Port: 6379,
|
|
}
|
|
|
|
ValidateSession(&config, validator)
|
|
|
|
assert.False(t, validator.HasWarnings())
|
|
assert.Len(t, validator.Errors(), 1)
|
|
assert.EqualError(t, validator.Errors()[0], fmt.Sprintf(errFmtSessionSecretRequired, "redis"))
|
|
}
|
|
|
|
func TestShouldRaiseErrorWhenRedisHasHostnameButNoPort(t *testing.T) {
|
|
validator := schema.NewStructValidator()
|
|
config := newDefaultSessionConfig()
|
|
|
|
ValidateSession(&config, validator)
|
|
|
|
assert.Len(t, validator.Errors(), 0)
|
|
validator.Clear()
|
|
|
|
// Set redis config because password must be set only when redis is used.
|
|
config.Redis = &schema.RedisSessionConfiguration{
|
|
Host: "redis.localhost",
|
|
Port: 0,
|
|
}
|
|
|
|
ValidateSession(&config, validator)
|
|
|
|
assert.False(t, validator.HasWarnings())
|
|
assert.Len(t, validator.Errors(), 1)
|
|
assert.EqualError(t, validator.Errors()[0], "A redis port different than 0 must be provided")
|
|
}
|
|
|
|
func TestShouldRaiseOneErrorWhenRedisHighAvailabilityHasNodesWithNoHost(t *testing.T) {
|
|
validator := schema.NewStructValidator()
|
|
config := newDefaultSessionConfig()
|
|
|
|
config.Redis = &schema.RedisSessionConfiguration{
|
|
Host: "redis",
|
|
Port: 6379,
|
|
HighAvailability: &schema.RedisHighAvailabilityConfiguration{
|
|
SentinelName: "authelia-sentinel",
|
|
SentinelPassword: "abc123",
|
|
Nodes: []schema.RedisNode{
|
|
{
|
|
Port: 26379,
|
|
},
|
|
{
|
|
Port: 26379,
|
|
},
|
|
},
|
|
},
|
|
}
|
|
|
|
ValidateSession(&config, validator)
|
|
|
|
errors := validator.Errors()
|
|
|
|
assert.False(t, validator.HasWarnings())
|
|
require.Len(t, errors, 1)
|
|
|
|
assert.EqualError(t, errors[0], "session: redis: high_availability: option 'nodes': option 'host' is required for each node but one or more nodes are missing this")
|
|
}
|
|
|
|
func TestShouldRaiseOneErrorWhenRedisHighAvailabilityDoesNotHaveSentinelName(t *testing.T) {
|
|
validator := schema.NewStructValidator()
|
|
config := newDefaultSessionConfig()
|
|
|
|
config.Redis = &schema.RedisSessionConfiguration{
|
|
Host: "redis",
|
|
Port: 6379,
|
|
HighAvailability: &schema.RedisHighAvailabilityConfiguration{
|
|
SentinelPassword: "abc123",
|
|
},
|
|
}
|
|
|
|
ValidateSession(&config, validator)
|
|
|
|
errors := validator.Errors()
|
|
|
|
assert.False(t, validator.HasWarnings())
|
|
require.Len(t, errors, 1)
|
|
|
|
assert.EqualError(t, errors[0], "session: redis: high_availability: option 'sentinel_name' is required")
|
|
}
|
|
|
|
func TestShouldUpdateDefaultPortWhenRedisSentinelHasNodes(t *testing.T) {
|
|
validator := schema.NewStructValidator()
|
|
config := newDefaultSessionConfig()
|
|
|
|
config.Redis = &schema.RedisSessionConfiguration{
|
|
Host: "redis",
|
|
Port: 6379,
|
|
HighAvailability: &schema.RedisHighAvailabilityConfiguration{
|
|
SentinelName: "authelia-sentinel",
|
|
SentinelPassword: "abc123",
|
|
Nodes: []schema.RedisNode{
|
|
{
|
|
Host: "node-1",
|
|
Port: 333,
|
|
},
|
|
{
|
|
Host: "node-2",
|
|
},
|
|
{
|
|
Host: "node-3",
|
|
},
|
|
},
|
|
},
|
|
}
|
|
|
|
ValidateSession(&config, validator)
|
|
|
|
assert.False(t, validator.HasWarnings())
|
|
assert.False(t, validator.HasErrors())
|
|
|
|
assert.Equal(t, 333, config.Redis.HighAvailability.Nodes[0].Port)
|
|
assert.Equal(t, 26379, config.Redis.HighAvailability.Nodes[1].Port)
|
|
assert.Equal(t, 26379, config.Redis.HighAvailability.Nodes[2].Port)
|
|
}
|
|
|
|
func TestShouldRaiseErrorsWhenRedisSentinelOptionsIncorrectlyConfigured(t *testing.T) {
|
|
validator := schema.NewStructValidator()
|
|
config := newDefaultSessionConfig()
|
|
|
|
config.Secret = ""
|
|
config.Redis = &schema.RedisSessionConfiguration{
|
|
Port: 65536,
|
|
HighAvailability: &schema.RedisHighAvailabilityConfiguration{
|
|
SentinelName: "sentinel",
|
|
SentinelPassword: "abc123",
|
|
Nodes: []schema.RedisNode{
|
|
{
|
|
Host: "node1",
|
|
Port: 26379,
|
|
},
|
|
},
|
|
RouteByLatency: true,
|
|
RouteRandomly: true,
|
|
},
|
|
}
|
|
|
|
ValidateSession(&config, validator)
|
|
|
|
errors := validator.Errors()
|
|
|
|
assert.False(t, validator.HasWarnings())
|
|
require.Len(t, errors, 2)
|
|
|
|
assert.EqualError(t, errors[0], fmt.Sprintf(errFmtSessionRedisPortRange, 65536))
|
|
assert.EqualError(t, errors[1], fmt.Sprintf(errFmtSessionSecretRequired, "redis"))
|
|
|
|
validator.Clear()
|
|
|
|
config.Redis.Port = -1
|
|
|
|
ValidateSession(&config, validator)
|
|
|
|
errors = validator.Errors()
|
|
|
|
assert.False(t, validator.HasWarnings())
|
|
require.Len(t, errors, 2)
|
|
|
|
assert.EqualError(t, errors[0], fmt.Sprintf(errFmtSessionRedisPortRange, -1))
|
|
assert.EqualError(t, errors[1], fmt.Sprintf(errFmtSessionSecretRequired, "redis"))
|
|
}
|
|
|
|
func TestShouldNotRaiseErrorsAndSetDefaultPortWhenRedisSentinelPortBlank(t *testing.T) {
|
|
validator := schema.NewStructValidator()
|
|
config := newDefaultSessionConfig()
|
|
|
|
config.Redis = &schema.RedisSessionConfiguration{
|
|
Host: "mysentinelHost",
|
|
Port: 0,
|
|
HighAvailability: &schema.RedisHighAvailabilityConfiguration{
|
|
SentinelName: "sentinel",
|
|
SentinelPassword: "abc123",
|
|
Nodes: []schema.RedisNode{
|
|
{
|
|
Host: "node1",
|
|
Port: 26379,
|
|
},
|
|
},
|
|
RouteByLatency: true,
|
|
RouteRandomly: true,
|
|
},
|
|
}
|
|
|
|
ValidateSession(&config, validator)
|
|
|
|
assert.False(t, validator.HasWarnings())
|
|
assert.False(t, validator.HasErrors())
|
|
|
|
assert.Equal(t, 26379, config.Redis.Port)
|
|
}
|
|
|
|
func TestShouldRaiseErrorWhenRedisHostAndHighAvailabilityNodesEmpty(t *testing.T) {
|
|
validator := schema.NewStructValidator()
|
|
config := newDefaultSessionConfig()
|
|
|
|
config.Redis = &schema.RedisSessionConfiguration{
|
|
Port: 26379,
|
|
HighAvailability: &schema.RedisHighAvailabilityConfiguration{
|
|
SentinelName: "sentinel",
|
|
SentinelPassword: "abc123",
|
|
RouteByLatency: true,
|
|
RouteRandomly: true,
|
|
},
|
|
}
|
|
|
|
ValidateSession(&config, validator)
|
|
|
|
assert.False(t, validator.HasWarnings())
|
|
require.Len(t, validator.Errors(), 1)
|
|
|
|
assert.EqualError(t, validator.Errors()[0], errFmtSessionRedisHostOrNodesRequired)
|
|
}
|
|
|
|
func TestShouldRaiseErrorsWhenRedisHostNotSet(t *testing.T) {
|
|
validator := schema.NewStructValidator()
|
|
config := newDefaultSessionConfig()
|
|
|
|
config.Redis = &schema.RedisSessionConfiguration{
|
|
Port: 6379,
|
|
}
|
|
|
|
ValidateSession(&config, validator)
|
|
|
|
errors := validator.Errors()
|
|
|
|
assert.False(t, validator.HasWarnings())
|
|
require.Len(t, errors, 1)
|
|
|
|
assert.EqualError(t, errors[0], errFmtSessionRedisHostRequired)
|
|
}
|
|
|
|
func TestShouldRaiseErrorWhenDomainNotSet(t *testing.T) {
|
|
validator := schema.NewStructValidator()
|
|
config := newDefaultSessionConfig()
|
|
config.Domain = ""
|
|
|
|
ValidateSession(&config, validator)
|
|
|
|
assert.False(t, validator.HasWarnings())
|
|
assert.Len(t, validator.Errors(), 1)
|
|
assert.EqualError(t, validator.Errors()[0], "session: option 'domain' is required")
|
|
}
|
|
|
|
func TestShouldRaiseErrorWhenDomainIsWildcard(t *testing.T) {
|
|
validator := schema.NewStructValidator()
|
|
config := newDefaultSessionConfig()
|
|
config.Domain = "*.example.com"
|
|
|
|
ValidateSession(&config, validator)
|
|
|
|
assert.False(t, validator.HasWarnings())
|
|
assert.Len(t, validator.Errors(), 1)
|
|
assert.EqualError(t, validator.Errors()[0], "session: option 'domain' must be the domain you wish to protect not a wildcard domain but it is configured as '*.example.com'")
|
|
}
|
|
|
|
func TestShouldRaiseErrorWhenSameSiteSetIncorrectly(t *testing.T) {
|
|
validator := schema.NewStructValidator()
|
|
config := newDefaultSessionConfig()
|
|
config.SameSite = "NOne"
|
|
|
|
ValidateSession(&config, validator)
|
|
|
|
assert.False(t, validator.HasWarnings())
|
|
assert.Len(t, validator.Errors(), 1)
|
|
assert.EqualError(t, validator.Errors()[0], "session: option 'same_site' must be one of 'none', 'lax', 'strict' but is configured as 'NOne'")
|
|
}
|
|
|
|
func TestShouldNotRaiseErrorWhenSameSiteSetCorrectly(t *testing.T) {
|
|
validator := schema.NewStructValidator()
|
|
config := newDefaultSessionConfig()
|
|
|
|
validOptions := []string{"none", "lax", "strict"}
|
|
|
|
for _, opt := range validOptions {
|
|
config.SameSite = opt
|
|
|
|
ValidateSession(&config, validator)
|
|
|
|
assert.False(t, validator.HasWarnings())
|
|
assert.Len(t, validator.Errors(), 0)
|
|
}
|
|
}
|
|
|
|
func TestShouldSetDefaultWhenNegativeInactivityAndExpirationSet(t *testing.T) {
|
|
validator := schema.NewStructValidator()
|
|
config := newDefaultSessionConfig()
|
|
config.Inactivity = -1
|
|
config.Expiration = -1
|
|
config.RememberMeDuration = -1
|
|
|
|
ValidateSession(&config, validator)
|
|
|
|
assert.Len(t, validator.Warnings(), 0)
|
|
assert.Len(t, validator.Errors(), 0)
|
|
|
|
assert.Equal(t, schema.DefaultSessionConfiguration.Inactivity, config.Inactivity)
|
|
assert.Equal(t, schema.DefaultSessionConfiguration.Expiration, config.Expiration)
|
|
assert.Equal(t, schema.DefaultSessionConfiguration.RememberMeDuration, config.RememberMeDuration)
|
|
}
|
|
|
|
func TestShouldSetDefaultRememberMeDuration(t *testing.T) {
|
|
validator := schema.NewStructValidator()
|
|
config := newDefaultSessionConfig()
|
|
|
|
ValidateSession(&config, validator)
|
|
|
|
assert.False(t, validator.HasWarnings())
|
|
assert.False(t, validator.HasErrors())
|
|
assert.Equal(t, config.RememberMeDuration, schema.DefaultSessionConfiguration.RememberMeDuration)
|
|
}
|