Skip to content

Commit a6b5405

Browse files
Merge pull request #1601 from krishnadurai/feature/static_password_env
Option to add staticPasswords from environment variables
2 parents 8894eed + 3217908 commit a6b5405

File tree

4 files changed

+175
-4
lines changed

4 files changed

+175
-4
lines changed

.github/workflows/ci.yml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -51,6 +51,7 @@ jobs:
5151
- name: Run tests
5252
run: make testall
5353
env:
54+
DEX_FOO_USER_PASSWORD: $2a$10$33EMT0cVYVlPy6WAMCLsceLYjWhuHpbz5yuZxu/GAFj03J9Lytjuy
5455
DEX_MYSQL_DATABASE: dex
5556
DEX_MYSQL_USER: root
5657
DEX_MYSQL_PASSWORD: root

cmd/dex/config.go

Lines changed: 8 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -85,10 +85,11 @@ type password storage.Password
8585

8686
func (p *password) UnmarshalJSON(b []byte) error {
8787
var data struct {
88-
Email string `json:"email"`
89-
Username string `json:"username"`
90-
UserID string `json:"userID"`
91-
Hash string `json:"hash"`
88+
Email string `json:"email"`
89+
Username string `json:"username"`
90+
UserID string `json:"userID"`
91+
Hash string `json:"hash"`
92+
HashFromEnv string `json:"hashFromEnv"`
9293
}
9394
if err := json.Unmarshal(b, &data); err != nil {
9495
return err
@@ -98,6 +99,9 @@ func (p *password) UnmarshalJSON(b []byte) error {
9899
Username: data.Username,
99100
UserID: data.UserID,
100101
})
102+
if len(data.Hash) == 0 && len(data.HashFromEnv) > 0 {
103+
data.Hash = os.Getenv(data.HashFromEnv)
104+
}
101105
if len(data.Hash) == 0 {
102106
return fmt.Errorf("no password hash provided")
103107
}

cmd/dex/config_test.go

Lines changed: 163 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
package main
22

33
import (
4+
"os"
45
"testing"
56

67
"github.com/ghodss/yaml"
@@ -15,6 +16,8 @@ import (
1516

1617
var _ = yaml.YAMLToJSON
1718

19+
const testHashStaticPasswordEnv = "DEX_FOO_USER_PASSWORD"
20+
1821
func TestValidConfiguration(t *testing.T) {
1922
configuration := Config{
2023
Issuer: "http://127.0.0.1:5556/dex",
@@ -212,3 +215,163 @@ logger:
212215
t.Errorf("got!=want: %s", diff)
213216
}
214217
}
218+
219+
func TestUnmarshalConfigWithEnv(t *testing.T) {
220+
staticPasswordEnv := os.Getenv(testHashStaticPasswordEnv)
221+
if staticPasswordEnv == "" {
222+
t.Skipf("test environment variable %q not set, skipping", testHashStaticPasswordEnv)
223+
}
224+
rawConfig := []byte(`
225+
issuer: http://127.0.0.1:5556/dex
226+
storage:
227+
type: postgres
228+
config:
229+
host: 10.0.0.1
230+
port: 65432
231+
maxOpenConns: 5
232+
maxIdleConns: 3
233+
connMaxLifetime: 30
234+
connectionTimeout: 3
235+
web:
236+
http: 127.0.0.1:5556
237+
238+
frontend:
239+
dir: ./web
240+
extra:
241+
foo: bar
242+
243+
staticClients:
244+
- id: example-app
245+
redirectURIs:
246+
- 'http://127.0.0.1:5555/callback'
247+
name: 'Example App'
248+
secret: ZXhhbXBsZS1hcHAtc2VjcmV0
249+
250+
oauth2:
251+
alwaysShowLoginScreen: true
252+
253+
connectors:
254+
- type: mockCallback
255+
id: mock
256+
name: Example
257+
- type: oidc
258+
id: google
259+
name: Google
260+
config:
261+
issuer: https://accounts.google.com
262+
clientID: foo
263+
clientSecret: bar
264+
redirectURI: http://127.0.0.1:5556/dex/callback/google
265+
266+
enablePasswordDB: true
267+
staticPasswords:
268+
269+
# bcrypt hash of the string "password"
270+
hash: "$2a$10$33EMT0cVYVlPy6WAMCLsceLYjWhuHpbz5yuZxu/GAFj03J9Lytjuy"
271+
username: "admin"
272+
userID: "08a8684b-db88-4b73-90a9-3cd1661f5466"
273+
274+
hashFromEnv: "DEX_FOO_USER_PASSWORD"
275+
username: "foo"
276+
userID: "41331323-6f44-45e6-b3b9-2c4b60c02be5"
277+
278+
expiry:
279+
signingKeys: "7h"
280+
idTokens: "25h"
281+
authRequests: "25h"
282+
283+
logger:
284+
level: "debug"
285+
format: "json"
286+
`)
287+
288+
want := Config{
289+
Issuer: "http://127.0.0.1:5556/dex",
290+
Storage: Storage{
291+
Type: "postgres",
292+
Config: &sql.Postgres{
293+
NetworkDB: sql.NetworkDB{
294+
Host: "10.0.0.1",
295+
Port: 65432,
296+
MaxOpenConns: 5,
297+
MaxIdleConns: 3,
298+
ConnMaxLifetime: 30,
299+
ConnectionTimeout: 3,
300+
},
301+
},
302+
},
303+
Web: Web{
304+
HTTP: "127.0.0.1:5556",
305+
},
306+
Frontend: server.WebConfig{
307+
Dir: "./web",
308+
Extra: map[string]string{
309+
"foo": "bar",
310+
},
311+
},
312+
StaticClients: []storage.Client{
313+
{
314+
ID: "example-app",
315+
Secret: "ZXhhbXBsZS1hcHAtc2VjcmV0",
316+
Name: "Example App",
317+
RedirectURIs: []string{
318+
"http://127.0.0.1:5555/callback",
319+
},
320+
},
321+
},
322+
OAuth2: OAuth2{
323+
AlwaysShowLoginScreen: true,
324+
},
325+
StaticConnectors: []Connector{
326+
{
327+
Type: "mockCallback",
328+
ID: "mock",
329+
Name: "Example",
330+
Config: &mock.CallbackConfig{},
331+
},
332+
{
333+
Type: "oidc",
334+
ID: "google",
335+
Name: "Google",
336+
Config: &oidc.Config{
337+
Issuer: "https://accounts.google.com",
338+
ClientID: "foo",
339+
ClientSecret: "bar",
340+
RedirectURI: "http://127.0.0.1:5556/dex/callback/google",
341+
},
342+
},
343+
},
344+
EnablePasswordDB: true,
345+
StaticPasswords: []password{
346+
{
347+
348+
Hash: []byte("$2a$10$33EMT0cVYVlPy6WAMCLsceLYjWhuHpbz5yuZxu/GAFj03J9Lytjuy"),
349+
Username: "admin",
350+
UserID: "08a8684b-db88-4b73-90a9-3cd1661f5466",
351+
},
352+
{
353+
354+
Hash: []byte("$2a$10$33EMT0cVYVlPy6WAMCLsceLYjWhuHpbz5yuZxu/GAFj03J9Lytjuy"),
355+
Username: "foo",
356+
UserID: "41331323-6f44-45e6-b3b9-2c4b60c02be5",
357+
},
358+
},
359+
Expiry: Expiry{
360+
SigningKeys: "7h",
361+
IDTokens: "25h",
362+
AuthRequests: "25h",
363+
},
364+
Logger: Logger{
365+
Level: "debug",
366+
Format: "json",
367+
},
368+
}
369+
370+
var c Config
371+
if err := yaml.Unmarshal(rawConfig, &c); err != nil {
372+
t.Fatalf("failed to decode config: %v", err)
373+
}
374+
if diff := pretty.Compare(c, want); diff != "" {
375+
t.Errorf("got!=want: %s", diff)
376+
}
377+
}

storage/storage.go

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -292,6 +292,9 @@ type Password struct {
292292
// Bcrypt encoded hash of the password. This package enforces a min cost value of 10
293293
Hash []byte `json:"hash"`
294294

295+
// Bcrypt encoded hash of the password set in environment variable of this name.
296+
HashFromEnv string `json:"hashFromEnv"`
297+
295298
// Optional username to display. NOT used during login.
296299
Username string `json:"username"`
297300

0 commit comments

Comments
 (0)