diff --git a/cmd/dex/config.go b/cmd/dex/config.go index 26c82311..6e93478b 100644 --- a/cmd/dex/config.go +++ b/cmd/dex/config.go @@ -1,9 +1,12 @@ package main import ( + "encoding/base64" "encoding/json" "fmt" + "golang.org/x/crypto/bcrypt" + "github.com/coreos/dex/connector" "github.com/coreos/dex/connector/github" "github.com/coreos/dex/connector/ldap" @@ -38,7 +41,47 @@ type Config struct { // StaticPasswords cause the server use this list of passwords rather than // querying the storage. Cannot be specified without enabling a passwords // database. - StaticPasswords []storage.Password `json:"staticPasswords"` + StaticPasswords []password `json:"staticPasswords"` +} + +type password storage.Password + +func (p *password) UnmarshalJSON(b []byte) error { + var data struct { + Email string `json:"email"` + Username string `json:"username"` + UserID string `json:"userID"` + Hash string `json:"hash"` + } + if err := json.Unmarshal(b, &data); err != nil { + return err + } + *p = password(storage.Password{ + Email: data.Email, + Username: data.Username, + UserID: data.UserID, + }) + if len(data.Hash) == 0 { + return fmt.Errorf("no password hash provided") + } + + // If this value is a valid bcrypt, use it. + _, bcryptErr := bcrypt.Cost([]byte(data.Hash)) + if bcryptErr == nil { + p.Hash = []byte(data.Hash) + return nil + } + + // For backwards compatibility try to base64 decode this value. + hashBytes, err := base64.StdEncoding.DecodeString(data.Hash) + if err != nil { + return fmt.Errorf("malformed bcrypt hash: %v", bcryptErr) + } + if _, err := bcrypt.Cost(hashBytes); err != nil { + return fmt.Errorf("malformed bcrypt hash: %v", err) + } + p.Hash = hashBytes + return nil } // OAuth2 describes enabled OAuth2 extensions. @@ -161,7 +204,7 @@ func (c *Connector) UnmarshalJSON(b []byte) error { } *c = Connector{ Type: conn.Type, - Name: conn.Type, + Name: conn.Name, ID: conn.ID, Config: connConfig, } diff --git a/cmd/dex/config_test.go b/cmd/dex/config_test.go index 5a70b487..e49d98ac 100644 --- a/cmd/dex/config_test.go +++ b/cmd/dex/config_test.go @@ -3,37 +3,124 @@ package main import ( "testing" + "github.com/coreos/dex/connector/mock" + "github.com/coreos/dex/connector/oidc" "github.com/coreos/dex/storage" + "github.com/coreos/dex/storage/sql" + "github.com/ghodss/yaml" "github.com/kylelemons/godebug/pretty" - - yaml "gopkg.in/yaml.v2" ) -func TestUnmarshalClients(t *testing.T) { - data := `staticClients: +var _ = yaml.YAMLToJSON + +func TestUnmarshalConfig(t *testing.T) { + rawConfig := []byte(` +issuer: http://127.0.0.1:5556/dex +storage: + type: sqlite3 + config: + file: examples/dex.db + +web: + http: 127.0.0.1:5556 +staticClients: - id: example-app redirectURIs: - 'http://127.0.0.1:5555/callback' name: 'Example App' secret: ZXhhbXBsZS1hcHAtc2VjcmV0 -` - var c Config - if err := yaml.Unmarshal([]byte(data), &c); err != nil { - t.Fatal(err) - } - wantClients := []storage.Client{ - { - ID: "example-app", - Name: "Example App", - Secret: "ZXhhbXBsZS1hcHAtc2VjcmV0", - RedirectURIs: []string{ - "http://127.0.0.1:5555/callback", +connectors: +- type: mockCallback + id: mock + name: Example +- type: oidc + id: google + name: Google + config: + issuer: https://accounts.google.com + # Config values starting with a "$" will read from the environment. + clientID: $GOOGLE_CLIENT_ID + clientSecret: $GOOGLE_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" + # 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" +`) + + want := 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", + }, + StaticClients: []storage.Client{ + { + ID: "example-app", + Secret: "ZXhhbXBsZS1hcHAtc2VjcmV0", + Name: "Example App", + RedirectURIs: []string{ + "http://127.0.0.1:5555/callback", + }, + }, + }, + Connectors: []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: "$GOOGLE_CLIENT_ID", + ClientSecret: "$GOOGLE_CLIENT_SECRET", + 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", }, }, } - if diff := pretty.Compare(wantClients, c.StaticClients); diff != "" { - t.Errorf("did not get expected clients: %s", diff) + 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) + } + } diff --git a/cmd/dex/serve.go b/cmd/dex/serve.go index d6d545e7..59679642 100644 --- a/cmd/dex/serve.go +++ b/cmd/dex/serve.go @@ -136,7 +136,11 @@ func serve(cmd *cobra.Command, args []string) error { s = storage.WithStaticClients(s, c.StaticClients) } if len(c.StaticPasswords) > 0 { - s = storage.WithStaticPasswords(s, c.StaticPasswords) + passwords := make([]storage.Password, len(c.StaticPasswords)) + for i, p := range c.StaticPasswords { + passwords[i] = storage.Password(p) + } + s = storage.WithStaticPasswords(s, passwords) } serverConfig := server.Config{ diff --git a/examples/config-dev.yaml b/examples/config-dev.yaml index 84af2fbb..b6a4dc09 100644 --- a/examples/config-dev.yaml +++ b/examples/config-dev.yaml @@ -58,7 +58,7 @@ enablePasswordDB: true staticPasswords: - email: "admin@example.com" # bcrypt hash of the string "password" - hash: "JDJhJDE0JDh4TnlVZ3pzSmVuQm4ySlRPT2QvbmVGcUlnQzF4TEFVRFA3VlpTVzhDNWlkLnFPcmNlYUJX" + hash: "$2a$10$33EMT0cVYVlPy6WAMCLsceLYjWhuHpbz5yuZxu/GAFj03J9Lytjuy" username: "admin" userID: "08a8684b-db88-4b73-90a9-3cd1661f5466"