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{}{
"type": "local",
"id": "foo",
"passwordInfos": []map[string]string{
{"userId": "abc", "passwordHash": "UElORw=="}, // []byte is base64 encoded when using json.Marshasl
{"userId": "271", "passwordPlaintext": "pong"},
},
},
want: &LocalConnectorConfig{
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) {
tests := []map[string]interface{}{
// invalid local connector
map[string]interface{}{
"type": "local",
"passwordInfos": "invalid",
},
// no type
map[string]interface{}{
"id": "bar",

View file

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

View file

@ -113,7 +113,7 @@ func TestHTTPExchangeTokenRefreshToken(t *testing.T) {
}
cfg := &connector.LocalConnectorConfig{
PasswordInfos: []user.PasswordInfo{passwordInfo},
ID: "local",
}
ci := oidc.ClientIdentity{
@ -128,6 +128,10 @@ func TestHTTPExchangeTokenRefreshToken(t *testing.T) {
if err != nil {
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"}
sm := manager.NewSessionManager(db.NewSessionRepo(dbMap), db.NewSessionKeyRepo(dbMap))
@ -153,7 +157,6 @@ func TestHTTPExchangeTokenRefreshToken(t *testing.T) {
t.Fatalf("Unexpected error: %v", err)
}
passwordInfoRepo := db.NewPasswordInfoRepo(db.NewMemDB())
refreshTokenRepo := refreshtest.NewTestRefreshTokenRepo()
srv := &server.Server{

View file

@ -5,6 +5,7 @@ import (
"errors"
"fmt"
"html/template"
"io"
"net/url"
"os"
"path/filepath"
@ -136,7 +137,7 @@ func (cfg *SingleServerConfig) Configure(srv *Server) error {
skRepo := db.NewSessionKeyRepo(dbMap)
sm := sessionmanager.NewSessionManager(sRepo, skRepo)
users, err := loadUsers(cfg.UsersFile)
users, pwis, err := loadUsers(cfg.UsersFile)
if err != nil {
return fmt.Errorf("unable to read users from file: %v", err)
}
@ -145,7 +146,10 @@ func (cfg *SingleServerConfig) Configure(srv *Server) error {
return err
}
pwiRepo := db.NewPasswordInfoRepo(dbMap)
pwiRepo, err := db.NewPasswordInfoRepoFromPasswordInfos(dbMap, pwis)
if err != nil {
return err
}
refTokRepo := db.NewRefreshTokenRepo(dbMap)
@ -163,28 +167,61 @@ func (cfg *SingleServerConfig) Configure(srv *Server) error {
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)
if err != nil {
return
return nil, nil, err
}
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
}
// loadClients parses the clients.json file and returns the clients to be created.
func loadClients(filepath string) ([]oidc.ClientIdentity, error) {
f, err := os.Open(filepath)
if err != nil {
return nil, err
}
defer f.Close()
return loadClientsFromReader(f)
}
func loadClientsFromReader(r io.Reader) ([]oidc.ClientIdentity, error) {
var c []struct {
ID string `json:"id"`
Secret string `json:"secret"`
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
}
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,
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())

View file

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

View file

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