cmd/dex: accept raw bcrypt'd hash as well as base64'd version of hash
This commit is contained in:
		| @@ -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, | ||||
| 	} | ||||
|   | ||||
| @@ -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) | ||||
| 	} | ||||
|  | ||||
| } | ||||
|   | ||||
| @@ -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{ | ||||
|   | ||||
| @@ -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" | ||||
|  | ||||
|   | ||||
		Reference in New Issue
	
	Block a user