forked from mystiq/dex
4cb5577e11
Signed-off-by: Martin Heide <martin.heide@faro.com>
425 lines
10 KiB
Go
425 lines
10 KiB
Go
package main
|
|
|
|
import (
|
|
"os"
|
|
"testing"
|
|
|
|
"github.com/ghodss/yaml"
|
|
"github.com/kylelemons/godebug/pretty"
|
|
|
|
"github.com/dexidp/dex/connector/mock"
|
|
"github.com/dexidp/dex/connector/oidc"
|
|
"github.com/dexidp/dex/server"
|
|
"github.com/dexidp/dex/storage"
|
|
"github.com/dexidp/dex/storage/sql"
|
|
)
|
|
|
|
var _ = yaml.YAMLToJSON
|
|
|
|
func TestValidConfiguration(t *testing.T) {
|
|
configuration := Config{
|
|
Issuer: "http://127.0.0.1:5556/dex",
|
|
Storage: Storage{
|
|
Type: "sqlite3",
|
|
Config: &sql.SQLite3{
|
|
File: "examples/dex.db",
|
|
},
|
|
},
|
|
Web: Web{
|
|
HTTP: "127.0.0.1:5556",
|
|
},
|
|
StaticConnectors: []Connector{
|
|
{
|
|
Type: "mockCallback",
|
|
ID: "mock",
|
|
Name: "Example",
|
|
Config: &mock.CallbackConfig{},
|
|
},
|
|
},
|
|
}
|
|
if err := configuration.Validate(); err != nil {
|
|
t.Fatalf("this configuration should have been valid: %v", err)
|
|
}
|
|
}
|
|
|
|
func TestInvalidConfiguration(t *testing.T) {
|
|
configuration := Config{}
|
|
err := configuration.Validate()
|
|
if err == nil {
|
|
t.Fatal("this configuration should be invalid")
|
|
}
|
|
got := err.Error()
|
|
wanted := `invalid Config:
|
|
- no issuer specified in config file
|
|
- no storage supplied in config file
|
|
- must supply a HTTP/HTTPS address to listen on`
|
|
if got != wanted {
|
|
t.Fatalf("Expected error message to be %q, got %q", wanted, got)
|
|
}
|
|
}
|
|
|
|
func TestUnmarshalConfig(t *testing.T) {
|
|
rawConfig := []byte(`
|
|
issuer: http://127.0.0.1:5556/dex
|
|
storage:
|
|
type: postgres
|
|
config:
|
|
host: 10.0.0.1
|
|
port: 65432
|
|
maxOpenConns: 5
|
|
maxIdleConns: 3
|
|
connMaxLifetime: 30
|
|
connectionTimeout: 3
|
|
web:
|
|
http: 127.0.0.1:5556
|
|
|
|
frontend:
|
|
dir: ./web
|
|
extra:
|
|
foo: bar
|
|
|
|
staticClients:
|
|
- id: example-app
|
|
redirectURIs:
|
|
- 'http://127.0.0.1:5555/callback'
|
|
name: 'Example App'
|
|
secret: ZXhhbXBsZS1hcHAtc2VjcmV0
|
|
|
|
oauth2:
|
|
alwaysShowLoginScreen: true
|
|
|
|
connectors:
|
|
- type: mockCallback
|
|
id: mock
|
|
name: Example
|
|
- type: oidc
|
|
id: google
|
|
name: Google
|
|
config:
|
|
issuer: https://accounts.google.com
|
|
clientID: foo
|
|
clientSecret: bar
|
|
redirectURI: http://127.0.0.1:5556/dex/callback/google
|
|
|
|
enablePasswordDB: true
|
|
staticPasswords:
|
|
- email: "admin@example.com"
|
|
# bcrypt hash of the string "password"
|
|
hash: "$2a$10$33EMT0cVYVlPy6WAMCLsceLYjWhuHpbz5yuZxu/GAFj03J9Lytjuy"
|
|
username: "admin"
|
|
userID: "08a8684b-db88-4b73-90a9-3cd1661f5466"
|
|
- email: "foo@example.com"
|
|
# base64'd value of the same bcrypt hash above. We want to be able to parse both of these
|
|
hash: "JDJhJDEwJDMzRU1UMGNWWVZsUHk2V0FNQ0xzY2VMWWpXaHVIcGJ6NXl1Wnh1L0dBRmowM0o5THl0anV5"
|
|
username: "foo"
|
|
userID: "41331323-6f44-45e6-b3b9-2c4b60c02be5"
|
|
|
|
expiry:
|
|
signingKeys: "7h"
|
|
idTokens: "25h"
|
|
authRequests: "25h"
|
|
deviceRequests: "10m"
|
|
|
|
logger:
|
|
level: "debug"
|
|
format: "json"
|
|
`)
|
|
|
|
want := Config{
|
|
Issuer: "http://127.0.0.1:5556/dex",
|
|
Storage: Storage{
|
|
Type: "postgres",
|
|
Config: &sql.Postgres{
|
|
NetworkDB: sql.NetworkDB{
|
|
Host: "10.0.0.1",
|
|
Port: 65432,
|
|
MaxOpenConns: 5,
|
|
MaxIdleConns: 3,
|
|
ConnMaxLifetime: 30,
|
|
ConnectionTimeout: 3,
|
|
},
|
|
},
|
|
},
|
|
Web: Web{
|
|
HTTP: "127.0.0.1:5556",
|
|
},
|
|
Frontend: server.WebConfig{
|
|
Dir: "./web",
|
|
Extra: map[string]string{
|
|
"foo": "bar",
|
|
},
|
|
},
|
|
StaticClients: []storage.Client{
|
|
{
|
|
ID: "example-app",
|
|
Secret: "ZXhhbXBsZS1hcHAtc2VjcmV0",
|
|
Name: "Example App",
|
|
RedirectURIs: []string{
|
|
"http://127.0.0.1:5555/callback",
|
|
},
|
|
},
|
|
},
|
|
OAuth2: OAuth2{
|
|
AlwaysShowLoginScreen: true,
|
|
},
|
|
StaticConnectors: []Connector{
|
|
{
|
|
Type: "mockCallback",
|
|
ID: "mock",
|
|
Name: "Example",
|
|
Config: &mock.CallbackConfig{},
|
|
},
|
|
{
|
|
Type: "oidc",
|
|
ID: "google",
|
|
Name: "Google",
|
|
Config: &oidc.Config{
|
|
Issuer: "https://accounts.google.com",
|
|
ClientID: "foo",
|
|
ClientSecret: "bar",
|
|
RedirectURI: "http://127.0.0.1:5556/dex/callback/google",
|
|
},
|
|
},
|
|
},
|
|
EnablePasswordDB: true,
|
|
StaticPasswords: []password{
|
|
{
|
|
Email: "admin@example.com",
|
|
Hash: []byte("$2a$10$33EMT0cVYVlPy6WAMCLsceLYjWhuHpbz5yuZxu/GAFj03J9Lytjuy"),
|
|
Username: "admin",
|
|
UserID: "08a8684b-db88-4b73-90a9-3cd1661f5466",
|
|
},
|
|
{
|
|
Email: "foo@example.com",
|
|
Hash: []byte("$2a$10$33EMT0cVYVlPy6WAMCLsceLYjWhuHpbz5yuZxu/GAFj03J9Lytjuy"),
|
|
Username: "foo",
|
|
UserID: "41331323-6f44-45e6-b3b9-2c4b60c02be5",
|
|
},
|
|
},
|
|
Expiry: Expiry{
|
|
SigningKeys: "7h",
|
|
IDTokens: "25h",
|
|
AuthRequests: "25h",
|
|
DeviceRequests: "10m",
|
|
},
|
|
Logger: Logger{
|
|
Level: "debug",
|
|
Format: "json",
|
|
},
|
|
}
|
|
|
|
var c Config
|
|
if err := yaml.Unmarshal(rawConfig, &c); err != nil {
|
|
t.Fatalf("failed to decode config: %v", err)
|
|
}
|
|
if diff := pretty.Compare(c, want); diff != "" {
|
|
t.Errorf("got!=want: %s", diff)
|
|
}
|
|
}
|
|
|
|
func TestUnmarshalConfigWithEnvNoExpand(t *testing.T) {
|
|
// If the env variable DEX_EXPAND_ENV is set and has a "falsy" value, os.ExpandEnv is disabled.
|
|
// ParseBool: "It accepts 1, t, T, TRUE, true, True, 0, f, F, FALSE, false, False."
|
|
checkUnmarshalConfigWithEnv(t, "0", false)
|
|
checkUnmarshalConfigWithEnv(t, "f", false)
|
|
checkUnmarshalConfigWithEnv(t, "F", false)
|
|
checkUnmarshalConfigWithEnv(t, "FALSE", false)
|
|
checkUnmarshalConfigWithEnv(t, "false", false)
|
|
checkUnmarshalConfigWithEnv(t, "False", false)
|
|
os.Unsetenv("DEX_EXPAND_ENV")
|
|
}
|
|
|
|
func TestUnmarshalConfigWithEnvExpand(t *testing.T) {
|
|
// If the env variable DEX_EXPAND_ENV is unset or has a "truthy" or unknown value, os.ExpandEnv is enabled.
|
|
// ParseBool: "It accepts 1, t, T, TRUE, true, True, 0, f, F, FALSE, false, False."
|
|
checkUnmarshalConfigWithEnv(t, "1", true)
|
|
checkUnmarshalConfigWithEnv(t, "t", true)
|
|
checkUnmarshalConfigWithEnv(t, "T", true)
|
|
checkUnmarshalConfigWithEnv(t, "TRUE", true)
|
|
checkUnmarshalConfigWithEnv(t, "true", true)
|
|
checkUnmarshalConfigWithEnv(t, "True", true)
|
|
// Values that can't be parsed as bool:
|
|
checkUnmarshalConfigWithEnv(t, "UNSET", true)
|
|
checkUnmarshalConfigWithEnv(t, "", true)
|
|
checkUnmarshalConfigWithEnv(t, "whatever - true is default", true)
|
|
os.Unsetenv("DEX_EXPAND_ENV")
|
|
}
|
|
|
|
func checkUnmarshalConfigWithEnv(t *testing.T, dexExpandEnv string, wantExpandEnv bool) {
|
|
// For hashFromEnv:
|
|
os.Setenv("DEX_FOO_USER_PASSWORD", "$2a$10$33EMT0cVYVlPy6WAMCLsceLYjWhuHpbz5yuZxu/GAFj03J9Lytjuy")
|
|
// For os.ExpandEnv ($VAR -> value_of_VAR):
|
|
os.Setenv("DEX_FOO_POSTGRES_HOST", "10.0.0.1")
|
|
os.Setenv("DEX_FOO_OIDC_CLIENT_SECRET", "bar")
|
|
if dexExpandEnv != "UNSET" {
|
|
os.Setenv("DEX_EXPAND_ENV", dexExpandEnv)
|
|
} else {
|
|
os.Unsetenv("DEX_EXPAND_ENV")
|
|
}
|
|
|
|
rawConfig := []byte(`
|
|
issuer: http://127.0.0.1:5556/dex
|
|
storage:
|
|
type: postgres
|
|
config:
|
|
# Env variables are expanded in raw YAML source.
|
|
# Single quotes work fine, as long as the env variable doesn't contain any.
|
|
host: '$DEX_FOO_POSTGRES_HOST'
|
|
port: 65432
|
|
maxOpenConns: 5
|
|
maxIdleConns: 3
|
|
connMaxLifetime: 30
|
|
connectionTimeout: 3
|
|
web:
|
|
http: 127.0.0.1:5556
|
|
|
|
frontend:
|
|
dir: ./web
|
|
extra:
|
|
foo: bar
|
|
|
|
staticClients:
|
|
- id: example-app
|
|
redirectURIs:
|
|
- 'http://127.0.0.1:5555/callback'
|
|
name: 'Example App'
|
|
secret: ZXhhbXBsZS1hcHAtc2VjcmV0
|
|
|
|
oauth2:
|
|
alwaysShowLoginScreen: true
|
|
|
|
connectors:
|
|
- type: mockCallback
|
|
id: mock
|
|
name: Example
|
|
- type: oidc
|
|
id: google
|
|
name: Google
|
|
config:
|
|
issuer: https://accounts.google.com
|
|
clientID: foo
|
|
# Env variables are expanded in raw YAML source.
|
|
# Single quotes work fine, as long as the env variable doesn't contain any.
|
|
clientSecret: '$DEX_FOO_OIDC_CLIENT_SECRET'
|
|
redirectURI: http://127.0.0.1:5556/dex/callback/google
|
|
|
|
enablePasswordDB: true
|
|
staticPasswords:
|
|
- email: "admin@example.com"
|
|
# bcrypt hash of the string "password"
|
|
hash: "$2a$10$33EMT0cVYVlPy6WAMCLsceLYjWhuHpbz5yuZxu/GAFj03J9Lytjuy"
|
|
username: "admin"
|
|
userID: "08a8684b-db88-4b73-90a9-3cd1661f5466"
|
|
- email: "foo@example.com"
|
|
hashFromEnv: "DEX_FOO_USER_PASSWORD"
|
|
username: "foo"
|
|
userID: "41331323-6f44-45e6-b3b9-2c4b60c02be5"
|
|
|
|
expiry:
|
|
signingKeys: "7h"
|
|
idTokens: "25h"
|
|
authRequests: "25h"
|
|
|
|
logger:
|
|
level: "debug"
|
|
format: "json"
|
|
`)
|
|
|
|
// This is not a valid hostname. It's only used to check whether os.ExpandEnv was applied or not.
|
|
wantPostgresHost := "$DEX_FOO_POSTGRES_HOST"
|
|
wantOidcClientSecret := "$DEX_FOO_OIDC_CLIENT_SECRET"
|
|
if wantExpandEnv {
|
|
wantPostgresHost = "10.0.0.1"
|
|
wantOidcClientSecret = "bar"
|
|
}
|
|
|
|
want := Config{
|
|
Issuer: "http://127.0.0.1:5556/dex",
|
|
Storage: Storage{
|
|
Type: "postgres",
|
|
Config: &sql.Postgres{
|
|
NetworkDB: sql.NetworkDB{
|
|
Host: wantPostgresHost,
|
|
Port: 65432,
|
|
MaxOpenConns: 5,
|
|
MaxIdleConns: 3,
|
|
ConnMaxLifetime: 30,
|
|
ConnectionTimeout: 3,
|
|
},
|
|
},
|
|
},
|
|
Web: Web{
|
|
HTTP: "127.0.0.1:5556",
|
|
},
|
|
Frontend: server.WebConfig{
|
|
Dir: "./web",
|
|
Extra: map[string]string{
|
|
"foo": "bar",
|
|
},
|
|
},
|
|
StaticClients: []storage.Client{
|
|
{
|
|
ID: "example-app",
|
|
Secret: "ZXhhbXBsZS1hcHAtc2VjcmV0",
|
|
Name: "Example App",
|
|
RedirectURIs: []string{
|
|
"http://127.0.0.1:5555/callback",
|
|
},
|
|
},
|
|
},
|
|
OAuth2: OAuth2{
|
|
AlwaysShowLoginScreen: true,
|
|
},
|
|
StaticConnectors: []Connector{
|
|
{
|
|
Type: "mockCallback",
|
|
ID: "mock",
|
|
Name: "Example",
|
|
Config: &mock.CallbackConfig{},
|
|
},
|
|
{
|
|
Type: "oidc",
|
|
ID: "google",
|
|
Name: "Google",
|
|
Config: &oidc.Config{
|
|
Issuer: "https://accounts.google.com",
|
|
ClientID: "foo",
|
|
ClientSecret: wantOidcClientSecret,
|
|
RedirectURI: "http://127.0.0.1:5556/dex/callback/google",
|
|
},
|
|
},
|
|
},
|
|
EnablePasswordDB: true,
|
|
StaticPasswords: []password{
|
|
{
|
|
Email: "admin@example.com",
|
|
Hash: []byte("$2a$10$33EMT0cVYVlPy6WAMCLsceLYjWhuHpbz5yuZxu/GAFj03J9Lytjuy"),
|
|
Username: "admin",
|
|
UserID: "08a8684b-db88-4b73-90a9-3cd1661f5466",
|
|
},
|
|
{
|
|
Email: "foo@example.com",
|
|
Hash: []byte("$2a$10$33EMT0cVYVlPy6WAMCLsceLYjWhuHpbz5yuZxu/GAFj03J9Lytjuy"),
|
|
Username: "foo",
|
|
UserID: "41331323-6f44-45e6-b3b9-2c4b60c02be5",
|
|
},
|
|
},
|
|
Expiry: Expiry{
|
|
SigningKeys: "7h",
|
|
IDTokens: "25h",
|
|
AuthRequests: "25h",
|
|
},
|
|
Logger: Logger{
|
|
Level: "debug",
|
|
Format: "json",
|
|
},
|
|
}
|
|
|
|
var c Config
|
|
if err := yaml.Unmarshal(rawConfig, &c); err != nil {
|
|
t.Fatalf("failed to decode config: %v", err)
|
|
}
|
|
if diff := pretty.Compare(c, want); diff != "" {
|
|
t.Errorf("got!=want: %s", diff)
|
|
}
|
|
}
|