Merge pull request #393 from ericchiang/nodb_users
remove passwordInfos from local connector
This commit is contained in:
commit
e6177cf307
8 changed files with 132 additions and 59 deletions
|
@ -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",
|
||||||
|
|
|
@ -21,8 +21,7 @@ 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 {
|
||||||
|
|
|
@ -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{
|
||||||
|
|
|
@ -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
78
server/config_test.go
Normal 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)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -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())
|
||||||
|
|
|
@ -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",
|
||||||
|
|
|
@ -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",
|
||||||
|
|
Reference in a new issue