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
 | 
					      - 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
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -85,10 +85,11 @@ type password storage.Password
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
func (p *password) UnmarshalJSON(b []byte) error {
 | 
					func (p *password) UnmarshalJSON(b []byte) error {
 | 
				
			||||||
	var data struct {
 | 
						var data struct {
 | 
				
			||||||
		Email    string `json:"email"`
 | 
							Email       string `json:"email"`
 | 
				
			||||||
		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")
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -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)
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -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"`
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 
 | 
				
			|||||||
		Reference in New Issue
	
	Block a user