Merge pull request #1601 from krishnadurai/feature/static_password_env

Option to add staticPasswords from environment variables
This commit is contained in:
Márk Sági-Kazár 2020-01-30 16:22:17 +01:00 committed by GitHub
commit a6b5405c2e
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
4 changed files with 175 additions and 4 deletions

View file

@ -51,6 +51,7 @@ jobs:
- name: Run tests - name: Run tests
run: make testall run: make testall
env: env:
DEX_FOO_USER_PASSWORD: $2a$10$33EMT0cVYVlPy6WAMCLsceLYjWhuHpbz5yuZxu/GAFj03J9Lytjuy
DEX_MYSQL_DATABASE: dex DEX_MYSQL_DATABASE: dex
DEX_MYSQL_USER: root DEX_MYSQL_USER: root
DEX_MYSQL_PASSWORD: root DEX_MYSQL_PASSWORD: root

View file

@ -89,6 +89,7 @@ func (p *password) UnmarshalJSON(b []byte) error {
Username string `json:"username"` Username string `json:"username"`
UserID string `json:"userID"` UserID string `json:"userID"`
Hash string `json:"hash"` Hash string `json:"hash"`
HashFromEnv string `json:"hashFromEnv"`
} }
if err := json.Unmarshal(b, &data); err != nil { if err := json.Unmarshal(b, &data); err != nil {
return err return err
@ -98,6 +99,9 @@ func (p *password) UnmarshalJSON(b []byte) error {
Username: data.Username, Username: data.Username,
UserID: data.UserID, UserID: data.UserID,
}) })
if len(data.Hash) == 0 && len(data.HashFromEnv) > 0 {
data.Hash = os.Getenv(data.HashFromEnv)
}
if len(data.Hash) == 0 { if len(data.Hash) == 0 {
return fmt.Errorf("no password hash provided") return fmt.Errorf("no password hash provided")
} }

View file

@ -1,6 +1,7 @@
package main package main
import ( import (
"os"
"testing" "testing"
"github.com/ghodss/yaml" "github.com/ghodss/yaml"
@ -15,6 +16,8 @@ import (
var _ = yaml.YAMLToJSON var _ = yaml.YAMLToJSON
const testHashStaticPasswordEnv = "DEX_FOO_USER_PASSWORD"
func TestValidConfiguration(t *testing.T) { func TestValidConfiguration(t *testing.T) {
configuration := Config{ configuration := Config{
Issuer: "http://127.0.0.1:5556/dex", Issuer: "http://127.0.0.1:5556/dex",
@ -212,3 +215,163 @@ logger:
t.Errorf("got!=want: %s", diff) t.Errorf("got!=want: %s", diff)
} }
} }
func TestUnmarshalConfigWithEnv(t *testing.T) {
staticPasswordEnv := os.Getenv(testHashStaticPasswordEnv)
if staticPasswordEnv == "" {
t.Skipf("test environment variable %q not set, skipping", testHashStaticPasswordEnv)
}
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"
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"
`)
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",
},
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)
}
}

View file

@ -292,6 +292,9 @@ type Password struct {
// Bcrypt encoded hash of the password. This package enforces a min cost value of 10 // Bcrypt encoded hash of the password. This package enforces a min cost value of 10
Hash []byte `json:"hash"` Hash []byte `json:"hash"`
// Bcrypt encoded hash of the password set in environment variable of this name.
HashFromEnv string `json:"hashFromEnv"`
// Optional username to display. NOT used during login. // Optional username to display. NOT used during login.
Username string `json:"username"` Username string `json:"username"`