Merge pull request #393 from ericchiang/nodb_users

remove passwordInfos from local connector
This commit is contained in:
Eric Chiang 2016-04-06 11:30:08 -07:00
commit e6177cf307
8 changed files with 132 additions and 59 deletions

View file

@ -60,23 +60,9 @@ func TestNewConnectorConfigFromMap(t *testing.T) {
m: map[string]interface{}{ m: map[string]interface{}{
"type": "local", "type": "local",
"id": "foo", "id": "foo",
"passwordInfos": []map[string]string{
{"userId": "abc", "passwordHash": "UElORw=="}, // []byte is base64 encoded when using json.Marshasl
{"userId": "271", "passwordPlaintext": "pong"},
},
}, },
want: &LocalConnectorConfig{ want: &LocalConnectorConfig{
ID: "foo", ID: "foo",
PasswordInfos: []user.PasswordInfo{
user.PasswordInfo{
UserID: "abc",
Password: user.Password("PING"),
},
user.PasswordInfo{
UserID: "271",
Password: user.Password("PONG"),
},
},
}, },
}, },
{ {
@ -111,12 +97,6 @@ func TestNewConnectorConfigFromMap(t *testing.T) {
func TestNewConnectorConfigFromMapFail(t *testing.T) { func TestNewConnectorConfigFromMapFail(t *testing.T) {
tests := []map[string]interface{}{ tests := []map[string]interface{}{
// invalid local connector
map[string]interface{}{
"type": "local",
"passwordInfos": "invalid",
},
// no type // no type
map[string]interface{}{ map[string]interface{}{
"id": "bar", "id": "bar",

View file

@ -22,7 +22,6 @@ func init() {
type LocalConnectorConfig struct { type LocalConnectorConfig struct {
ID string `json:"id"` ID string `json:"id"`
PasswordInfos []user.PasswordInfo `json:"passwordInfos"`
} }
func (cfg *LocalConnectorConfig) ConnectorID() string { func (cfg *LocalConnectorConfig) ConnectorID() string {

View file

@ -113,7 +113,7 @@ func TestHTTPExchangeTokenRefreshToken(t *testing.T) {
} }
cfg := &connector.LocalConnectorConfig{ cfg := &connector.LocalConnectorConfig{
PasswordInfos: []user.PasswordInfo{passwordInfo}, ID: "local",
} }
ci := oidc.ClientIdentity{ ci := oidc.ClientIdentity{
@ -128,6 +128,10 @@ func TestHTTPExchangeTokenRefreshToken(t *testing.T) {
if err != nil { if err != nil {
t.Fatalf("Failed to create client identity repo: " + err.Error()) t.Fatalf("Failed to create client identity repo: " + err.Error())
} }
passwordInfoRepo, err := db.NewPasswordInfoRepoFromPasswordInfos(db.NewMemDB(), []user.PasswordInfo{passwordInfo})
if err != nil {
t.Fatalf("Failed to create password info repo: %v", err)
}
issuerURL := url.URL{Scheme: "http", Host: "server.example.com"} issuerURL := url.URL{Scheme: "http", Host: "server.example.com"}
sm := manager.NewSessionManager(db.NewSessionRepo(dbMap), db.NewSessionKeyRepo(dbMap)) sm := manager.NewSessionManager(db.NewSessionRepo(dbMap), db.NewSessionKeyRepo(dbMap))
@ -153,7 +157,6 @@ func TestHTTPExchangeTokenRefreshToken(t *testing.T) {
t.Fatalf("Unexpected error: %v", err) t.Fatalf("Unexpected error: %v", err)
} }
passwordInfoRepo := db.NewPasswordInfoRepo(db.NewMemDB())
refreshTokenRepo := refreshtest.NewTestRefreshTokenRepo() refreshTokenRepo := refreshtest.NewTestRefreshTokenRepo()
srv := &server.Server{ srv := &server.Server{

View file

@ -5,6 +5,7 @@ import (
"errors" "errors"
"fmt" "fmt"
"html/template" "html/template"
"io"
"net/url" "net/url"
"os" "os"
"path/filepath" "path/filepath"
@ -136,7 +137,7 @@ func (cfg *SingleServerConfig) Configure(srv *Server) error {
skRepo := db.NewSessionKeyRepo(dbMap) skRepo := db.NewSessionKeyRepo(dbMap)
sm := sessionmanager.NewSessionManager(sRepo, skRepo) sm := sessionmanager.NewSessionManager(sRepo, skRepo)
users, err := loadUsers(cfg.UsersFile) users, pwis, err := loadUsers(cfg.UsersFile)
if err != nil { if err != nil {
return fmt.Errorf("unable to read users from file: %v", err) return fmt.Errorf("unable to read users from file: %v", err)
} }
@ -145,7 +146,10 @@ func (cfg *SingleServerConfig) Configure(srv *Server) error {
return err return err
} }
pwiRepo := db.NewPasswordInfoRepo(dbMap) pwiRepo, err := db.NewPasswordInfoRepoFromPasswordInfos(dbMap, pwis)
if err != nil {
return err
}
refTokRepo := db.NewRefreshTokenRepo(dbMap) refTokRepo := db.NewRefreshTokenRepo(dbMap)
@ -163,28 +167,61 @@ func (cfg *SingleServerConfig) Configure(srv *Server) error {
return nil return nil
} }
func loadUsers(filepath string) (users []user.UserWithRemoteIdentities, err error) { // loadUsers parses the user.json file and returns the users to be created.
func loadUsers(filepath string) ([]user.UserWithRemoteIdentities, []user.PasswordInfo, error) {
f, err := os.Open(filepath) f, err := os.Open(filepath)
if err != nil { if err != nil {
return return nil, nil, err
} }
defer f.Close() defer f.Close()
err = json.NewDecoder(f).Decode(&users) return loadUsersFromReader(f)
}
func loadUsersFromReader(r io.Reader) (users []user.UserWithRemoteIdentities, pwis []user.PasswordInfo, err error) {
// Encoding used by the user config file.
var configUsers []struct {
user.User
Password string `json:"password"`
RemoteIdentities []user.RemoteIdentity `json:"remoteIdentities"`
}
if err := json.NewDecoder(r).Decode(&configUsers); err != nil {
return nil, nil, err
}
users = make([]user.UserWithRemoteIdentities, len(configUsers))
pwis = make([]user.PasswordInfo, len(configUsers))
for i, u := range configUsers {
users[i] = user.UserWithRemoteIdentities{
User: u.User,
RemoteIdentities: u.RemoteIdentities,
}
hashedPassword, err := user.NewPasswordFromPlaintext(u.Password)
if err != nil {
return nil, nil, err
}
pwis[i] = user.PasswordInfo{UserID: u.ID, Password: hashedPassword}
}
return return
} }
// loadClients parses the clients.json file and returns the clients to be created.
func loadClients(filepath string) ([]oidc.ClientIdentity, error) { func loadClients(filepath string) ([]oidc.ClientIdentity, error) {
f, err := os.Open(filepath) f, err := os.Open(filepath)
if err != nil { if err != nil {
return nil, err return nil, err
} }
defer f.Close() defer f.Close()
return loadClientsFromReader(f)
}
func loadClientsFromReader(r io.Reader) ([]oidc.ClientIdentity, error) {
var c []struct { var c []struct {
ID string `json:"id"` ID string `json:"id"`
Secret string `json:"secret"` Secret string `json:"secret"`
RedirectURLs []string `json:"redirectURLs"` RedirectURLs []string `json:"redirectURLs"`
} }
if err := json.NewDecoder(f).Decode(&c); err != nil { if err := json.NewDecoder(r).Decode(&c); err != nil {
return nil, err return nil, err
} }
clients := make([]oidc.ClientIdentity, len(c)) clients := make([]oidc.ClientIdentity, len(c))

78
server/config_test.go Normal file
View file

@ -0,0 +1,78 @@
package server
import (
"strings"
"testing"
"github.com/coreos/dex/user"
"github.com/kylelemons/godebug/pretty"
)
func TestLoadUsers(t *testing.T) {
tests := []struct {
// The raw JSON file
raw string
expUsers []user.UserWithRemoteIdentities
// userid -> plaintext password
expPasswds map[string]string
}{
{
raw: `[
{
"id": "elroy-id",
"email": "elroy77@example.com",
"displayName": "Elroy Jonez",
"password": "bones",
"remoteIdentities": [
{
"connectorId": "local",
"id": "elroy-id"
}
]
}
]`,
expUsers: []user.UserWithRemoteIdentities{
{
User: user.User{
ID: "elroy-id",
Email: "elroy77@example.com",
DisplayName: "Elroy Jonez",
},
RemoteIdentities: []user.RemoteIdentity{
{
ConnectorID: "local",
ID: "elroy-id",
},
},
},
},
expPasswds: map[string]string{
"elroy-id": "bones",
},
},
}
for i, tt := range tests {
users, pwInfos, err := loadUsersFromReader(strings.NewReader(tt.raw))
if err != nil {
t.Errorf("case %d: failed to load user: %v", i, err)
return
}
if diff := pretty.Compare(tt.expUsers, users); diff != "" {
t.Errorf("case: %d: wantUsers!=gotUsers: %s", i, diff)
}
// For each password info loaded, verify the password.
for _, pwInfo := range pwInfos {
expPW, ok := tt.expPasswds[pwInfo.UserID]
if !ok {
t.Errorf("no password entry for %s", pwInfo.UserID)
continue
}
if _, err := pwInfo.Authenticate(expPW); err != nil {
t.Errorf("case %d: user %s's password did not match", i, pwInfo.UserID)
}
}
}
}

View file

@ -178,19 +178,6 @@ func (s *Server) AddConnector(cfg connector.ConnectorConfig) error {
UserRepo: s.UserRepo, UserRepo: s.UserRepo,
PasswordInfoRepo: s.PasswordInfoRepo, PasswordInfoRepo: s.PasswordInfoRepo,
}) })
localCfg, ok := cfg.(*connector.LocalConnectorConfig)
if !ok {
return errors.New("config for LocalConnector not a LocalConnectorConfig?")
}
if len(localCfg.PasswordInfos) > 0 {
err := user.LoadPasswordInfos(s.PasswordInfoRepo,
localCfg.PasswordInfos)
if err != nil {
return err
}
}
} }
log.Infof("Loaded IdP connector: id=%s type=%s", connectorID, cfg.ConnectorType()) log.Infof("Loaded IdP connector: id=%s type=%s", connectorID, cfg.ConnectorType())

View file

@ -1,17 +1,7 @@
[ [
{ {
"type": "local", "type": "local",
"id": "local", "id": "local"
"passwordInfos": [
{
"userId":"elroy-id",
"passwordPlaintext": "bones"
},
{
"userId":"penny",
"passwordPlaintext": "kibble"
}
]
}, },
{ {
"type": "oidc", "type": "oidc",

View file

@ -1,10 +1,9 @@
[ [
{ {
"user":{
"id": "elroy-id", "id": "elroy-id",
"email": "elroy77@example.com", "email": "elroy77@example.com",
"displayName": "Elroy Jonez" "displayName": "Elroy Jonez",
}, "password": "bones",
"remoteIdentities": [ "remoteIdentities": [
{ {
"connectorId": "local", "connectorId": "local",