Merge pull request #1601 from krishnadurai/feature/static_password_env
Option to add staticPasswords from environment variables
This commit is contained in:
		
							
								
								
									
										1
									
								
								.github/workflows/ci.yml
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										1
									
								
								.github/workflows/ci.yml
									
									
									
									
										vendored
									
									
								
							@@ -51,6 +51,7 @@ jobs:
 | 
			
		||||
      - name: Run tests
 | 
			
		||||
        run: make testall
 | 
			
		||||
        env:
 | 
			
		||||
          DEX_FOO_USER_PASSWORD: $2a$10$33EMT0cVYVlPy6WAMCLsceLYjWhuHpbz5yuZxu/GAFj03J9Lytjuy
 | 
			
		||||
          DEX_MYSQL_DATABASE: dex
 | 
			
		||||
          DEX_MYSQL_USER: root
 | 
			
		||||
          DEX_MYSQL_PASSWORD: root
 | 
			
		||||
 
 | 
			
		||||
@@ -89,6 +89,7 @@ func (p *password) UnmarshalJSON(b []byte) error {
 | 
			
		||||
		Username    string `json:"username"`
 | 
			
		||||
		UserID      string `json:"userID"`
 | 
			
		||||
		Hash        string `json:"hash"`
 | 
			
		||||
		HashFromEnv string `json:"hashFromEnv"`
 | 
			
		||||
	}
 | 
			
		||||
	if err := json.Unmarshal(b, &data); err != nil {
 | 
			
		||||
		return err
 | 
			
		||||
@@ -98,6 +99,9 @@ func (p *password) UnmarshalJSON(b []byte) error {
 | 
			
		||||
		Username: data.Username,
 | 
			
		||||
		UserID:   data.UserID,
 | 
			
		||||
	})
 | 
			
		||||
	if len(data.Hash) == 0 && len(data.HashFromEnv) > 0 {
 | 
			
		||||
		data.Hash = os.Getenv(data.HashFromEnv)
 | 
			
		||||
	}
 | 
			
		||||
	if len(data.Hash) == 0 {
 | 
			
		||||
		return fmt.Errorf("no password hash provided")
 | 
			
		||||
	}
 | 
			
		||||
 
 | 
			
		||||
@@ -1,6 +1,7 @@
 | 
			
		||||
package main
 | 
			
		||||
 | 
			
		||||
import (
 | 
			
		||||
	"os"
 | 
			
		||||
	"testing"
 | 
			
		||||
 | 
			
		||||
	"github.com/ghodss/yaml"
 | 
			
		||||
@@ -15,6 +16,8 @@ import (
 | 
			
		||||
 | 
			
		||||
var _ = yaml.YAMLToJSON
 | 
			
		||||
 | 
			
		||||
const testHashStaticPasswordEnv = "DEX_FOO_USER_PASSWORD"
 | 
			
		||||
 | 
			
		||||
func TestValidConfiguration(t *testing.T) {
 | 
			
		||||
	configuration := Config{
 | 
			
		||||
		Issuer: "http://127.0.0.1:5556/dex",
 | 
			
		||||
@@ -212,3 +215,163 @@ logger:
 | 
			
		||||
		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)
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
@@ -292,6 +292,9 @@ type Password struct {
 | 
			
		||||
	// Bcrypt encoded hash of the password. This package enforces a min cost value of 10
 | 
			
		||||
	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.
 | 
			
		||||
	Username string `json:"username"`
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
		Reference in New Issue
	
	Block a user