Merge pull request #411 from bobbyrullo/cross_client

Use Client defined in dex instead of go-oidc for storing clients
This commit is contained in:
bobbyrullo 2016-04-20 14:38:18 -07:00
commit 69ca9dba2e
44 changed files with 1181 additions and 515 deletions

View file

@ -5,36 +5,36 @@ import (
"net/http" "net/http"
"github.com/coreos/go-oidc/oidc" "github.com/coreos/go-oidc/oidc"
"github.com/go-gorp/gorp"
"github.com/coreos/dex/client" "github.com/coreos/dex/client"
"github.com/coreos/dex/db"
"github.com/coreos/dex/schema/adminschema" "github.com/coreos/dex/schema/adminschema"
"github.com/coreos/dex/user" "github.com/coreos/dex/user"
"github.com/coreos/dex/user/manager" "github.com/coreos/dex/user/manager"
) )
var (
ClientIDGenerator = oidc.GenClientID
)
// AdminAPI provides the logic necessary to implement the Admin API. // AdminAPI provides the logic necessary to implement the Admin API.
type AdminAPI struct { type AdminAPI struct {
userManager *manager.UserManager userManager *manager.UserManager
userRepo user.UserRepo userRepo user.UserRepo
passwordInfoRepo user.PasswordInfoRepo passwordInfoRepo user.PasswordInfoRepo
clientIdentityRepo client.ClientIdentityRepo clientRepo client.ClientRepo
localConnectorID string localConnectorID string
} }
// TODO(ericchiang): Swap the DbMap for a storage interface. See #278 func NewAdminAPI(userRepo user.UserRepo, pwiRepo user.PasswordInfoRepo, clientRepo client.ClientRepo, userManager *manager.UserManager, localConnectorID string) *AdminAPI {
func NewAdminAPI(dbMap *gorp.DbMap, userManager *manager.UserManager, localConnectorID string) *AdminAPI {
if localConnectorID == "" { if localConnectorID == "" {
panic("must specify non-blank localConnectorID") panic("must specify non-blank localConnectorID")
} }
return &AdminAPI{ return &AdminAPI{
userManager: userManager, userManager: userManager,
userRepo: db.NewUserRepo(dbMap), userRepo: userRepo,
passwordInfoRepo: db.NewPasswordInfoRepo(dbMap), passwordInfoRepo: pwiRepo,
clientIdentityRepo: db.NewClientIdentityRepo(dbMap), clientRepo: clientRepo,
localConnectorID: localConnectorID, localConnectorID: localConnectorID,
} }
} }
@ -67,10 +67,20 @@ func errorMaker(typ string, desc string, code int) func(internal error) Error {
} }
var ( var (
ErrorMissingClient = errorMaker("bad_request", "The 'client' cannot be empty", http.StatusBadRequest)(nil)
// Called when oidc.ClientMetadata.Valid() fails.
ErrorInvalidClientFunc = errorMaker("bad_request", "Your client could not be validated.", http.StatusBadRequest)
errorMap = map[error]func(error) Error{ errorMap = map[error]func(error) Error{
user.ErrorNotFound: errorMaker("resource_not_found", "Resource could not be found.", http.StatusNotFound), user.ErrorNotFound: errorMaker("resource_not_found", "Resource could not be found.", http.StatusNotFound),
user.ErrorDuplicateEmail: errorMaker("bad_request", "Email already in use.", http.StatusBadRequest), user.ErrorDuplicateEmail: errorMaker("bad_request", "Email already in use.", http.StatusBadRequest),
user.ErrorInvalidEmail: errorMaker("bad_request", "invalid email.", http.StatusBadRequest), user.ErrorInvalidEmail: errorMaker("bad_request", "invalid email.", http.StatusBadRequest),
adminschema.ErrorInvalidRedirectURI: errorMaker("bad_request", "invalid redirectURI.", http.StatusBadRequest),
adminschema.ErrorInvalidLogoURI: errorMaker("bad_request", "invalid logoURI.", http.StatusBadRequest),
adminschema.ErrorInvalidClientURI: errorMaker("bad_request", "invalid clientURI.", http.StatusBadRequest),
adminschema.ErrorNoRedirectURI: errorMaker("bad_request", "invalid redirectURI.", http.StatusBadRequest),
} }
) )
@ -116,25 +126,38 @@ func (a *AdminAPI) GetState() (adminschema.State, error) {
return state, nil return state, nil
} }
type ClientRegistrationRequest struct { func (a *AdminAPI) CreateClient(req adminschema.ClientCreateRequest) (adminschema.ClientCreateResponse, error) {
IsAdmin bool `json:"isAdmin"` if req.Client == nil {
Client oidc.ClientMetadata `json:"client"` return adminschema.ClientCreateResponse{}, ErrorMissingClient
} }
func (a *AdminAPI) CreateClient(req ClientRegistrationRequest) (oidc.ClientRegistrationResponse, error) { cli, err := adminschema.MapSchemaClientToClient(*req.Client)
if err := req.Client.Valid(); err != nil {
return oidc.ClientRegistrationResponse{}, mapError(err)
}
// metadata is guarenteed to have at least one redirect_uri by earlier validation.
id, err := oidc.GenClientID(req.Client.RedirectURIs[0].Host)
if err != nil { if err != nil {
return oidc.ClientRegistrationResponse{}, mapError(err) return adminschema.ClientCreateResponse{}, mapError(err)
} }
c, err := a.clientIdentityRepo.New(id, req.Client, req.IsAdmin)
if err := cli.Metadata.Valid(); err != nil {
return adminschema.ClientCreateResponse{}, ErrorInvalidClientFunc(err)
}
// metadata is guaranteed to have at least one redirect_uri by earlier validation.
id, err := ClientIDGenerator(cli.Metadata.RedirectURIs[0].Host)
if err != nil { if err != nil {
return oidc.ClientRegistrationResponse{}, mapError(err) return adminschema.ClientCreateResponse{}, mapError(err)
} }
return oidc.ClientRegistrationResponse{ClientID: c.ID, ClientSecret: c.Secret, ClientMetadata: req.Client}, nil
cli.Credentials.ID = id
creds, err := a.clientRepo.New(cli)
if err != nil {
return adminschema.ClientCreateResponse{}, mapError(err)
}
req.Client.Id = creds.ID
req.Client.Secret = creds.Secret
return adminschema.ClientCreateResponse{
Client: req.Client,
}, nil
} }
func mapError(e error) error { func mapError(e error) error {

View file

@ -3,6 +3,7 @@ package admin
import ( import (
"testing" "testing"
"github.com/coreos/dex/client"
"github.com/coreos/dex/connector" "github.com/coreos/dex/connector"
"github.com/coreos/dex/db" "github.com/coreos/dex/db"
"github.com/coreos/dex/schema/adminschema" "github.com/coreos/dex/schema/adminschema"
@ -15,6 +16,7 @@ import (
type testFixtures struct { type testFixtures struct {
ur user.UserRepo ur user.UserRepo
pwr user.PasswordInfoRepo pwr user.PasswordInfoRepo
cr client.ClientRepo
mgr *manager.UserManager mgr *manager.UserManager
adAPI *AdminAPI adAPI *AdminAPI
} }
@ -69,7 +71,7 @@ func makeTestFixtures() *testFixtures {
}() }()
f.mgr = manager.NewUserManager(f.ur, f.pwr, ccr, db.TransactionFactory(dbMap), manager.ManagerOptions{}) f.mgr = manager.NewUserManager(f.ur, f.pwr, ccr, db.TransactionFactory(dbMap), manager.ManagerOptions{})
f.adAPI = NewAdminAPI(dbMap, f.mgr, "local") f.adAPI = NewAdminAPI(f.ur, f.pwr, f.cr, f.mgr, "local")
return f return f
} }

View file

@ -1,7 +1,9 @@
package client package client
import ( import (
"encoding/json"
"errors" "errors"
"io"
"net/url" "net/url"
"reflect" "reflect"
@ -15,7 +17,15 @@ var (
ErrorNotFound = errors.New("no data found") ErrorNotFound = errors.New("no data found")
) )
type ClientIdentityRepo interface { type Client struct {
Credentials oidc.ClientCredentials
Metadata oidc.ClientMetadata
Admin bool
}
type ClientRepo interface {
Get(clientID string) (Client, error)
// Metadata returns one matching ClientMetadata if the given client // Metadata returns one matching ClientMetadata if the given client
// exists, otherwise nil. The returned error will be non-nil only // exists, otherwise nil. The returned error will be non-nil only
// if the repo was unable to determine client existence. // if the repo was unable to determine client existence.
@ -27,13 +37,13 @@ type ClientIdentityRepo interface {
// to make these assertions will a non-nil error be returned. // to make these assertions will a non-nil error be returned.
Authenticate(creds oidc.ClientCredentials) (bool, error) Authenticate(creds oidc.ClientCredentials) (bool, error)
// All returns all registered Client Identities. // All returns all registered Clients
All() ([]oidc.ClientIdentity, error) All() ([]Client, error)
// New registers a ClientIdentity with the repo for the given metadata. // New registers a Client with the repo.
// An unused ID must be provided. A corresponding secret will be returned // An unused ID must be provided. A corresponding secret will be returned
// in a ClientCredentials struct along with the provided ID. // in a ClientCredentials struct along with the provided ID.
New(id string, meta oidc.ClientMetadata, admin bool) (*oidc.ClientCredentials, error) New(client Client) (*oidc.ClientCredentials, error)
SetDexAdmin(clientID string, isAdmin bool) error SetDexAdmin(clientID string, isAdmin bool) error
@ -64,3 +74,42 @@ func ValidRedirectURL(rURL *url.URL, redirectURLs []url.URL) (url.URL, error) {
} }
return url.URL{}, ErrorInvalidRedirectURL return url.URL{}, ErrorInvalidRedirectURL
} }
func ClientsFromReader(r io.Reader) ([]Client, error) {
var c []struct {
ID string `json:"id"`
Secret string `json:"secret"`
RedirectURLs []string `json:"redirectURLs"`
}
if err := json.NewDecoder(r).Decode(&c); err != nil {
return nil, err
}
clients := make([]Client, len(c))
for i, client := range c {
if client.ID == "" {
return nil, errors.New("clients must have an ID")
}
if len(client.Secret) == 0 {
return nil, errors.New("clients must have a Secret")
}
redirectURIs := make([]url.URL, len(client.RedirectURLs))
for j, u := range client.RedirectURLs {
uri, err := url.Parse(u)
if err != nil {
return nil, err
}
redirectURIs[j] = *uri
}
clients[i] = Client{
Credentials: oidc.ClientCredentials{
ID: client.ID,
Secret: client.Secret,
},
Metadata: oidc.ClientMetadata{
RedirectURIs: redirectURIs,
},
}
}
return clients, nil
}

149
client/client_test.go Normal file
View file

@ -0,0 +1,149 @@
package client
import (
"encoding/base64"
"net/url"
"strings"
"testing"
"github.com/coreos/go-oidc/oidc"
"github.com/kylelemons/godebug/pretty"
)
var (
goodSecret1 = base64.URLEncoding.EncodeToString([]byte("my_secret"))
goodSecret2 = base64.URLEncoding.EncodeToString([]byte("my_other_secret"))
goodClient1 = `{
"id": "my_id",
"secret": "` + goodSecret1 + `",
"redirectURLs": ["https://client.example.com"]
}`
goodClient2 = `{
"id": "my_other_id",
"secret": "` + goodSecret2 + `",
"redirectURLs": ["https://client2.example.com","https://client2_a.example.com"]
}`
badURLClient = `{
"id": "my_id",
"secret": "` + goodSecret1 + `",
"redirectURLs": ["hdtp:/\(bad)(u)(r)(l)"]
}`
badSecretClient = `{
"id": "my_id",
"secret": "` + "****" + `",
"redirectURLs": ["https://client.example.com"]
}`
noSecretClient = `{
"id": "my_id",
"redirectURLs": ["https://client.example.com"]
}`
noIDClient = `{
"secret": "` + goodSecret1 + `",
"redirectURLs": ["https://client.example.com"]
}`
)
func TestClientsFromReader(t *testing.T) {
tests := []struct {
json string
want []Client
wantErr bool
}{
{
json: "[]",
want: []Client{},
},
{
json: "[" + goodClient1 + "]",
want: []Client{
{
Credentials: oidc.ClientCredentials{
ID: "my_id",
Secret: "my_secret",
},
Metadata: oidc.ClientMetadata{
RedirectURIs: []url.URL{
mustParseURL(t, "https://client.example.com"),
},
},
},
},
},
{
json: "[" + strings.Join([]string{goodClient1, goodClient2}, ",") + "]",
want: []Client{
{
Credentials: oidc.ClientCredentials{
ID: "my_id",
Secret: "my_secret",
},
Metadata: oidc.ClientMetadata{
RedirectURIs: []url.URL{
mustParseURL(t, "https://client.example.com"),
},
},
},
{
Credentials: oidc.ClientCredentials{
ID: "my_other_id",
Secret: "my_other_secret",
},
Metadata: oidc.ClientMetadata{
RedirectURIs: []url.URL{
mustParseURL(t, "https://client2.example.com"),
mustParseURL(t, "https://client2_a.example.com"),
},
},
},
},
}, {
json: "[" + badURLClient + "]",
wantErr: true,
},
{
json: "[" + badSecretClient + "]",
wantErr: true,
},
{
json: "[" + noSecretClient + "]",
wantErr: true,
},
{
json: "[" + noIDClient + "]",
wantErr: true,
},
}
for i, tt := range tests {
r := strings.NewReader(tt.json)
cs, err := ClientsFromReader(r)
if tt.wantErr {
if err == nil {
t.Errorf("case %d: want non-nil err", i)
t.Logf(pretty.Sprint(cs))
}
continue
}
if err != nil {
t.Errorf("case %d: got unexpected error parsing clients: %v", i, err)
t.Logf(tt.json)
}
if diff := pretty.Compare(tt.want, cs); diff != "" {
t.Errorf("case %d: Compare(want, got): %v", i, diff)
}
}
}
func mustParseURL(t *testing.T, s string) url.URL {
u, err := url.Parse(s)
if err != nil {
t.Fatalf("Cannot parse %v as url: %v", s, err)
}
return *u
}

View file

@ -116,10 +116,11 @@ func main() {
userRepo := db.NewUserRepo(dbc) userRepo := db.NewUserRepo(dbc)
pwiRepo := db.NewPasswordInfoRepo(dbc) pwiRepo := db.NewPasswordInfoRepo(dbc)
connCfgRepo := db.NewConnectorConfigRepo(dbc) connCfgRepo := db.NewConnectorConfigRepo(dbc)
clientRepo := db.NewClientRepo(dbc)
userManager := manager.NewUserManager(userRepo, userManager := manager.NewUserManager(userRepo,
pwiRepo, connCfgRepo, db.TransactionFactory(dbc), manager.ManagerOptions{}) pwiRepo, connCfgRepo, db.TransactionFactory(dbc), manager.ManagerOptions{})
adminAPI := admin.NewAdminAPI(dbc, userManager, *localConnectorID) adminAPI := admin.NewAdminAPI(userRepo, pwiRepo, clientRepo, userManager, *localConnectorID)
kRepo, err := db.NewPrivateKeySetRepo(dbc, *useOldFormat, keySecrets.BytesSlice()...) kRepo, err := db.NewPrivateKeySetRepo(dbc, *useOldFormat, keySecrets.BytesSlice()...)
if err != nil { if err != nil {
log.Fatalf(err.Error()) log.Fatalf(err.Error())

View file

@ -14,7 +14,7 @@ func newDBDriver(dsn string) (driver, error) {
} }
drv := &dbDriver{ drv := &dbDriver{
ciRepo: db.NewClientIdentityRepo(dbc), ciRepo: db.NewClientRepo(dbc),
cfgRepo: db.NewConnectorConfigRepo(dbc), cfgRepo: db.NewConnectorConfigRepo(dbc),
} }
@ -22,7 +22,7 @@ func newDBDriver(dsn string) (driver, error) {
} }
type dbDriver struct { type dbDriver struct {
ciRepo client.ClientIdentityRepo ciRepo client.ClientRepo
cfgRepo *db.ConnectorConfigRepo cfgRepo *db.ConnectorConfigRepo
} }
@ -36,7 +36,12 @@ func (d *dbDriver) NewClient(meta oidc.ClientMetadata) (*oidc.ClientCredentials,
return nil, err return nil, err
} }
return d.ciRepo.New(clientID, meta, false) return d.ciRepo.New(client.Client{
Credentials: oidc.ClientCredentials{
ID: clientID,
},
Metadata: meta,
})
} }
func (d *dbDriver) ConnectorConfigs() ([]connector.ConnectorConfig, error) { func (d *dbDriver) ConnectorConfigs() ([]connector.ConnectorConfig, error) {

View file

@ -18,7 +18,7 @@ import (
) )
const ( const (
clientIdentityTableName = "client_identity" clientTableName = "client_identity"
bcryptHashCost = 10 bcryptHashCost = 10
@ -34,46 +34,53 @@ const (
func init() { func init() {
register(table{ register(table{
name: clientIdentityTableName, name: clientTableName,
model: clientIdentityModel{}, model: clientModel{},
autoinc: false, autoinc: false,
pkey: []string{"id"}, pkey: []string{"id"},
}) })
} }
func newClientIdentityModel(id string, secret []byte, meta *oidc.ClientMetadata) (*clientIdentityModel, error) { func newClientModel(cli client.Client) (*clientModel, error) {
hashed, err := bcrypt.GenerateFromPassword(secret, bcryptHashCost) secretBytes, err := base64.URLEncoding.DecodeString(cli.Credentials.Secret)
if err != nil {
return nil, err
}
hashed, err := bcrypt.GenerateFromPassword([]byte(
secretBytes),
bcryptHashCost)
if err != nil { if err != nil {
return nil, err return nil, err
} }
bmeta, err := json.Marshal(meta) bmeta, err := json.Marshal(&cli.Metadata)
if err != nil { if err != nil {
return nil, err return nil, err
} }
cim := clientIdentityModel{ cim := clientModel{
ID: id, ID: cli.Credentials.ID,
Secret: hashed, Secret: hashed,
Metadata: string(bmeta), Metadata: string(bmeta),
DexAdmin: cli.Admin,
} }
return &cim, nil return &cim, nil
} }
type clientIdentityModel struct { type clientModel struct {
ID string `db:"id"` ID string `db:"id"`
Secret []byte `db:"secret"` Secret []byte `db:"secret"`
Metadata string `db:"metadata"` Metadata string `db:"metadata"`
DexAdmin bool `db:"dex_admin"` DexAdmin bool `db:"dex_admin"`
} }
func (m *clientIdentityModel) ClientIdentity() (*oidc.ClientIdentity, error) { func (m *clientModel) Client() (*client.Client, error) {
ci := oidc.ClientIdentity{ ci := client.Client{
Credentials: oidc.ClientCredentials{ Credentials: oidc.ClientCredentials{
ID: m.ID, ID: m.ID,
Secret: string(m.Secret),
}, },
Admin: m.DexAdmin,
} }
if err := json.Unmarshal([]byte(m.Metadata), &ci.Metadata); err != nil { if err := json.Unmarshal([]byte(m.Metadata), &ci.Metadata); err != nil {
@ -83,16 +90,26 @@ func (m *clientIdentityModel) ClientIdentity() (*oidc.ClientIdentity, error) {
return &ci, nil return &ci, nil
} }
func NewClientIdentityRepo(dbm *gorp.DbMap) client.ClientIdentityRepo { func NewClientRepo(dbm *gorp.DbMap) client.ClientRepo {
return newClientIdentityRepo(dbm) return newClientRepo(dbm)
} }
func newClientIdentityRepo(dbm *gorp.DbMap) *clientIdentityRepo { func NewClientRepoWithSecretGenerator(dbm *gorp.DbMap, secGen SecretGenerator) client.ClientRepo {
return &clientIdentityRepo{db: &db{dbm}} rep := newClientRepo(dbm)
rep.secretGenerator = secGen
return rep
} }
func NewClientIdentityRepoFromClients(dbm *gorp.DbMap, clients []oidc.ClientIdentity) (client.ClientIdentityRepo, error) { func newClientRepo(dbm *gorp.DbMap) *clientRepo {
repo := newClientIdentityRepo(dbm) return &clientRepo{
db: &db{dbm},
secretGenerator: DefaultSecretGenerator,
}
}
func NewClientRepoFromClients(dbm *gorp.DbMap, clients []client.Client) (client.ClientRepo, error) {
repo := newClientRepo(dbm)
tx, err := repo.begin() tx, err := repo.begin()
if err != nil { if err != nil {
return nil, err return nil, err
@ -103,12 +120,7 @@ func NewClientIdentityRepoFromClients(dbm *gorp.DbMap, clients []oidc.ClientIden
if c.Credentials.Secret == "" { if c.Credentials.Secret == "" {
return nil, fmt.Errorf("client %q has no secret", c.Credentials.ID) return nil, fmt.Errorf("client %q has no secret", c.Credentials.ID)
} }
dec, err := base64.URLEncoding.DecodeString(c.Credentials.Secret) cm, err := newClientModel(c)
if err != nil {
return nil, fmt.Errorf("client secrets must be base64 decodable. See issue #337. Please consider replacing %q with %q",
c.Credentials.Secret, base64.URLEncoding.EncodeToString([]byte(c.Credentials.Secret)))
}
cm, err := newClientIdentityModel(c.Credentials.ID, dec, &c.Metadata)
if err != nil { if err != nil {
return nil, err return nil, err
} }
@ -123,49 +135,59 @@ func NewClientIdentityRepoFromClients(dbm *gorp.DbMap, clients []oidc.ClientIden
return repo, nil return repo, nil
} }
type clientIdentityRepo struct { type clientRepo struct {
*db *db
secretGenerator SecretGenerator
} }
func (r *clientIdentityRepo) Metadata(clientID string) (*oidc.ClientMetadata, error) { func (r *clientRepo) Get(clientID string) (client.Client, error) {
m, err := r.executor(nil).Get(clientIdentityModel{}, clientID) m, err := r.executor(nil).Get(clientModel{}, clientID)
if err == sql.ErrNoRows || m == nil { if err == sql.ErrNoRows || m == nil {
return nil, client.ErrorNotFound return client.Client{}, client.ErrorNotFound
} }
if err != nil { if err != nil {
return nil, err return client.Client{}, err
} }
cim, ok := m.(*clientIdentityModel) cim, ok := m.(*clientModel)
if !ok { if !ok {
log.Errorf("expected clientIdentityModel but found %v", reflect.TypeOf(m)) log.Errorf("expected clientModel but found %v", reflect.TypeOf(m))
return nil, errors.New("unrecognized model") return client.Client{}, errors.New("unrecognized model")
} }
ci, err := cim.ClientIdentity() ci, err := cim.Client()
if err != nil { if err != nil {
return nil, err return client.Client{}, err
} }
return &ci.Metadata, nil return *ci, nil
} }
func (r *clientIdentityRepo) IsDexAdmin(clientID string) (bool, error) { func (r *clientRepo) Metadata(clientID string) (*oidc.ClientMetadata, error) {
m, err := r.executor(nil).Get(clientIdentityModel{}, clientID) c, err := r.Get(clientID)
if err != nil {
return nil, err
}
return &c.Metadata, nil
}
func (r *clientRepo) IsDexAdmin(clientID string) (bool, error) {
m, err := r.executor(nil).Get(clientModel{}, clientID)
if m == nil || err != nil { if m == nil || err != nil {
return false, err return false, err
} }
cim, ok := m.(*clientIdentityModel) cim, ok := m.(*clientModel)
if !ok { if !ok {
log.Errorf("expected clientIdentityModel but found %v", reflect.TypeOf(m)) log.Errorf("expected clientModel but found %v", reflect.TypeOf(m))
return false, errors.New("unrecognized model") return false, errors.New("unrecognized model")
} }
return cim.DexAdmin, nil return cim.DexAdmin, nil
} }
func (r *clientIdentityRepo) SetDexAdmin(clientID string, isAdmin bool) error { func (r *clientRepo) SetDexAdmin(clientID string, isAdmin bool) error {
tx, err := r.begin() tx, err := r.begin()
if err != nil { if err != nil {
return err return err
@ -173,14 +195,14 @@ func (r *clientIdentityRepo) SetDexAdmin(clientID string, isAdmin bool) error {
defer tx.Rollback() defer tx.Rollback()
exec := r.executor(tx) exec := r.executor(tx)
m, err := exec.Get(clientIdentityModel{}, clientID) m, err := exec.Get(clientModel{}, clientID)
if m == nil || err != nil { if m == nil || err != nil {
return err return err
} }
cim, ok := m.(*clientIdentityModel) cim, ok := m.(*clientModel)
if !ok { if !ok {
log.Errorf("expected clientIdentityModel but found %v", reflect.TypeOf(m)) log.Errorf("expected clientModel but found %v", reflect.TypeOf(m))
return errors.New("unrecognized model") return errors.New("unrecognized model")
} }
@ -193,15 +215,15 @@ func (r *clientIdentityRepo) SetDexAdmin(clientID string, isAdmin bool) error {
return tx.Commit() return tx.Commit()
} }
func (r *clientIdentityRepo) Authenticate(creds oidc.ClientCredentials) (bool, error) { func (r *clientRepo) Authenticate(creds oidc.ClientCredentials) (bool, error) {
m, err := r.executor(nil).Get(clientIdentityModel{}, creds.ID) m, err := r.executor(nil).Get(clientModel{}, creds.ID)
if m == nil || err != nil { if m == nil || err != nil {
return false, err return false, err
} }
cim, ok := m.(*clientIdentityModel) cim, ok := m.(*clientModel)
if !ok { if !ok {
log.Errorf("expected clientIdentityModel but found %v", reflect.TypeOf(m)) log.Errorf("expected clientModel but found %v", reflect.TypeOf(m))
return false, errors.New("unrecognized model") return false, errors.New("unrecognized model")
} }
@ -238,17 +260,24 @@ func isAlreadyExistsErr(err error) bool {
return false return false
} }
func (r *clientIdentityRepo) New(id string, meta oidc.ClientMetadata, admin bool) (*oidc.ClientCredentials, error) { type SecretGenerator func() ([]byte, error)
secret, err := pcrypto.RandBytes(maxSecretLength)
func DefaultSecretGenerator() ([]byte, error) {
return pcrypto.RandBytes(maxSecretLength)
}
func (r *clientRepo) New(cli client.Client) (*oidc.ClientCredentials, error) {
secret, err := r.secretGenerator()
if err != nil { if err != nil {
return nil, err return nil, err
} }
cim, err := newClientIdentityModel(id, secret, &meta) cli.Credentials.Secret = base64.URLEncoding.EncodeToString(secret)
cim, err := newClientModel(cli)
if err != nil { if err != nil {
return nil, err return nil, err
} }
cim.DexAdmin = admin
if err := r.executor(nil).Insert(cim); err != nil { if err := r.executor(nil).Insert(cim); err != nil {
if isAlreadyExistsErr(err) { if isAlreadyExistsErr(err) {
@ -258,29 +287,29 @@ func (r *clientIdentityRepo) New(id string, meta oidc.ClientMetadata, admin bool
} }
cc := oidc.ClientCredentials{ cc := oidc.ClientCredentials{
ID: id, ID: cli.Credentials.ID,
Secret: base64.URLEncoding.EncodeToString(secret), Secret: cli.Credentials.Secret,
} }
return &cc, nil return &cc, nil
} }
func (r *clientIdentityRepo) All() ([]oidc.ClientIdentity, error) { func (r *clientRepo) All() ([]client.Client, error) {
qt := r.quote(clientIdentityTableName) qt := r.quote(clientTableName)
q := fmt.Sprintf("SELECT * FROM %s", qt) q := fmt.Sprintf("SELECT * FROM %s", qt)
objs, err := r.executor(nil).Select(&clientIdentityModel{}, q) objs, err := r.executor(nil).Select(&clientModel{}, q)
if err != nil { if err != nil {
return nil, err return nil, err
} }
cs := make([]oidc.ClientIdentity, len(objs)) cs := make([]client.Client, len(objs))
for i, obj := range objs { for i, obj := range objs {
m, ok := obj.(*clientIdentityModel) m, ok := obj.(*clientModel)
if !ok { if !ok {
return nil, errors.New("unable to cast client identity to clientIdentityModel") return nil, errors.New("unable to cast client identity to clientModel")
} }
ci, err := m.ClientIdentity() ci, err := m.Client()
if err != nil { if err != nil {
return nil, err return nil, err
} }

View file

@ -88,7 +88,7 @@ func TestMigrateClientMetadata(t *testing.T) {
} }
for i, tt := range tests { for i, tt := range tests {
model := &clientIdentityModel{ model := &clientModel{
ID: strconv.Itoa(i), ID: strconv.Itoa(i),
Secret: []byte("verysecret"), Secret: []byte("verysecret"),
Metadata: tt.before, Metadata: tt.before,
@ -108,12 +108,12 @@ func TestMigrateClientMetadata(t *testing.T) {
for i, tt := range tests { for i, tt := range tests {
id := strconv.Itoa(i) id := strconv.Itoa(i)
m, err := dbMap.Get(clientIdentityModel{}, id) m, err := dbMap.Get(clientModel{}, id)
if err != nil { if err != nil {
t.Errorf("case %d: failed to get model: %v", i, err) t.Errorf("case %d: failed to get model: %v", i, err)
continue continue
} }
cim, ok := m.(*clientIdentityModel) cim, ok := m.(*clientModel)
if !ok { if !ok {
t.Errorf("case %d: unrecognized model type: %T", i, m) t.Errorf("case %d: unrecognized model type: %T", i, m)
continue continue

View file

@ -11,10 +11,10 @@ import (
"github.com/go-gorp/gorp" "github.com/go-gorp/gorp"
"golang.org/x/crypto/bcrypt" "golang.org/x/crypto/bcrypt"
"github.com/coreos/dex/client"
"github.com/coreos/dex/pkg/log" "github.com/coreos/dex/pkg/log"
"github.com/coreos/dex/refresh" "github.com/coreos/dex/refresh"
"github.com/coreos/dex/repo" "github.com/coreos/dex/repo"
"github.com/coreos/go-oidc/oidc"
) )
const ( const (
@ -186,19 +186,19 @@ func (r *refreshTokenRepo) RevokeTokensForClient(userID, clientID string) error
return err return err
} }
func (r *refreshTokenRepo) ClientsWithRefreshTokens(userID string) ([]oidc.ClientIdentity, error) { func (r *refreshTokenRepo) ClientsWithRefreshTokens(userID string) ([]client.Client, error) {
q := `SELECT c.* FROM %s as c q := `SELECT c.* FROM %s as c
INNER JOIN %s as r ON c.id = r.client_id WHERE r.user_id = $1;` INNER JOIN %s as r ON c.id = r.client_id WHERE r.user_id = $1;`
q = fmt.Sprintf(q, r.quote(clientIdentityTableName), r.quote(refreshTokenTableName)) q = fmt.Sprintf(q, r.quote(clientTableName), r.quote(refreshTokenTableName))
var clients []clientIdentityModel var clients []clientModel
if _, err := r.executor(nil).Select(&clients, q, userID); err != nil { if _, err := r.executor(nil).Select(&clients, q, userID); err != nil {
return nil, err return nil, err
} }
c := make([]oidc.ClientIdentity, len(clients)) c := make([]client.Client, len(clients))
for i, client := range clients { for i, client := range clients {
ident, err := client.ClientIdentity() ident, err := client.Client()
if err != nil { if err != nil {
return nil, err return nil, err
} }

View file

@ -181,8 +181,8 @@ func TestDBPrivateKeySetRepoSetGet(t *testing.T) {
} }
} }
func TestDBClientIdentityRepoMetadata(t *testing.T) { func TestDBClientRepoMetadata(t *testing.T) {
r := db.NewClientIdentityRepo(connect(t)) r := db.NewClientRepo(connect(t))
cm := oidc.ClientMetadata{ cm := oidc.ClientMetadata{
RedirectURIs: []url.URL{ RedirectURIs: []url.URL{
@ -191,7 +191,12 @@ func TestDBClientIdentityRepoMetadata(t *testing.T) {
}, },
} }
_, err := r.New("foo", cm, false) _, err := r.New(client.Client{
Credentials: oidc.ClientCredentials{
ID: "foo",
},
Metadata: cm,
})
if err != nil { if err != nil {
t.Fatalf(err.Error()) t.Fatalf(err.Error())
} }
@ -206,8 +211,8 @@ func TestDBClientIdentityRepoMetadata(t *testing.T) {
} }
} }
func TestDBClientIdentityRepoMetadataNoExist(t *testing.T) { func TestDBClientRepoMetadataNoExist(t *testing.T) {
r := db.NewClientIdentityRepo(connect(t)) r := db.NewClientRepo(connect(t))
got, err := r.Metadata("noexist") got, err := r.Metadata("noexist")
if err != client.ErrorNotFound { if err != client.ErrorNotFound {
@ -218,8 +223,8 @@ func TestDBClientIdentityRepoMetadataNoExist(t *testing.T) {
} }
} }
func TestDBClientIdentityRepoNewDuplicate(t *testing.T) { func TestDBClientRepoNewDuplicate(t *testing.T) {
r := db.NewClientIdentityRepo(connect(t)) r := db.NewClientRepo(connect(t))
meta1 := oidc.ClientMetadata{ meta1 := oidc.ClientMetadata{
RedirectURIs: []url.URL{ RedirectURIs: []url.URL{
@ -227,7 +232,12 @@ func TestDBClientIdentityRepoNewDuplicate(t *testing.T) {
}, },
} }
if _, err := r.New("foo", meta1, false); err != nil { if _, err := r.New(client.Client{
Credentials: oidc.ClientCredentials{
ID: "foo",
},
Metadata: meta1,
}); err != nil {
t.Fatalf("unexpected error: %v", err) t.Fatalf("unexpected error: %v", err)
} }
@ -237,13 +247,54 @@ func TestDBClientIdentityRepoNewDuplicate(t *testing.T) {
}, },
} }
if _, err := r.New("foo", meta2, false); err == nil { if _, err := r.New(client.Client{
Credentials: oidc.ClientCredentials{
ID: "foo",
},
Metadata: meta2,
}); err == nil {
t.Fatalf("expected non-nil error") t.Fatalf("expected non-nil error")
} }
} }
func TestDBClientIdentityRepoAuthenticate(t *testing.T) { func TestDBClientRepoNewAdmin(t *testing.T) {
r := db.NewClientIdentityRepo(connect(t))
for _, admin := range []bool{true, false} {
r := db.NewClientRepo(connect(t))
if _, err := r.New(client.Client{
Credentials: oidc.ClientCredentials{
ID: "foo",
},
Metadata: oidc.ClientMetadata{
RedirectURIs: []url.URL{
url.URL{Scheme: "http", Host: "foo.example.com"},
},
},
Admin: admin,
}); err != nil {
t.Fatalf("expected non-nil error: %v", err)
}
gotAdmin, err := r.IsDexAdmin("foo")
if err != nil {
t.Fatalf("expected non-nil error")
}
if gotAdmin != admin {
t.Errorf("want=%v, gotAdmin=%v", admin, gotAdmin)
}
cli, err := r.Get("foo")
if err != nil {
t.Fatalf("expected non-nil error")
}
if cli.Admin != admin {
t.Errorf("want=%v, cli.Admin=%v", admin, cli.Admin)
}
}
}
func TestDBClientRepoAuthenticate(t *testing.T) {
r := db.NewClientRepo(connect(t))
cm := oidc.ClientMetadata{ cm := oidc.ClientMetadata{
RedirectURIs: []url.URL{ RedirectURIs: []url.URL{
@ -251,7 +302,12 @@ func TestDBClientIdentityRepoAuthenticate(t *testing.T) {
}, },
} }
cc, err := r.New("baz", cm, false) cc, err := r.New(client.Client{
Credentials: oidc.ClientCredentials{
ID: "baz",
},
Metadata: cm,
})
if err != nil { if err != nil {
t.Fatalf(err.Error()) t.Fatalf(err.Error())
} }
@ -290,8 +346,8 @@ func TestDBClientIdentityRepoAuthenticate(t *testing.T) {
} }
} }
func TestDBClientIdentityAll(t *testing.T) { func TestDBClientAll(t *testing.T) {
r := db.NewClientIdentityRepo(connect(t)) r := db.NewClientRepo(connect(t))
cm := oidc.ClientMetadata{ cm := oidc.ClientMetadata{
RedirectURIs: []url.URL{ RedirectURIs: []url.URL{
@ -299,7 +355,12 @@ func TestDBClientIdentityAll(t *testing.T) {
}, },
} }
_, err := r.New("foo", cm, false) _, err := r.New(client.Client{
Credentials: oidc.ClientCredentials{
ID: "foo",
},
Metadata: cm,
})
if err != nil { if err != nil {
t.Fatalf(err.Error()) t.Fatalf(err.Error())
} }
@ -322,7 +383,12 @@ func TestDBClientIdentityAll(t *testing.T) {
url.URL{Scheme: "http", Host: "foo.com", Path: "/cb"}, url.URL{Scheme: "http", Host: "foo.com", Path: "/cb"},
}, },
} }
_, err = r.New("bar", cm, false) _, err = r.New(client.Client{
Credentials: oidc.ClientCredentials{
ID: "bar",
},
Metadata: cm,
})
if err != nil { if err != nil {
t.Fatalf(err.Error()) t.Fatalf(err.Error())
} }

View file

@ -14,8 +14,8 @@ import (
) )
var ( var (
testClients = []oidc.ClientIdentity{ testClients = []client.Client{
oidc.ClientIdentity{ client.Client{
Credentials: oidc.ClientCredentials{ Credentials: oidc.ClientCredentials{
ID: "client1", ID: "client1",
Secret: base64.URLEncoding.EncodeToString([]byte("secret-1")), Secret: base64.URLEncoding.EncodeToString([]byte("secret-1")),
@ -30,7 +30,7 @@ var (
}, },
}, },
}, },
oidc.ClientIdentity{ client.Client{
Credentials: oidc.ClientCredentials{ Credentials: oidc.ClientCredentials{
ID: "client2", ID: "client2",
Secret: base64.URLEncoding.EncodeToString([]byte("secret-2")), Secret: base64.URLEncoding.EncodeToString([]byte("secret-2")),
@ -48,7 +48,7 @@ var (
} }
) )
func newClientIdentityRepo(t *testing.T) client.ClientIdentityRepo { func newClientRepo(t *testing.T) client.ClientRepo {
dsn := os.Getenv("DEX_TEST_DSN") dsn := os.Getenv("DEX_TEST_DSN")
var dbMap *gorp.DbMap var dbMap *gorp.DbMap
if dsn == "" { if dsn == "" {
@ -56,7 +56,7 @@ func newClientIdentityRepo(t *testing.T) client.ClientIdentityRepo {
} else { } else {
dbMap = connect(t) dbMap = connect(t)
} }
repo, err := db.NewClientIdentityRepoFromClients(dbMap, testClients) repo, err := db.NewClientRepoFromClients(dbMap, testClients)
if err != nil { if err != nil {
t.Fatalf("failed to create client repo from clients: %v", err) t.Fatalf("failed to create client repo from clients: %v", err)
} }
@ -101,7 +101,7 @@ func TestGetSetAdminClient(t *testing.T) {
Tests: Tests:
for i, tt := range tests { for i, tt := range tests {
repo := newClientIdentityRepo(t) repo := newClientRepo(t)
for _, cid := range startAdmins { for _, cid := range startAdmins {
err := repo.SetDexAdmin(cid, true) err := repo.SetDexAdmin(cid, true)
if err != nil { if err != nil {

View file

@ -11,12 +11,13 @@ import (
"github.com/go-gorp/gorp" "github.com/go-gorp/gorp"
"github.com/kylelemons/godebug/pretty" "github.com/kylelemons/godebug/pretty"
"github.com/coreos/dex/client"
"github.com/coreos/dex/db" "github.com/coreos/dex/db"
"github.com/coreos/dex/refresh" "github.com/coreos/dex/refresh"
"github.com/coreos/dex/user" "github.com/coreos/dex/user"
) )
func newRefreshRepo(t *testing.T, users []user.UserWithRemoteIdentities, clients []oidc.ClientIdentity) refresh.RefreshTokenRepo { func newRefreshRepo(t *testing.T, users []user.UserWithRemoteIdentities, clients []client.Client) refresh.RefreshTokenRepo {
var dbMap *gorp.DbMap var dbMap *gorp.DbMap
if dsn := os.Getenv("DEX_TEST_DSN"); dsn == "" { if dsn := os.Getenv("DEX_TEST_DSN"); dsn == "" {
dbMap = db.NewMemDB() dbMap = db.NewMemDB()
@ -26,7 +27,7 @@ func newRefreshRepo(t *testing.T, users []user.UserWithRemoteIdentities, clients
if _, err := db.NewUserRepoFromUsers(dbMap, users); err != nil { if _, err := db.NewUserRepoFromUsers(dbMap, users); err != nil {
t.Fatalf("Unable to add users: %v", err) t.Fatalf("Unable to add users: %v", err)
} }
if _, err := db.NewClientIdentityRepoFromClients(dbMap, clients); err != nil { if _, err := db.NewClientRepoFromClients(dbMap, clients); err != nil {
t.Fatalf("Unable to add clients: %v", err) t.Fatalf("Unable to add clients: %v", err)
} }
return db.NewRefreshTokenRepo(dbMap) return db.NewRefreshTokenRepo(dbMap)
@ -35,7 +36,7 @@ func newRefreshRepo(t *testing.T, users []user.UserWithRemoteIdentities, clients
func TestRefreshTokenRepo(t *testing.T) { func TestRefreshTokenRepo(t *testing.T) {
clientID := "client1" clientID := "client1"
userID := "user1" userID := "user1"
clients := []oidc.ClientIdentity{ clients := []client.Client{
{ {
Credentials: oidc.ClientCredentials{ Credentials: oidc.ClientCredentials{
ID: clientID, ID: clientID,

View file

@ -1,20 +1,23 @@
package integration package integration
import ( import (
"errors" "encoding/base64"
"fmt"
"net/http" "net/http"
"net/http/httptest" "net/http/httptest"
"net/url" "net/url"
"testing" "testing"
"github.com/coreos/go-oidc/oidc"
"github.com/kylelemons/godebug/pretty" "github.com/kylelemons/godebug/pretty"
"google.golang.org/api/googleapi" "google.golang.org/api/googleapi"
"github.com/coreos/dex/admin" "github.com/coreos/dex/admin"
"github.com/coreos/dex/client"
"github.com/coreos/dex/db"
"github.com/coreos/dex/schema/adminschema" "github.com/coreos/dex/schema/adminschema"
"github.com/coreos/dex/server" "github.com/coreos/dex/server"
"github.com/coreos/dex/user" "github.com/coreos/dex/user"
"github.com/coreos/go-oidc/oidc"
) )
const ( const (
@ -24,6 +27,7 @@ const (
type adminAPITestFixtures struct { type adminAPITestFixtures struct {
ur user.UserRepo ur user.UserRepo
pwr user.PasswordInfoRepo pwr user.PasswordInfoRepo
cr client.ClientRepo
adAPI *admin.AdminAPI adAPI *admin.AdminAPI
adSrv *server.AdminServer adSrv *server.AdminServer
hSrv *httptest.Server hSrv *httptest.Server
@ -78,9 +82,17 @@ func makeAdminAPITestFixtures() *adminAPITestFixtures {
f := &adminAPITestFixtures{} f := &adminAPITestFixtures{}
dbMap, ur, pwr, um := makeUserObjects(adminUsers, adminPasswords) dbMap, ur, pwr, um := makeUserObjects(adminUsers, adminPasswords)
var cliCount int
secGen := func() ([]byte, error) {
return []byte(fmt.Sprintf("client_%v", cliCount)), nil
}
cr := db.NewClientRepoWithSecretGenerator(dbMap, secGen)
f.cr = cr
f.ur = ur f.ur = ur
f.pwr = pwr f.pwr = pwr
f.adAPI = admin.NewAdminAPI(dbMap, um, "local") f.adAPI = admin.NewAdminAPI(ur, pwr, cr, um, "local")
f.adSrv = server.NewAdminServer(f.adAPI, nil, adminAPITestSecret) f.adSrv = server.NewAdminServer(f.adAPI, nil, adminAPITestSecret)
f.hSrv = httptest.NewServer(f.adSrv.HTTPHandler()) f.hSrv = httptest.NewServer(f.adSrv.HTTPHandler())
f.hc = &http.Client{ f.hc = &http.Client{
@ -256,47 +268,147 @@ func TestCreateAdmin(t *testing.T) {
} }
func TestCreateClient(t *testing.T) { func TestCreateClient(t *testing.T) {
oldGen := admin.ClientIDGenerator
admin.ClientIDGenerator = func(hostport string) (string, error) {
return fmt.Sprintf("client_%v", hostport), nil
}
defer func() {
admin.ClientIDGenerator = oldGen
}()
mustParseURL := func(s string) *url.URL {
u, err := url.Parse(s)
if err != nil {
t.Fatalf("couldn't parse URL: %v", err)
}
return u
}
addIDAndSecret := func(cli adminschema.Client) *adminschema.Client {
cli.Id = "client_auth.example.com"
cli.Secret = base64.URLEncoding.EncodeToString([]byte("client_0"))
return &cli
}
adminClientGood := adminschema.Client{
RedirectURIs: []string{"https://auth.example.com/"},
}
clientGood := client.Client{
Credentials: oidc.ClientCredentials{
ID: "client_auth.example.com",
},
Metadata: oidc.ClientMetadata{
RedirectURIs: []url.URL{*mustParseURL("https://auth.example.com/")},
},
}
adminAdminClient := adminClientGood
adminAdminClient.IsAdmin = true
clientGoodAdmin := clientGood
clientGoodAdmin.Admin = true
adminMultiRedirect := adminClientGood
adminMultiRedirect.RedirectURIs = []string{"https://auth.example.com/", "https://auth2.example.com/"}
clientMultiRedirect := clientGoodAdmin
clientMultiRedirect.Metadata.RedirectURIs = append(
clientMultiRedirect.Metadata.RedirectURIs,
*mustParseURL("https://auth2.example.com/"))
tests := []struct { tests := []struct {
client oidc.ClientMetadata req adminschema.ClientCreateRequest
wantError bool want adminschema.ClientCreateResponse
wantClient client.Client
wantError int
}{ }{
{ {
client: oidc.ClientMetadata{}, req: adminschema.ClientCreateRequest{},
wantError: true, wantError: http.StatusBadRequest,
}, },
{ {
client: oidc.ClientMetadata{ req: adminschema.ClientCreateRequest{
RedirectURIs: []url.URL{ Client: &adminschema.Client{
{Scheme: "https", Host: "auth.example.com", Path: "/"}, IsAdmin: true,
}, },
}, },
wantError: http.StatusBadRequest,
},
{
req: adminschema.ClientCreateRequest{
Client: &adminschema.Client{
RedirectURIs: []string{"909090"},
},
},
wantError: http.StatusBadRequest,
},
{
req: adminschema.ClientCreateRequest{
Client: &adminClientGood,
},
want: adminschema.ClientCreateResponse{
Client: addIDAndSecret(adminClientGood),
},
wantClient: clientGood,
},
{
req: adminschema.ClientCreateRequest{
Client: &adminAdminClient,
},
want: adminschema.ClientCreateResponse{
Client: addIDAndSecret(adminAdminClient),
},
wantClient: clientGoodAdmin,
},
{
req: adminschema.ClientCreateRequest{
Client: &adminMultiRedirect,
},
want: adminschema.ClientCreateResponse{
Client: addIDAndSecret(adminMultiRedirect),
},
wantClient: clientMultiRedirect,
}, },
} }
for i, tt := range tests { for i, tt := range tests {
err := func() error { if i != 3 {
f := makeAdminAPITestFixtures() continue
req := &adminschema.ClientCreateRequestClient{} }
for _, redirectURI := range tt.client.RedirectURIs { f := makeAdminAPITestFixtures()
req.Redirect_uris = append(req.Redirect_uris, redirectURI.String())
resp, err := f.adClient.Client.Create(&tt.req).Do()
if tt.wantError != 0 {
if err == nil {
t.Errorf("case %d: want non-nil error.", i)
continue
} }
resp, err := f.adClient.Client.Create(&adminschema.ClientCreateRequest{Client: req}).Do()
if err != nil { aErr, ok := err.(*googleapi.Error)
if tt.wantError { if !ok {
return nil t.Errorf("case %d: could not assert as adminSchema.Error: %v", i, err)
} continue
return err
} }
if resp.Client_id == "" { if aErr.Code != tt.wantError {
return errors.New("no client id returned") t.Errorf("case %d: want aErr.Code=%v, got %v", i, tt.wantError, aErr.Code)
continue
} }
if resp.Client_secret == "" { continue
return errors.New("no client secret returned") }
}
return nil
}()
if err != nil { if err != nil {
t.Errorf("case %d: %v", i, err) t.Errorf("case %d: unexpected error creating client: %v", i, err)
continue
}
if diff := pretty.Compare(tt.want, resp); diff != "" {
t.Errorf("case %d: Compare(want, got) = %v", i, diff)
}
repoClient, err := f.cr.Get(resp.Client.Id)
if err != nil {
t.Errorf("case %d: Unexpected error getting client: %v", i, err)
}
if diff := pretty.Compare(tt.wantClient, repoClient); diff != "" {
t.Errorf("case %d: Compare(wantClient, repoClient) = %v", i, diff)
} }
} }
} }

View file

@ -7,12 +7,13 @@ import (
"reflect" "reflect"
"testing" "testing"
"github.com/coreos/dex/client"
schema "github.com/coreos/dex/schema/workerschema" schema "github.com/coreos/dex/schema/workerschema"
"github.com/coreos/go-oidc/oidc" "github.com/coreos/go-oidc/oidc"
) )
func TestClientCreate(t *testing.T) { func TestClientCreate(t *testing.T) {
ci := oidc.ClientIdentity{ ci := client.Client{
Credentials: oidc.ClientCredentials{ Credentials: oidc.ClientCredentials{
ID: "72de74a9", ID: "72de74a9",
Secret: base64.URLEncoding.EncodeToString([]byte("XXX")), Secret: base64.URLEncoding.EncodeToString([]byte("XXX")),
@ -23,7 +24,7 @@ func TestClientCreate(t *testing.T) {
}, },
}, },
} }
cis := []oidc.ClientIdentity{ci} cis := []client.Client{ci}
srv, err := mockServer(cis) srv, err := mockServer(cis)
if err != nil { if err != nil {
@ -72,7 +73,7 @@ func TestClientCreate(t *testing.T) {
t.Error("Expected non-empty Client Secret") t.Error("Expected non-empty Client Secret")
} }
meta, err := srv.ClientIdentityRepo.Metadata(newClient.Id) meta, err := srv.ClientRepo.Metadata(newClient.Id)
if err != nil { if err != nil {
t.Errorf("Error looking up client metadata: %v", err) t.Errorf("Error looking up client metadata: %v", err)
} else if meta == nil { } else if meta == nil {

View file

@ -9,6 +9,7 @@ import (
"testing" "testing"
"time" "time"
"github.com/coreos/dex/client"
"github.com/coreos/dex/connector" "github.com/coreos/dex/connector"
"github.com/coreos/dex/db" "github.com/coreos/dex/db"
phttp "github.com/coreos/dex/pkg/http" phttp "github.com/coreos/dex/pkg/http"
@ -22,7 +23,7 @@ import (
"github.com/coreos/go-oidc/oidc" "github.com/coreos/go-oidc/oidc"
) )
func mockServer(cis []oidc.ClientIdentity) (*server.Server, error) { func mockServer(cis []client.Client) (*server.Server, error) {
dbMap := db.NewMemDB() dbMap := db.NewMemDB()
k, err := key.GeneratePrivateKey() k, err := key.GeneratePrivateKey()
if err != nil { if err != nil {
@ -34,23 +35,23 @@ func mockServer(cis []oidc.ClientIdentity) (*server.Server, error) {
if err != nil { if err != nil {
return nil, err return nil, err
} }
clientIdentityRepo, err := db.NewClientIdentityRepoFromClients(dbMap, cis) clientRepo, err := db.NewClientRepoFromClients(dbMap, cis)
if err != nil { if err != nil {
return nil, err return nil, err
} }
sm := manager.NewSessionManager(db.NewSessionRepo(dbMap), db.NewSessionKeyRepo(dbMap)) sm := manager.NewSessionManager(db.NewSessionRepo(dbMap), db.NewSessionKeyRepo(dbMap))
srv := &server.Server{ srv := &server.Server{
IssuerURL: url.URL{Scheme: "http", Host: "server.example.com"}, IssuerURL: url.URL{Scheme: "http", Host: "server.example.com"},
KeyManager: km, KeyManager: km,
ClientIdentityRepo: clientIdentityRepo, ClientRepo: clientRepo,
SessionManager: sm, SessionManager: sm,
} }
return srv, nil return srv, nil
} }
func mockClient(srv *server.Server, ci oidc.ClientIdentity) (*oidc.Client, error) { func mockClient(srv *server.Server, ci client.Client) (*oidc.Client, error) {
hdlr := srv.HTTPHandler() hdlr := srv.HTTPHandler()
sClient := &phttp.HandlerClient{Handler: hdlr} sClient := &phttp.HandlerClient{Handler: hdlr}
@ -75,7 +76,7 @@ func mockClient(srv *server.Server, ci oidc.ClientIdentity) (*oidc.Client, error
return oidc.NewClient(ccfg) return oidc.NewClient(ccfg)
} }
func verifyUserClaims(claims jose.Claims, ci *oidc.ClientIdentity, user *user.User, issuerURL url.URL) error { func verifyUserClaims(claims jose.Claims, ci *client.Client, user *user.User, issuerURL url.URL) error {
expectedSub, expectedName := ci.Credentials.ID, ci.Credentials.ID expectedSub, expectedName := ci.Credentials.ID, ci.Credentials.ID
if user != nil { if user != nil {
expectedSub, expectedName = user.ID, user.DisplayName expectedSub, expectedName = user.ID, user.DisplayName
@ -116,7 +117,7 @@ func TestHTTPExchangeTokenRefreshToken(t *testing.T) {
ID: "local", ID: "local",
} }
ci := oidc.ClientIdentity{ ci := client.Client{
Credentials: oidc.ClientCredentials{ Credentials: oidc.ClientCredentials{
ID: "72de74a9", ID: "72de74a9",
Secret: base64.URLEncoding.EncodeToString([]byte("XXX")), Secret: base64.URLEncoding.EncodeToString([]byte("XXX")),
@ -124,7 +125,7 @@ func TestHTTPExchangeTokenRefreshToken(t *testing.T) {
} }
dbMap := db.NewMemDB() dbMap := db.NewMemDB()
cir, err := db.NewClientIdentityRepoFromClients(dbMap, []oidc.ClientIdentity{ci}) cir, err := db.NewClientRepoFromClients(dbMap, []client.Client{ci})
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())
} }
@ -160,15 +161,15 @@ func TestHTTPExchangeTokenRefreshToken(t *testing.T) {
refreshTokenRepo := refreshtest.NewTestRefreshTokenRepo() refreshTokenRepo := refreshtest.NewTestRefreshTokenRepo()
srv := &server.Server{ srv := &server.Server{
IssuerURL: issuerURL, IssuerURL: issuerURL,
KeyManager: km, KeyManager: km,
SessionManager: sm, SessionManager: sm,
ClientIdentityRepo: cir, ClientRepo: cir,
Templates: template.New(connector.LoginPageTemplateName), Templates: template.New(connector.LoginPageTemplateName),
Connectors: []connector.Connector{}, Connectors: []connector.Connector{},
UserRepo: userRepo, UserRepo: userRepo,
PasswordInfoRepo: passwordInfoRepo, PasswordInfoRepo: passwordInfoRepo,
RefreshTokenRepo: refreshTokenRepo, RefreshTokenRepo: refreshTokenRepo,
} }
if err = srv.AddConnector(cfg); err != nil { if err = srv.AddConnector(cfg); err != nil {
@ -262,13 +263,13 @@ func TestHTTPExchangeTokenRefreshToken(t *testing.T) {
} }
func TestHTTPClientCredsToken(t *testing.T) { func TestHTTPClientCredsToken(t *testing.T) {
ci := oidc.ClientIdentity{ ci := client.Client{
Credentials: oidc.ClientCredentials{ Credentials: oidc.ClientCredentials{
ID: "72de74a9", ID: "72de74a9",
Secret: base64.URLEncoding.EncodeToString([]byte("XXX")), Secret: base64.URLEncoding.EncodeToString([]byte("XXX")),
}, },
} }
cis := []oidc.ClientIdentity{ci} cis := []client.Client{ci}
srv, err := mockServer(cis) srv, err := mockServer(cis)
if err != nil { if err != nil {

View file

@ -101,9 +101,9 @@ func makeUserAPITestFixtures() *userAPITestFixtures {
f := &userAPITestFixtures{} f := &userAPITestFixtures{}
dbMap, _, _, um := makeUserObjects(userUsers, userPasswords) dbMap, _, _, um := makeUserObjects(userUsers, userPasswords)
cir := func() client.ClientIdentityRepo { cir := func() client.ClientRepo {
repo, err := db.NewClientIdentityRepoFromClients(dbMap, []oidc.ClientIdentity{ repo, err := db.NewClientRepoFromClients(dbMap, []client.Client{
oidc.ClientIdentity{ client.Client{
Credentials: oidc.ClientCredentials{ Credentials: oidc.ClientCredentials{
ID: testClientID, ID: testClientID,
Secret: testClientSecret, Secret: testClientSecret,
@ -114,7 +114,7 @@ func makeUserAPITestFixtures() *userAPITestFixtures {
}, },
}, },
}, },
oidc.ClientIdentity{ client.Client{
Credentials: oidc.ClientCredentials{ Credentials: oidc.ClientCredentials{
ID: userBadClientID, ID: userBadClientID,
Secret: base64.URLEncoding.EncodeToString([]byte("secret")), Secret: base64.URLEncoding.EncodeToString([]byte("secret")),

View file

@ -4,7 +4,7 @@ import (
"crypto/rand" "crypto/rand"
"errors" "errors"
"github.com/coreos/go-oidc/oidc" "github.com/coreos/dex/client"
) )
const ( const (
@ -54,5 +54,5 @@ type RefreshTokenRepo interface {
RevokeTokensForClient(userID, clientID string) error RevokeTokensForClient(userID, clientID string) error
// ClientsWithRefreshTokens returns a list of all clients the user has an outstanding client with. // ClientsWithRefreshTokens returns a list of all clients the user has an outstanding client with.
ClientsWithRefreshTokens(userID string) ([]oidc.ClientIdentity, error) ClientsWithRefreshTokens(userID string) ([]client.Client, error)
} }

View file

@ -20,32 +20,41 @@ __Version:__ v1
} }
``` ```
### Client
```
{
clientName: string // OPTIONAL. Name of the Client to be presented to the End-User. If desired, representation of this Claim in different languages and scripts is represented as described in Section 2.1 ( Metadata Languages and Scripts ) .,
clientURI: string // OPTIONAL. URL of the home page of the Client. The value of this field MUST point to a valid Web page. If present, the server SHOULD display this URL to the End-User in a followable fashion. If desired, representation of this Claim in different languages and scripts is represented as described in Section 2.1 ( Metadata Languages and Scripts ) .,
id: string // The client ID. Ignored in client create requests.,
isAdmin: boolean,
logoURI: string // OPTIONAL. URL that references a logo for the Client application. If present, the server SHOULD display this image to the End-User during approval. The value of this field MUST point to a valid image file. If desired, representation of this Claim in different languages and scripts is represented as described in Section 2.1 ( Metadata Languages and Scripts ) .,
redirectURIs: [
string
],
secret: string
}
```
### ClientCreateRequest ### ClientCreateRequest
A request to register a client with dex. A request to register a client with dex.
``` ```
{ {
client: { client: Client
client_name: string // OPTIONAL. Name of the Client to be presented to the End-User. If desired, representation of this Claim in different languages and scripts is represented as described in Section 2.1 ( Metadata Languages and Scripts ) .,
client_uri: string // OPTIONAL. URL of the home page of the Client. The value of this field MUST point to a valid Web page. If present, the server SHOULD display this URL to the End-User in a followable fashion. If desired, representation of this Claim in different languages and scripts is represented as described in Section 2.1 ( Metadata Languages and Scripts ) .,
logo_uri: string // OPTIONAL. URL that references a logo for the Client application. If present, the server SHOULD display this image to the End-User during approval. The value of this field MUST point to a valid image file. If desired, representation of this Claim in different languages and scripts is represented as described in Section 2.1 ( Metadata Languages and Scripts ) .,
redirect_uris: [
string
]
},
isAdmin: boolean
} }
``` ```
### ClientRegistrationResponse ### ClientCreateResponse
Upon successful registration, an ID and secret is assigned to the client. Upon successful registration, an ID and secret is assigned to the client.
``` ```
{ {
client_id: string, client: Client
client_secret: string
} }
``` ```
@ -137,7 +146,7 @@ Upon successful registration, an ID and secret is assigned to the client.
> |Code|Description|Type| > |Code|Description|Type|
|:-----|:-----|:-----| |:-----|:-----|:-----|
| 200 | | [ClientRegistrationResponse](#clientregistrationresponse) | | 200 | | [ClientCreateResponse](#clientcreateresponse) |
| default | Unexpected error | | | default | Unexpected error | |

View file

@ -0,0 +1,83 @@
package adminschema
import (
"errors"
"net/url"
"github.com/coreos/dex/client"
"github.com/coreos/go-oidc/oidc"
)
var (
ErrorNoRedirectURI = errors.New("No Redirect URIs")
ErrorInvalidRedirectURI = errors.New("Invalid Redirect URI")
ErrorInvalidLogoURI = errors.New("Invalid Logo URI")
ErrorInvalidClientURI = errors.New("Invalid Client URI")
)
func MapSchemaClientToClient(sc Client) (client.Client, error) {
c := client.Client{
Credentials: oidc.ClientCredentials{
ID: sc.Id,
Secret: sc.Secret,
},
Metadata: oidc.ClientMetadata{
RedirectURIs: make([]url.URL, len(sc.RedirectURIs)),
},
}
for i, ru := range sc.RedirectURIs {
if ru == "" {
return client.Client{}, ErrorNoRedirectURI
}
u, err := url.Parse(ru)
if err != nil {
return client.Client{}, ErrorInvalidRedirectURI
}
c.Metadata.RedirectURIs[i] = *u
}
c.Metadata.ClientName = sc.ClientName
if sc.LogoURI != "" {
logoURI, err := url.Parse(sc.LogoURI)
if err != nil {
return client.Client{}, ErrorInvalidLogoURI
}
c.Metadata.LogoURI = logoURI
}
if sc.ClientURI != "" {
clientURI, err := url.Parse(sc.ClientURI)
if err != nil {
return client.Client{}, ErrorInvalidClientURI
}
c.Metadata.ClientURI = clientURI
}
c.Admin = sc.IsAdmin
return c, nil
}
func MapClientToSchemaClient(c client.Client) Client {
cl := Client{
Id: c.Credentials.ID,
Secret: c.Credentials.Secret,
RedirectURIs: make([]string, len(c.Metadata.RedirectURIs)),
}
for i, u := range c.Metadata.RedirectURIs {
cl.RedirectURIs[i] = u.String()
}
cl.ClientName = c.Metadata.ClientName
if c.Metadata.LogoURI != nil {
cl.LogoURI = c.Metadata.LogoURI.String()
}
if c.Metadata.ClientURI != nil {
cl.ClientURI = c.Metadata.ClientURI.String()
}
cl.IsAdmin = c.Admin
return cl
}

View file

@ -0,0 +1,129 @@
package adminschema
import (
"net/url"
"testing"
"github.com/coreos/go-oidc/oidc"
"github.com/kylelemons/godebug/pretty"
"github.com/coreos/dex/client"
)
func TestMapSchemaClientToClient(t *testing.T) {
tests := []struct {
sc Client
want client.Client
wantErr bool
}{
{
sc: Client{
Id: "123",
Secret: "sec_123",
RedirectURIs: []string{
"https://client.example.com",
"https://client2.example.com",
},
ClientName: "Bill",
LogoURI: "https://logo.example.com",
ClientURI: "https://clientURI.example.com",
},
want: client.Client{
Credentials: oidc.ClientCredentials{
ID: "123",
Secret: "sec_123",
},
Metadata: oidc.ClientMetadata{
RedirectURIs: []url.URL{
*mustParseURL(t, "https://client.example.com"),
*mustParseURL(t, "https://client2.example.com"),
},
ClientName: "Bill",
LogoURI: mustParseURL(t, "https://logo.example.com"),
ClientURI: mustParseURL(t, "https://clientURI.example.com"),
},
},
}, {
sc: Client{
Id: "123",
Secret: "sec_123",
RedirectURIs: []string{
"ht.d://p * * *",
},
},
wantErr: true,
},
}
for i, tt := range tests {
got, err := MapSchemaClientToClient(tt.sc)
if tt.wantErr {
if err == nil {
t.Errorf("case %d: want non-nil error", i)
t.Logf(pretty.Sprint(got))
}
continue
}
if err != nil {
t.Errorf("case %d: unexpected error mapping: %v", i, err)
}
if diff := pretty.Compare(tt.want, got); diff != "" {
t.Errorf("case %d: Compare(want, got): %v", i, diff)
}
}
}
func TestMapClientToClientSchema(t *testing.T) {
tests := []struct {
c client.Client
want Client
}{
{
want: Client{
Id: "123",
Secret: "sec_123",
RedirectURIs: []string{
"https://client.example.com",
"https://client2.example.com",
},
ClientName: "Bill",
LogoURI: "https://logo.example.com",
ClientURI: "https://clientURI.example.com",
},
c: client.Client{
Credentials: oidc.ClientCredentials{
ID: "123",
Secret: "sec_123",
},
Metadata: oidc.ClientMetadata{
RedirectURIs: []url.URL{
*mustParseURL(t, "https://client.example.com"),
*mustParseURL(t, "https://client2.example.com"),
},
ClientName: "Bill",
LogoURI: mustParseURL(t, "https://logo.example.com"),
ClientURI: mustParseURL(t, "https://clientURI.example.com"),
},
},
},
}
for i, tt := range tests {
got := MapClientToSchemaClient(tt.c)
if diff := pretty.Compare(tt.want, got); diff != "" {
t.Errorf("case %d: Compare(want, got): %v", i, diff)
}
}
}
func mustParseURL(t *testing.T, s string) *url.URL {
u, err := url.Parse(s)
if err != nil {
t.Fatalf("Cannot parse %v as url: %v", s, err)
}
return u
}

View file

@ -97,49 +97,52 @@ type Admin struct {
Password string `json:"password,omitempty"` Password string `json:"password,omitempty"`
} }
type ClientCreateRequest struct { type Client struct {
Client *ClientCreateRequestClient `json:"client,omitempty"` // ClientName: OPTIONAL. Name of the Client to be presented to the
IsAdmin bool `json:"isAdmin,omitempty"`
}
type ClientCreateRequestClient struct {
// Client_name: OPTIONAL. Name of the Client to be presented to the
// End-User. If desired, representation of this Claim in different // End-User. If desired, representation of this Claim in different
// languages and scripts is represented as described in Section 2.1 ( // languages and scripts is represented as described in Section 2.1 (
// Metadata Languages and Scripts ) . // Metadata Languages and Scripts ) .
Client_name string `json:"client_name,omitempty"` ClientName string `json:"clientName,omitempty"`
// Client_uri: OPTIONAL. URL of the home page of the Client. The value // ClientURI: OPTIONAL. URL of the home page of the Client. The value of
// of this field MUST point to a valid Web page. If present, the server // this field MUST point to a valid Web page. If present, the server
// SHOULD display this URL to the End-User in a followable fashion. If // SHOULD display this URL to the End-User in a followable fashion. If
// desired, representation of this Claim in different languages and // desired, representation of this Claim in different languages and
// scripts is represented as described in Section 2.1 ( Metadata // scripts is represented as described in Section 2.1 ( Metadata
// Languages and Scripts ) . // Languages and Scripts ) .
Client_uri string `json:"client_uri,omitempty"` ClientURI string `json:"clientURI,omitempty"`
// Logo_uri: OPTIONAL. URL that references a logo for the Client // Id: The client ID. Ignored in client create requests.
Id string `json:"id,omitempty"`
IsAdmin bool `json:"isAdmin,omitempty"`
// LogoURI: OPTIONAL. URL that references a logo for the Client
// application. If present, the server SHOULD display this image to the // application. If present, the server SHOULD display this image to the
// End-User during approval. The value of this field MUST point to a // End-User during approval. The value of this field MUST point to a
// valid image file. If desired, representation of this Claim in // valid image file. If desired, representation of this Claim in
// different languages and scripts is represented as described in // different languages and scripts is represented as described in
// Section 2.1 ( Metadata Languages and Scripts ) . // Section 2.1 ( Metadata Languages and Scripts ) .
Logo_uri string `json:"logo_uri,omitempty"` LogoURI string `json:"logoURI,omitempty"`
// Redirect_uris: REQUIRED. Array of Redirection URI values used by the // RedirectURIs: REQUIRED. Array of Redirection URI values used by the
// Client. One of these registered Redirection URI values MUST exactly // Client. One of these registered Redirection URI values MUST exactly
// match the redirect_uri parameter value used in each Authorization // match the redirect_uri parameter value used in each Authorization
// Request, with the matching performed as described in Section 6.2.1 of // Request, with the matching performed as described in Section 6.2.1 of
// [RFC3986] ( Berners-Lee, T., Fielding, R., and L. Masinter, // [RFC3986] ( Berners-Lee, T., Fielding, R., and L. Masinter,
// “Uniform Resource Identifier (URI): Generic Syntax,” January // “Uniform Resource Identifier (URI): Generic Syntax,” January
// 2005. ) (Simple String Comparison). // 2005. ) (Simple String Comparison).
Redirect_uris []string `json:"redirect_uris,omitempty"` RedirectURIs []string `json:"redirectURIs,omitempty"`
Secret string `json:"secret,omitempty"`
} }
type ClientRegistrationResponse struct { type ClientCreateRequest struct {
Client_id string `json:"client_id,omitempty"` Client *Client `json:"client,omitempty"`
}
Client_secret string `json:"client_secret,omitempty"` type ClientCreateResponse struct {
Client *Client `json:"client,omitempty"`
} }
type State struct { type State struct {
@ -310,7 +313,7 @@ func (c *ClientCreateCall) Fields(s ...googleapi.Field) *ClientCreateCall {
return c return c
} }
func (c *ClientCreateCall) Do() (*ClientRegistrationResponse, error) { func (c *ClientCreateCall) Do() (*ClientCreateResponse, error) {
var body io.Reader = nil var body io.Reader = nil
body, err := googleapi.WithoutDataWrapper.JSONReader(c.clientcreaterequest) body, err := googleapi.WithoutDataWrapper.JSONReader(c.clientcreaterequest)
if err != nil { if err != nil {
@ -336,7 +339,7 @@ func (c *ClientCreateCall) Do() (*ClientRegistrationResponse, error) {
if err := googleapi.CheckResponse(res); err != nil { if err := googleapi.CheckResponse(res); err != nil {
return nil, err return nil, err
} }
var ret *ClientRegistrationResponse var ret *ClientCreateResponse
if err := json.NewDecoder(res.Body).Decode(&ret); err != nil { if err := json.NewDecoder(res.Body).Decode(&ret); err != nil {
return nil, err return nil, err
} }
@ -350,7 +353,7 @@ func (c *ClientCreateCall) Do() (*ClientRegistrationResponse, error) {
// "$ref": "ClientCreateRequest" // "$ref": "ClientCreateRequest"
// }, // },
// "response": { // "response": {
// "$ref": "ClientRegistrationResponse" // "$ref": "ClientCreateResponse"
// } // }
// } // }

View file

@ -1,4 +1,5 @@
package adminschema package adminschema
// //
// This file is automatically generated by schema/generator // This file is automatically generated by schema/generator
// //
@ -51,53 +52,66 @@ const DiscoveryJSON = `{
} }
} }
}, },
"ClientCreateRequest": { "Client": {
"id": "ClientCreateRequest", "id": "Client",
"type": "object", "type": "object",
"description": "A request to register a client with dex.", "properties": {
"properties": { "id": {
"isAdmin": { "type": "string",
"type": "boolean" "description": "The client ID. Ignored in client create requests."
}, },
"client": { "secret": {
"type": "object", "type": "string",
"properties": { "description": "The client secret. Ignored in client create requests."
"redirect_uris": { },
"type": "array", "secret": {
"items": { "type": "string",
"type": "string" "format": "byte"
}, },
"description": "REQUIRED. Array of Redirection URI values used by the Client. One of these registered Redirection URI values MUST exactly match the redirect_uri parameter value used in each Authorization Request, with the matching performed as described in Section 6.2.1 of [RFC3986] ( Berners-Lee, T., Fielding, R., and L. Masinter, “Uniform Resource Identifier (URI): Generic Syntax,” January 2005. ) (Simple String Comparison)." "isAdmin": {
}, "type": "boolean"
"client_name": { },
"type": "string", "redirectURIs": {
"description": "OPTIONAL. Name of the Client to be presented to the End-User. If desired, representation of this Claim in different languages and scripts is represented as described in Section 2.1 ( Metadata Languages and Scripts ) ." "type": "array",
}, "items": {
"logo_uri": { "type": "string"
"type": "string", },
"description": "OPTIONAL. URL that references a logo for the Client application. If present, the server SHOULD display this image to the End-User during approval. The value of this field MUST point to a valid image file. If desired, representation of this Claim in different languages and scripts is represented as described in Section 2.1 ( Metadata Languages and Scripts ) ." "description": "REQUIRED. Array of Redirection URI values used by the Client. One of these registered Redirection URI values MUST exactly match the redirect_uri parameter value used in each Authorization Request, with the matching performed as described in Section 6.2.1 of [RFC3986] ( Berners-Lee, T., Fielding, R., and L. Masinter, “Uniform Resource Identifier (URI): Generic Syntax,” January 2005. ) (Simple String Comparison)."
}, },
"client_uri": { "clientName": {
"type": "string", "type": "string",
"description": "OPTIONAL. URL of the home page of the Client. The value of this field MUST point to a valid Web page. If present, the server SHOULD display this URL to the End-User in a followable fashion. If desired, representation of this Claim in different languages and scripts is represented as described in Section 2.1 ( Metadata Languages and Scripts ) ." "description": "OPTIONAL. Name of the Client to be presented to the End-User. If desired, representation of this Claim in different languages and scripts is represented as described in Section 2.1 ( Metadata Languages and Scripts ) ."
} },
} "logoURI": {
} "type": "string",
"description": "OPTIONAL. URL that references a logo for the Client application. If present, the server SHOULD display this image to the End-User during approval. The value of this field MUST point to a valid image file. If desired, representation of this Claim in different languages and scripts is represented as described in Section 2.1 ( Metadata Languages and Scripts ) ."
},
"clientURI": {
"type": "string",
"description": "OPTIONAL. URL of the home page of the Client. The value of this field MUST point to a valid Web page. If present, the server SHOULD display this URL to the End-User in a followable fashion. If desired, representation of this Claim in different languages and scripts is represented as described in Section 2.1 ( Metadata Languages and Scripts ) ."
} }
}
}, },
"ClientRegistrationResponse": { "ClientCreateRequest": {
"id": "ClientRegistrationResponse", "id": "ClientCreateRequest",
"type": "object", "type": "object",
"description": "Upon successful registration, an ID and secret is assigned to the client.", "description": "A request to register a client with dex.",
"properties": { "properties": {
"client_id": { "client": {
"type": "string" "$ref": "Client"
}, }
"client_secret": {
"type": "string"
}
}
} }
},
"ClientCreateResponse": {
"id": "ClientCreateResponse",
"type": "object",
"description": "Upon successful registration, an ID and secret is assigned to the client.",
"properties": {
"client":{
"$ref": "Client"
}
}
}
}, },
"resources": { "resources": {
"Admin": { "Admin": {
@ -160,7 +174,7 @@ const DiscoveryJSON = `{
"$ref": "ClientCreateRequest" "$ref": "ClientCreateRequest"
}, },
"response": { "response": {
"$ref": "ClientRegistrationResponse" "$ref": "ClientCreateResponse"
} }
} }
} }

View file

@ -45,53 +45,62 @@
} }
} }
}, },
"ClientCreateRequest": { "Client": {
"id": "ClientCreateRequest", "id": "Client",
"type": "object", "type": "object",
"description": "A request to register a client with dex.", "properties": {
"properties": { "id": {
"isAdmin": { "type": "string",
"type": "boolean" "description": "The client ID. Ignored in client create requests."
}, },
"client": { "secret": {
"type": "object", "type": "string",
"properties": { "description": "The client secret. Ignored in client create requests."
"redirect_uris": { },
"type": "array", "isAdmin": {
"items": { "type": "boolean"
"type": "string" },
}, "redirectURIs": {
"description": "REQUIRED. Array of Redirection URI values used by the Client. One of these registered Redirection URI values MUST exactly match the redirect_uri parameter value used in each Authorization Request, with the matching performed as described in Section 6.2.1 of [RFC3986] ( Berners-Lee, T., Fielding, R., and L. Masinter, “Uniform Resource Identifier (URI): Generic Syntax,” January 2005. ) (Simple String Comparison)." "type": "array",
}, "items": {
"client_name": { "type": "string"
"type": "string", },
"description": "OPTIONAL. Name of the Client to be presented to the End-User. If desired, representation of this Claim in different languages and scripts is represented as described in Section 2.1 ( Metadata Languages and Scripts ) ." "description": "REQUIRED. Array of Redirection URI values used by the Client. One of these registered Redirection URI values MUST exactly match the redirect_uri parameter value used in each Authorization Request, with the matching performed as described in Section 6.2.1 of [RFC3986] ( Berners-Lee, T., Fielding, R., and L. Masinter, “Uniform Resource Identifier (URI): Generic Syntax,” January 2005. ) (Simple String Comparison)."
}, },
"logo_uri": { "clientName": {
"type": "string", "type": "string",
"description": "OPTIONAL. URL that references a logo for the Client application. If present, the server SHOULD display this image to the End-User during approval. The value of this field MUST point to a valid image file. If desired, representation of this Claim in different languages and scripts is represented as described in Section 2.1 ( Metadata Languages and Scripts ) ." "description": "OPTIONAL. Name of the Client to be presented to the End-User. If desired, representation of this Claim in different languages and scripts is represented as described in Section 2.1 ( Metadata Languages and Scripts ) ."
}, },
"client_uri": { "logoURI": {
"type": "string", "type": "string",
"description": "OPTIONAL. URL of the home page of the Client. The value of this field MUST point to a valid Web page. If present, the server SHOULD display this URL to the End-User in a followable fashion. If desired, representation of this Claim in different languages and scripts is represented as described in Section 2.1 ( Metadata Languages and Scripts ) ." "description": "OPTIONAL. URL that references a logo for the Client application. If present, the server SHOULD display this image to the End-User during approval. The value of this field MUST point to a valid image file. If desired, representation of this Claim in different languages and scripts is represented as described in Section 2.1 ( Metadata Languages and Scripts ) ."
} },
} "clientURI": {
} "type": "string",
"description": "OPTIONAL. URL of the home page of the Client. The value of this field MUST point to a valid Web page. If present, the server SHOULD display this URL to the End-User in a followable fashion. If desired, representation of this Claim in different languages and scripts is represented as described in Section 2.1 ( Metadata Languages and Scripts ) ."
} }
}
}, },
"ClientRegistrationResponse": { "ClientCreateRequest": {
"id": "ClientRegistrationResponse", "id": "ClientCreateRequest",
"type": "object", "type": "object",
"description": "Upon successful registration, an ID and secret is assigned to the client.", "description": "A request to register a client with dex.",
"properties": { "properties": {
"client_id": { "client": {
"type": "string" "$ref": "Client"
}, }
"client_secret": {
"type": "string"
}
}
} }
},
"ClientCreateResponse": {
"id": "ClientCreateResponse",
"type": "object",
"description": "Upon successful registration, an ID and secret is assigned to the client.",
"properties": {
"client":{
"$ref": "Client"
}
}
}
}, },
"resources": { "resources": {
"Admin": { "Admin": {
@ -154,7 +163,7 @@
"$ref": "ClientCreateRequest" "$ref": "ClientCreateRequest"
}, },
"response": { "response": {
"$ref": "ClientRegistrationResponse" "$ref": "ClientCreateResponse"
} }
} }
} }

View file

@ -4,11 +4,12 @@ import (
"errors" "errors"
"net/url" "net/url"
"github.com/coreos/dex/client"
"github.com/coreos/go-oidc/oidc" "github.com/coreos/go-oidc/oidc"
) )
func MapSchemaClientToClientIdentity(sc Client) (oidc.ClientIdentity, error) { func MapSchemaClientToClient(sc Client) (client.Client, error) {
ci := oidc.ClientIdentity{ ci := client.Client{
Credentials: oidc.ClientCredentials{ Credentials: oidc.ClientCredentials{
ID: sc.Id, ID: sc.Id,
}, },
@ -19,12 +20,12 @@ func MapSchemaClientToClientIdentity(sc Client) (oidc.ClientIdentity, error) {
for i, ru := range sc.RedirectURIs { for i, ru := range sc.RedirectURIs {
if ru == "" { if ru == "" {
return oidc.ClientIdentity{}, errors.New("redirect URL empty") return client.Client{}, errors.New("redirect URL empty")
} }
u, err := url.Parse(ru) u, err := url.Parse(ru)
if err != nil { if err != nil {
return oidc.ClientIdentity{}, errors.New("redirect URL invalid") return client.Client{}, errors.New("redirect URL invalid")
} }
ci.Metadata.RedirectURIs[i] = *u ci.Metadata.RedirectURIs[i] = *u
@ -33,7 +34,7 @@ func MapSchemaClientToClientIdentity(sc Client) (oidc.ClientIdentity, error) {
return ci, nil return ci, nil
} }
func MapClientIdentityToSchemaClient(c oidc.ClientIdentity) Client { func MapClientToSchemaClient(c client.Client) Client {
cl := Client{ cl := Client{
Id: c.Credentials.ID, Id: c.Credentials.ID,
RedirectURIs: make([]string, len(c.Metadata.RedirectURIs)), RedirectURIs: make([]string, len(c.Metadata.RedirectURIs)),
@ -44,7 +45,7 @@ func MapClientIdentityToSchemaClient(c oidc.ClientIdentity) Client {
return cl return cl
} }
func MapClientIdentityToSchemaClientWithSecret(c oidc.ClientIdentity) ClientWithSecret { func MapClientToSchemaClientWithSecret(c client.Client) ClientWithSecret {
cl := ClientWithSecret{ cl := ClientWithSecret{
Id: c.Credentials.ID, Id: c.Credentials.ID,
Secret: c.Credentials.Secret, Secret: c.Credentials.Secret,

View file

@ -1,4 +1,5 @@
package workerschema package workerschema
// //
// This file is automatically generated by schema/generator // This file is automatically generated by schema/generator
// //

View file

@ -116,7 +116,7 @@ func (s *AdminServer) getState(w http.ResponseWriter, r *http.Request, ps httpro
} }
func (s *AdminServer) createClient(w http.ResponseWriter, r *http.Request, ps httprouter.Params) { func (s *AdminServer) createClient(w http.ResponseWriter, r *http.Request, ps httprouter.Params) {
var req admin.ClientRegistrationRequest var req = adminschema.ClientCreateRequest{}
if err := json.NewDecoder(r.Body).Decode(&req); err != nil { if err := json.NewDecoder(r.Body).Decode(&req); err != nil {
writeInvalidRequest(w, "cannot parse JSON body") writeInvalidRequest(w, "cannot parse JSON body")
return return

View file

@ -14,7 +14,7 @@ import (
type clientTokenMiddleware struct { type clientTokenMiddleware struct {
issuerURL string issuerURL string
ciRepo client.ClientIdentityRepo ciRepo client.ClientRepo
keysFunc func() ([]key.PublicKey, error) keysFunc func() ([]key.PublicKey, error)
next http.Handler next http.Handler
} }
@ -31,7 +31,7 @@ func (c *clientTokenMiddleware) ServeHTTP(w http.ResponseWriter, r *http.Request
} }
if c.ciRepo == nil { if c.ciRepo == nil {
log.Errorf("Misconfigured clientTokenMiddleware, ClientIdentityRepo is not set") log.Errorf("Misconfigured clientTokenMiddleware, ClientRepo is not set")
respondError() respondError()
return return
} }

View file

@ -26,7 +26,7 @@ func TestClientToken(t *testing.T) {
now := time.Now() now := time.Now()
tomorrow := now.Add(24 * time.Hour) tomorrow := now.Add(24 * time.Hour)
validClientID := "valid-client" validClientID := "valid-client"
ci := oidc.ClientIdentity{ ci := client.Client{
Credentials: oidc.ClientCredentials{ Credentials: oidc.ClientCredentials{
ID: validClientID, ID: validClientID,
Secret: base64.URLEncoding.EncodeToString([]byte("secret")), Secret: base64.URLEncoding.EncodeToString([]byte("secret")),
@ -37,7 +37,7 @@ func TestClientToken(t *testing.T) {
}, },
}, },
} }
repo, err := db.NewClientIdentityRepoFromClients(db.NewMemDB(), []oidc.ClientIdentity{ci}) repo, err := db.NewClientRepoFromClients(db.NewMemDB(), []client.Client{ci})
if err != nil { if err != nil {
t.Fatalf("Failed to create client identity repo: %v", err) t.Fatalf("Failed to create client identity repo: %v", err)
} }
@ -65,7 +65,7 @@ func TestClientToken(t *testing.T) {
tests := []struct { tests := []struct {
keys []key.PublicKey keys []key.PublicKey
repo client.ClientIdentityRepo repo client.ClientRepo
header string header string
wantCode int wantCode int
}{ }{
@ -114,7 +114,7 @@ func TestClientToken(t *testing.T) {
// empty repo // empty repo
{ {
keys: []key.PublicKey{pubKey}, keys: []key.PublicKey{pubKey},
repo: db.NewClientIdentityRepo(db.NewMemDB()), repo: db.NewClientRepo(db.NewMemDB()),
header: fmt.Sprintf("BEARER %s", validJWT), header: fmt.Sprintf("BEARER %s", validJWT),
wantCode: http.StatusUnauthorized, wantCode: http.StatusUnauthorized,
}, },

View file

@ -4,7 +4,9 @@ import (
"encoding/json" "encoding/json"
"net/http" "net/http"
"github.com/coreos/dex/client"
"github.com/coreos/dex/pkg/log" "github.com/coreos/dex/pkg/log"
"github.com/coreos/go-oidc/oauth2" "github.com/coreos/go-oidc/oauth2"
"github.com/coreos/go-oidc/oidc" "github.com/coreos/go-oidc/oidc"
) )
@ -43,7 +45,12 @@ func (s *Server) handleClientRegistrationRequest(r *http.Request) (*oidc.ClientR
return nil, newAPIError(oauth2.ErrorServerError, "unable to save client metadata") return nil, newAPIError(oauth2.ErrorServerError, "unable to save client metadata")
} }
creds, err := s.ClientIdentityRepo.New(id, clientMetadata, false) creds, err := s.ClientRepo.New(client.Client{
Credentials: oidc.ClientCredentials{
ID: id,
},
Metadata: clientMetadata,
})
if err != nil { if err != nil {
log.Errorf("Failed to create new client identity: %v", err) log.Errorf("Failed to create new client identity: %v", err)
return nil, newAPIError(oauth2.ErrorServerError, "unable to save client metadata") return nil, newAPIError(oauth2.ErrorServerError, "unable to save client metadata")

View file

@ -143,7 +143,7 @@ func TestClientRegistration(t *testing.T) {
return fmt.Errorf("no client id in registration response") return fmt.Errorf("no client id in registration response")
} }
metadata, err := fixtures.clientIdentityRepo.Metadata(r.ClientID) metadata, err := fixtures.clientRepo.Metadata(r.ClientID)
if err != nil { if err != nil {
return fmt.Errorf("failed to lookup client id after creation") return fmt.Errorf("failed to lookup client id after creation")
} }

View file

@ -14,10 +14,10 @@ import (
) )
type clientResource struct { type clientResource struct {
repo client.ClientIdentityRepo repo client.ClientRepo
} }
func registerClientResource(prefix string, repo client.ClientIdentityRepo) (string, http.Handler) { func registerClientResource(prefix string, repo client.ClientRepo) (string, http.Handler) {
mux := http.NewServeMux() mux := http.NewServeMux()
c := &clientResource{ c := &clientResource{
repo: repo, repo: repo,
@ -49,7 +49,7 @@ func (c *clientResource) list(w http.ResponseWriter, r *http.Request) {
scs := make([]*schema.Client, len(cs)) scs := make([]*schema.Client, len(cs))
for i, ci := range cs { for i, ci := range cs {
sc := schema.MapClientIdentityToSchemaClient(ci) sc := schema.MapClientToSchemaClient(ci)
scs[i] = &sc scs[i] = &sc
} }
@ -76,7 +76,7 @@ func (c *clientResource) create(w http.ResponseWriter, r *http.Request) {
return return
} }
ci, err := schema.MapSchemaClientToClientIdentity(sc) ci, err := schema.MapSchemaClientToClient(sc)
if err != nil { if err != nil {
log.Debugf("Invalid request data: %v", err) log.Debugf("Invalid request data: %v", err)
writeAPIError(w, http.StatusBadRequest, newAPIError(errorInvalidClientMetadata, "missing or invalid field: redirectURIs")) writeAPIError(w, http.StatusBadRequest, newAPIError(errorInvalidClientMetadata, "missing or invalid field: redirectURIs"))
@ -96,7 +96,9 @@ func (c *clientResource) create(w http.ResponseWriter, r *http.Request) {
return return
} }
creds, err := c.repo.New(clientID, ci.Metadata, false) ci.Credentials.ID = clientID
creds, err := c.repo.New(ci)
if err != nil { if err != nil {
log.Errorf("Failed creating client: %v", err) log.Errorf("Failed creating client: %v", err)
writeAPIError(w, http.StatusInternalServerError, newAPIError(errorServerError, "unable to create client")) writeAPIError(w, http.StatusInternalServerError, newAPIError(errorServerError, "unable to create client"))
@ -104,7 +106,7 @@ func (c *clientResource) create(w http.ResponseWriter, r *http.Request) {
} }
ci.Credentials = *creds ci.Credentials = *creds
ssc := schema.MapClientIdentityToSchemaClientWithSecret(ci) ssc := schema.MapClientToSchemaClientWithSecret(ci)
w.Header().Add("Location", phttp.NewResourceLocation(r.URL, ci.Credentials.ID)) w.Header().Add("Location", phttp.NewResourceLocation(r.URL, ci.Credentials.ID))
writeResponseWithBody(w, http.StatusCreated, ssc) writeResponseWithBody(w, http.StatusCreated, ssc)
} }

View file

@ -14,6 +14,7 @@ import (
"strings" "strings"
"testing" "testing"
"github.com/coreos/dex/client"
"github.com/coreos/dex/db" "github.com/coreos/dex/db"
schema "github.com/coreos/dex/schema/workerschema" schema "github.com/coreos/dex/schema/workerschema"
"github.com/coreos/go-oidc/oidc" "github.com/coreos/go-oidc/oidc"
@ -27,7 +28,7 @@ func makeBody(s string) io.ReadCloser {
func TestCreateInvalidRequest(t *testing.T) { func TestCreateInvalidRequest(t *testing.T) {
u := &url.URL{Scheme: "http", Host: "example.com", Path: "clients"} u := &url.URL{Scheme: "http", Host: "example.com", Path: "clients"}
h := http.Header{"Content-Type": []string{"application/json"}} h := http.Header{"Content-Type": []string{"application/json"}}
repo := db.NewClientIdentityRepo(db.NewMemDB()) repo := db.NewClientRepo(db.NewMemDB())
res := &clientResource{repo: repo} res := &clientResource{repo: repo}
tests := []struct { tests := []struct {
req *http.Request req *http.Request
@ -118,7 +119,7 @@ func TestCreateInvalidRequest(t *testing.T) {
} }
func TestCreate(t *testing.T) { func TestCreate(t *testing.T) {
repo := db.NewClientIdentityRepo(db.NewMemDB()) repo := db.NewClientRepo(db.NewMemDB())
res := &clientResource{repo: repo} res := &clientResource{repo: repo}
tests := [][]string{ tests := [][]string{
[]string{"http://example.com"}, []string{"http://example.com"},
@ -177,7 +178,7 @@ func TestList(t *testing.T) {
} }
tests := []struct { tests := []struct {
cs []oidc.ClientIdentity cs []client.Client
want []*schema.Client want []*schema.Client
}{ }{
// empty repo // empty repo
@ -187,8 +188,8 @@ func TestList(t *testing.T) {
}, },
// single client // single client
{ {
cs: []oidc.ClientIdentity{ cs: []client.Client{
oidc.ClientIdentity{ client.Client{
Credentials: oidc.ClientCredentials{ID: "foo", Secret: b64Encode("bar")}, Credentials: oidc.ClientCredentials{ID: "foo", Secret: b64Encode("bar")},
Metadata: oidc.ClientMetadata{ Metadata: oidc.ClientMetadata{
RedirectURIs: []url.URL{ RedirectURIs: []url.URL{
@ -206,8 +207,8 @@ func TestList(t *testing.T) {
}, },
// multi client // multi client
{ {
cs: []oidc.ClientIdentity{ cs: []client.Client{
oidc.ClientIdentity{ client.Client{
Credentials: oidc.ClientCredentials{ID: "foo", Secret: b64Encode("bar")}, Credentials: oidc.ClientCredentials{ID: "foo", Secret: b64Encode("bar")},
Metadata: oidc.ClientMetadata{ Metadata: oidc.ClientMetadata{
RedirectURIs: []url.URL{ RedirectURIs: []url.URL{
@ -215,7 +216,7 @@ func TestList(t *testing.T) {
}, },
}, },
}, },
oidc.ClientIdentity{ client.Client{
Credentials: oidc.ClientCredentials{ID: "biz", Secret: b64Encode("bang")}, Credentials: oidc.ClientCredentials{ID: "biz", Secret: b64Encode("bang")},
Metadata: oidc.ClientMetadata{ Metadata: oidc.ClientMetadata{
RedirectURIs: []url.URL{ RedirectURIs: []url.URL{
@ -238,7 +239,7 @@ func TestList(t *testing.T) {
} }
for i, tt := range tests { for i, tt := range tests {
repo, err := db.NewClientIdentityRepoFromClients(db.NewMemDB(), tt.cs) repo, err := db.NewClientRepoFromClients(db.NewMemDB(), tt.cs)
if err != nil { if err != nil {
t.Errorf("case %d: failed to create client identity repo: %v", i, err) t.Errorf("case %d: failed to create client identity repo: %v", i, err)
continue continue

View file

@ -13,10 +13,10 @@ import (
"time" "time"
"github.com/coreos/go-oidc/key" "github.com/coreos/go-oidc/key"
"github.com/coreos/go-oidc/oidc"
"github.com/coreos/pkg/health" "github.com/coreos/pkg/health"
"github.com/go-gorp/gorp" "github.com/go-gorp/gorp"
"github.com/coreos/dex/client"
"github.com/coreos/dex/connector" "github.com/coreos/dex/connector"
"github.com/coreos/dex/db" "github.com/coreos/dex/db"
"github.com/coreos/dex/email" "github.com/coreos/dex/email"
@ -114,7 +114,7 @@ func (cfg *SingleServerConfig) Configure(srv *Server) error {
if err != nil { if err != nil {
return fmt.Errorf("unable to read clients from file %s: %v", cfg.ClientsFile, err) return fmt.Errorf("unable to read clients from file %s: %v", cfg.ClientsFile, err)
} }
ciRepo, err := db.NewClientIdentityRepoFromClients(dbMap, clients) ciRepo, err := db.NewClientRepoFromClients(dbMap, clients)
if err != nil { if err != nil {
return fmt.Errorf("failed to create client identity repo: %v", err) return fmt.Errorf("failed to create client identity repo: %v", err)
} }
@ -155,7 +155,7 @@ func (cfg *SingleServerConfig) Configure(srv *Server) error {
txnFactory := db.TransactionFactory(dbMap) txnFactory := db.TransactionFactory(dbMap)
userManager := usermanager.NewUserManager(userRepo, pwiRepo, cfgRepo, txnFactory, usermanager.ManagerOptions{}) userManager := usermanager.NewUserManager(userRepo, pwiRepo, cfgRepo, txnFactory, usermanager.ManagerOptions{})
srv.ClientIdentityRepo = ciRepo srv.ClientRepo = ciRepo
srv.KeySetRepo = kRepo srv.KeySetRepo = kRepo
srv.ConnectorConfigRepo = cfgRepo srv.ConnectorConfigRepo = cfgRepo
srv.UserRepo = userRepo srv.UserRepo = userRepo
@ -214,47 +214,14 @@ func loadUsersFromReader(r io.Reader) (users []user.UserWithRemoteIdentities, pw
return return
} }
// loadClients parses the clients.json file and returns the clients to be created. // loadClients parses the clients.json file and returns a list of clients.
func loadClients(filepath string) ([]oidc.ClientIdentity, error) { func loadClients(filepath string) ([]client.Client, 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) return client.ClientsFromReader(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(r).Decode(&c); err != nil {
return nil, err
}
clients := make([]oidc.ClientIdentity, len(c))
for i, client := range c {
redirectURIs := make([]url.URL, len(client.RedirectURLs))
for j, u := range client.RedirectURLs {
uri, err := url.Parse(u)
if err != nil {
return nil, err
}
redirectURIs[j] = *uri
}
clients[i] = oidc.ClientIdentity{
Credentials: oidc.ClientCredentials{
ID: client.ID,
Secret: client.Secret,
},
Metadata: oidc.ClientMetadata{
RedirectURIs: redirectURIs,
},
}
}
return clients, nil
} }
func (cfg *MultiServerConfig) Configure(srv *Server) error { func (cfg *MultiServerConfig) Configure(srv *Server) error {
@ -279,7 +246,7 @@ func (cfg *MultiServerConfig) Configure(srv *Server) error {
return fmt.Errorf("unable to create PrivateKeySetRepo: %v", err) return fmt.Errorf("unable to create PrivateKeySetRepo: %v", err)
} }
ciRepo := db.NewClientIdentityRepo(dbc) ciRepo := db.NewClientRepo(dbc)
sRepo := db.NewSessionRepo(dbc) sRepo := db.NewSessionRepo(dbc)
skRepo := db.NewSessionKeyRepo(dbc) skRepo := db.NewSessionKeyRepo(dbc)
cfgRepo := db.NewConnectorConfigRepo(dbc) cfgRepo := db.NewConnectorConfigRepo(dbc)
@ -290,7 +257,7 @@ func (cfg *MultiServerConfig) Configure(srv *Server) error {
sm := sessionmanager.NewSessionManager(sRepo, skRepo) sm := sessionmanager.NewSessionManager(sRepo, skRepo)
srv.ClientIdentityRepo = ciRepo srv.ClientRepo = ciRepo
srv.KeySetRepo = kRepo srv.KeySetRepo = kRepo
srv.ConnectorConfigRepo = cfgRepo srv.ConnectorConfigRepo = cfgRepo
srv.UserRepo = userRepo srv.UserRepo = userRepo

View file

@ -28,7 +28,7 @@ func handleVerifyEmailResendFunc(
srvKeysFunc func() ([]key.PublicKey, error), srvKeysFunc func() ([]key.PublicKey, error),
emailer *useremail.UserEmailer, emailer *useremail.UserEmailer,
userRepo user.UserRepo, userRepo user.UserRepo,
clientIdentityRepo client.ClientIdentityRepo) http.HandlerFunc { clientRepo client.ClientRepo) http.HandlerFunc {
return func(w http.ResponseWriter, r *http.Request) { return func(w http.ResponseWriter, r *http.Request) {
decoder := json.NewDecoder(r.Body) decoder := json.NewDecoder(r.Body)
var params struct { var params struct {
@ -57,7 +57,7 @@ func handleVerifyEmailResendFunc(
return return
} }
cm, err := clientIdentityRepo.Metadata(clientID) cm, err := clientRepo.Metadata(clientID)
if err == client.ErrorNotFound { if err == client.ErrorNotFound {
log.Errorf("No such client: %v", err) log.Errorf("No such client: %v", err)
writeAPIError(w, http.StatusBadRequest, writeAPIError(w, http.StatusBadRequest,

View file

@ -130,7 +130,7 @@ func TestHandleVerifyEmailResend(t *testing.T) {
keysFunc, keysFunc,
f.srv.UserEmailer, f.srv.UserEmailer,
f.userRepo, f.userRepo,
f.clientIdentityRepo) f.clientRepo)
w := httptest.NewRecorder() w := httptest.NewRecorder()
u := "http://example.com" u := "http://example.com"

View file

@ -78,9 +78,9 @@ func TestHandleAuthFuncResponsesSingleRedirectURL(t *testing.T) {
srv := &Server{ srv := &Server{
IssuerURL: url.URL{Scheme: "http", Host: "server.example.com"}, IssuerURL: url.URL{Scheme: "http", Host: "server.example.com"},
SessionManager: manager.NewSessionManager(db.NewSessionRepo(db.NewMemDB()), db.NewSessionKeyRepo(db.NewMemDB())), SessionManager: manager.NewSessionManager(db.NewSessionRepo(db.NewMemDB()), db.NewSessionKeyRepo(db.NewMemDB())),
ClientIdentityRepo: func() client.ClientIdentityRepo { ClientRepo: func() client.ClientRepo {
repo, err := db.NewClientIdentityRepoFromClients(db.NewMemDB(), []oidc.ClientIdentity{ repo, err := db.NewClientRepoFromClients(db.NewMemDB(), []client.Client{
oidc.ClientIdentity{ client.Client{
Credentials: oidc.ClientCredentials{ Credentials: oidc.ClientCredentials{
ID: "XXX", ID: "XXX",
Secret: base64.URLEncoding.EncodeToString([]byte("secrete")), Secret: base64.URLEncoding.EncodeToString([]byte("secrete")),
@ -230,9 +230,9 @@ func TestHandleAuthFuncResponsesMultipleRedirectURLs(t *testing.T) {
srv := &Server{ srv := &Server{
IssuerURL: url.URL{Scheme: "http", Host: "server.example.com"}, IssuerURL: url.URL{Scheme: "http", Host: "server.example.com"},
SessionManager: manager.NewSessionManager(db.NewSessionRepo(db.NewMemDB()), db.NewSessionKeyRepo(db.NewMemDB())), SessionManager: manager.NewSessionManager(db.NewSessionRepo(db.NewMemDB()), db.NewSessionKeyRepo(db.NewMemDB())),
ClientIdentityRepo: func() client.ClientIdentityRepo { ClientRepo: func() client.ClientRepo {
repo, err := db.NewClientIdentityRepoFromClients(db.NewMemDB(), []oidc.ClientIdentity{ repo, err := db.NewClientRepoFromClients(db.NewMemDB(), []client.Client{
oidc.ClientIdentity{ client.Client{
Credentials: oidc.ClientCredentials{ Credentials: oidc.ClientCredentials{
ID: "XXX", ID: "XXX",
Secret: base64.URLEncoding.EncodeToString([]byte("secrete")), Secret: base64.URLEncoding.EncodeToString([]byte("secrete")),

View file

@ -29,7 +29,7 @@ type SendResetPasswordEmailHandler struct {
tpl *template.Template tpl *template.Template
emailer *useremail.UserEmailer emailer *useremail.UserEmailer
sm *sessionmanager.SessionManager sm *sessionmanager.SessionManager
cr client.ClientIdentityRepo cr client.ClientRepo
} }
func (h *SendResetPasswordEmailHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) { func (h *SendResetPasswordEmailHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) {

View file

@ -267,7 +267,7 @@ func TestSendResetPasswordEmailHandler(t *testing.T) {
tpl: f.srv.SendResetPasswordEmailTemplate, tpl: f.srv.SendResetPasswordEmailTemplate,
emailer: f.srv.UserEmailer, emailer: f.srv.UserEmailer,
sm: f.sessionManager, sm: f.sessionManager,
cr: f.clientIdentityRepo, cr: f.clientRepo,
} }
w := httptest.NewRecorder() w := httptest.NewRecorder()

View file

@ -60,7 +60,7 @@ type Server struct {
KeyManager key.PrivateKeyManager KeyManager key.PrivateKeyManager
KeySetRepo key.PrivateKeySetRepo KeySetRepo key.PrivateKeySetRepo
SessionManager *sessionmanager.SessionManager SessionManager *sessionmanager.SessionManager
ClientIdentityRepo client.ClientIdentityRepo ClientRepo client.ClientRepo
ConnectorConfigRepo connector.ConnectorConfigRepo ConnectorConfigRepo connector.ConnectorConfigRepo
Templates *template.Template Templates *template.Template
LoginTemplate *template.Template LoginTemplate *template.Template
@ -213,13 +213,13 @@ func (s *Server) HTTPHandler() http.Handler {
s.KeyManager.PublicKeys, s.KeyManager.PublicKeys,
s.UserEmailer, s.UserEmailer,
s.UserRepo, s.UserRepo,
s.ClientIdentityRepo))) s.ClientRepo)))
mux.Handle(httpPathSendResetPassword, &SendResetPasswordEmailHandler{ mux.Handle(httpPathSendResetPassword, &SendResetPasswordEmailHandler{
tpl: s.SendResetPasswordEmailTemplate, tpl: s.SendResetPasswordEmailTemplate,
emailer: s.UserEmailer, emailer: s.UserEmailer,
sm: s.SessionManager, sm: s.SessionManager,
cr: s.ClientIdentityRepo, cr: s.ClientRepo,
}) })
mux.Handle(httpPathResetPassword, &ResetPasswordHandler{ mux.Handle(httpPathResetPassword, &ResetPasswordHandler{
@ -256,11 +256,11 @@ func (s *Server) HTTPHandler() http.Handler {
apiBasePath := path.Join(httpPathAPI, APIVersion) apiBasePath := path.Join(httpPathAPI, APIVersion)
registerDiscoveryResource(apiBasePath, mux) registerDiscoveryResource(apiBasePath, mux)
clientPath, clientHandler := registerClientResource(apiBasePath, s.ClientIdentityRepo) clientPath, clientHandler := registerClientResource(apiBasePath, s.ClientRepo)
mux.Handle(path.Join(apiBasePath, clientPath), s.NewClientTokenAuthHandler(clientHandler)) mux.Handle(path.Join(apiBasePath, clientPath), s.NewClientTokenAuthHandler(clientHandler))
usersAPI := usersapi.NewUsersAPI(s.dbMap, s.UserManager, s.UserEmailer, s.localConnectorID) usersAPI := usersapi.NewUsersAPI(s.dbMap, s.UserManager, s.UserEmailer, s.localConnectorID)
handler := NewUserMgmtServer(usersAPI, s.JWTVerifierFactory(), s.UserManager, s.ClientIdentityRepo).HTTPHandler() handler := NewUserMgmtServer(usersAPI, s.JWTVerifierFactory(), s.UserManager, s.ClientRepo).HTTPHandler()
mux.Handle(apiBasePath+"/", handler) mux.Handle(apiBasePath+"/", handler)
@ -271,14 +271,14 @@ func (s *Server) HTTPHandler() http.Handler {
func (s *Server) NewClientTokenAuthHandler(handler http.Handler) http.Handler { func (s *Server) NewClientTokenAuthHandler(handler http.Handler) http.Handler {
return &clientTokenMiddleware{ return &clientTokenMiddleware{
issuerURL: s.IssuerURL.String(), issuerURL: s.IssuerURL.String(),
ciRepo: s.ClientIdentityRepo, ciRepo: s.ClientRepo,
keysFunc: s.KeyManager.PublicKeys, keysFunc: s.KeyManager.PublicKeys,
next: handler, next: handler,
} }
} }
func (s *Server) ClientMetadata(clientID string) (*oidc.ClientMetadata, error) { func (s *Server) ClientMetadata(clientID string) (*oidc.ClientMetadata, error) {
return s.ClientIdentityRepo.Metadata(clientID) return s.ClientRepo.Metadata(clientID)
} }
func (s *Server) NewSession(ipdcID, clientID, clientState string, redirectURL url.URL, nonce string, register bool, scope []string) (string, error) { func (s *Server) NewSession(ipdcID, clientID, clientState string, redirectURL url.URL, nonce string, register bool, scope []string) (string, error) {
@ -365,7 +365,7 @@ func (s *Server) Login(ident oidc.Identity, key string) (string, error) {
} }
func (s *Server) ClientCredsToken(creds oidc.ClientCredentials) (*jose.JWT, error) { func (s *Server) ClientCredsToken(creds oidc.ClientCredentials) (*jose.JWT, error) {
ok, err := s.ClientIdentityRepo.Authenticate(creds) ok, err := s.ClientRepo.Authenticate(creds)
if err != nil { if err != nil {
log.Errorf("Failed fetching client %s from repo: %v", creds.ID, err) log.Errorf("Failed fetching client %s from repo: %v", creds.ID, err)
return nil, oauth2.NewError(oauth2.ErrorServerError) return nil, oauth2.NewError(oauth2.ErrorServerError)
@ -397,7 +397,7 @@ func (s *Server) ClientCredsToken(creds oidc.ClientCredentials) (*jose.JWT, erro
} }
func (s *Server) CodeToken(creds oidc.ClientCredentials, sessionKey string) (*jose.JWT, string, error) { func (s *Server) CodeToken(creds oidc.ClientCredentials, sessionKey string) (*jose.JWT, string, error) {
ok, err := s.ClientIdentityRepo.Authenticate(creds) ok, err := s.ClientRepo.Authenticate(creds)
if err != nil { if err != nil {
log.Errorf("Failed fetching client %s from repo: %v", creds.ID, err) log.Errorf("Failed fetching client %s from repo: %v", creds.ID, err)
return nil, "", oauth2.NewError(oauth2.ErrorServerError) return nil, "", oauth2.NewError(oauth2.ErrorServerError)
@ -466,7 +466,7 @@ func (s *Server) CodeToken(creds oidc.ClientCredentials, sessionKey string) (*jo
} }
func (s *Server) RefreshToken(creds oidc.ClientCredentials, token string) (*jose.JWT, error) { func (s *Server) RefreshToken(creds oidc.ClientCredentials, token string) (*jose.JWT, error) {
ok, err := s.ClientIdentityRepo.Authenticate(creds) ok, err := s.ClientRepo.Authenticate(creds)
if err != nil { if err != nil {
log.Errorf("Failed fetching client %s from repo: %v", creds.ID, err) log.Errorf("Failed fetching client %s from repo: %v", creds.ID, err)
return nil, oauth2.NewError(oauth2.ErrorServerError) return nil, oauth2.NewError(oauth2.ErrorServerError)

View file

@ -130,7 +130,7 @@ func TestServerNewSession(t *testing.T) {
state := "pants" state := "pants"
nonce := "oncenay" nonce := "oncenay"
ci := oidc.ClientIdentity{ ci := client.Client{
Credentials: oidc.ClientCredentials{ Credentials: oidc.ClientCredentials{
ID: "XXX", ID: "XXX",
Secret: "secrete", Secret: "secrete",
@ -179,7 +179,7 @@ func TestServerNewSession(t *testing.T) {
} }
func TestServerLogin(t *testing.T) { func TestServerLogin(t *testing.T) {
ci := oidc.ClientIdentity{ ci := client.Client{
Credentials: oidc.ClientCredentials{ Credentials: oidc.ClientCredentials{
ID: "XXX", ID: "XXX",
Secret: clientTestSecret, Secret: clientTestSecret,
@ -194,8 +194,8 @@ func TestServerLogin(t *testing.T) {
}, },
}, },
} }
ciRepo := func() client.ClientIdentityRepo { ciRepo := func() client.ClientRepo {
repo, err := db.NewClientIdentityRepoFromClients(db.NewMemDB(), []oidc.ClientIdentity{ci}) repo, err := db.NewClientRepoFromClients(db.NewMemDB(), []client.Client{ci})
if err != nil { if err != nil {
t.Fatalf("Failed to create client identity repo: %v", err) t.Fatalf("Failed to create client identity repo: %v", err)
} }
@ -219,11 +219,11 @@ func TestServerLogin(t *testing.T) {
} }
srv := &Server{ srv := &Server{
IssuerURL: url.URL{Scheme: "http", Host: "server.example.com"}, IssuerURL: url.URL{Scheme: "http", Host: "server.example.com"},
KeyManager: km, KeyManager: km,
SessionManager: sm, SessionManager: sm,
ClientIdentityRepo: ciRepo, ClientRepo: ciRepo,
UserRepo: userRepo, UserRepo: userRepo,
} }
ident := oidc.Identity{ID: "YYY", Name: "elroy", Email: "elroy@example.com"} ident := oidc.Identity{ID: "YYY", Name: "elroy", Email: "elroy@example.com"}
@ -244,9 +244,9 @@ func TestServerLogin(t *testing.T) {
} }
func TestServerLoginUnrecognizedSessionKey(t *testing.T) { func TestServerLoginUnrecognizedSessionKey(t *testing.T) {
ciRepo := func() client.ClientIdentityRepo { ciRepo := func() client.ClientRepo {
repo, err := db.NewClientIdentityRepoFromClients(db.NewMemDB(), []oidc.ClientIdentity{ repo, err := db.NewClientRepoFromClients(db.NewMemDB(), []client.Client{
oidc.ClientIdentity{ client.Client{
Credentials: oidc.ClientCredentials{ Credentials: oidc.ClientCredentials{
ID: "XXX", Secret: clientTestSecret, ID: "XXX", Secret: clientTestSecret,
}, },
@ -263,10 +263,10 @@ func TestServerLoginUnrecognizedSessionKey(t *testing.T) {
} }
sm := manager.NewSessionManager(db.NewSessionRepo(db.NewMemDB()), db.NewSessionKeyRepo(db.NewMemDB())) sm := manager.NewSessionManager(db.NewSessionRepo(db.NewMemDB()), db.NewSessionKeyRepo(db.NewMemDB()))
srv := &Server{ srv := &Server{
IssuerURL: url.URL{Scheme: "http", Host: "server.example.com"}, IssuerURL: url.URL{Scheme: "http", Host: "server.example.com"},
KeyManager: km, KeyManager: km,
SessionManager: sm, SessionManager: sm,
ClientIdentityRepo: ciRepo, ClientRepo: ciRepo,
} }
ident := oidc.Identity{ID: "YYY", Name: "elroy", Email: "elroy@example.com"} ident := oidc.Identity{ID: "YYY", Name: "elroy", Email: "elroy@example.com"}
@ -281,7 +281,7 @@ func TestServerLoginUnrecognizedSessionKey(t *testing.T) {
} }
func TestServerLoginDisabledUser(t *testing.T) { func TestServerLoginDisabledUser(t *testing.T) {
ci := oidc.ClientIdentity{ ci := client.Client{
Credentials: oidc.ClientCredentials{ Credentials: oidc.ClientCredentials{
ID: "XXX", ID: "XXX",
Secret: clientTestSecret, Secret: clientTestSecret,
@ -296,8 +296,8 @@ func TestServerLoginDisabledUser(t *testing.T) {
}, },
}, },
} }
ciRepo := func() client.ClientIdentityRepo { ciRepo := func() client.ClientRepo {
repo, err := db.NewClientIdentityRepoFromClients(db.NewMemDB(), []oidc.ClientIdentity{ci}) repo, err := db.NewClientRepoFromClients(db.NewMemDB(), []client.Client{ci})
if err != nil { if err != nil {
t.Fatalf("Failed to create client identity repo: %v", err) t.Fatalf("Failed to create client identity repo: %v", err)
} }
@ -335,11 +335,11 @@ func TestServerLoginDisabledUser(t *testing.T) {
}) })
srv := &Server{ srv := &Server{
IssuerURL: url.URL{Scheme: "http", Host: "server.example.com"}, IssuerURL: url.URL{Scheme: "http", Host: "server.example.com"},
KeyManager: km, KeyManager: km,
SessionManager: sm, SessionManager: sm,
ClientIdentityRepo: ciRepo, ClientRepo: ciRepo,
UserRepo: userRepo, UserRepo: userRepo,
} }
ident := oidc.Identity{ID: "disabled-connector-id", Name: "elroy", Email: "elroy@example.com"} ident := oidc.Identity{ID: "disabled-connector-id", Name: "elroy", Email: "elroy@example.com"}
@ -355,14 +355,14 @@ func TestServerLoginDisabledUser(t *testing.T) {
} }
func TestServerCodeToken(t *testing.T) { func TestServerCodeToken(t *testing.T) {
ci := oidc.ClientIdentity{ ci := client.Client{
Credentials: oidc.ClientCredentials{ Credentials: oidc.ClientCredentials{
ID: "XXX", ID: "XXX",
Secret: clientTestSecret, Secret: clientTestSecret,
}, },
} }
ciRepo := func() client.ClientIdentityRepo { ciRepo := func() client.ClientRepo {
repo, err := db.NewClientIdentityRepoFromClients(db.NewMemDB(), []oidc.ClientIdentity{ci}) repo, err := db.NewClientRepoFromClients(db.NewMemDB(), []client.Client{ci})
if err != nil { if err != nil {
t.Fatalf("Failed to create client identity repo: %v", err) t.Fatalf("Failed to create client identity repo: %v", err)
} }
@ -381,12 +381,12 @@ func TestServerCodeToken(t *testing.T) {
refreshTokenRepo := refreshtest.NewTestRefreshTokenRepo() refreshTokenRepo := refreshtest.NewTestRefreshTokenRepo()
srv := &Server{ srv := &Server{
IssuerURL: url.URL{Scheme: "http", Host: "server.example.com"}, IssuerURL: url.URL{Scheme: "http", Host: "server.example.com"},
KeyManager: km, KeyManager: km,
SessionManager: sm, SessionManager: sm,
ClientIdentityRepo: ciRepo, ClientRepo: ciRepo,
UserRepo: userRepo, UserRepo: userRepo,
RefreshTokenRepo: refreshTokenRepo, RefreshTokenRepo: refreshTokenRepo,
} }
tests := []struct { tests := []struct {
@ -441,14 +441,14 @@ func TestServerCodeToken(t *testing.T) {
} }
func TestServerTokenUnrecognizedKey(t *testing.T) { func TestServerTokenUnrecognizedKey(t *testing.T) {
ci := oidc.ClientIdentity{ ci := client.Client{
Credentials: oidc.ClientCredentials{ Credentials: oidc.ClientCredentials{
ID: "XXX", ID: "XXX",
Secret: clientTestSecret, Secret: clientTestSecret,
}, },
} }
ciRepo := func() client.ClientIdentityRepo { ciRepo := func() client.ClientRepo {
repo, err := db.NewClientIdentityRepoFromClients(db.NewMemDB(), []oidc.ClientIdentity{ci}) repo, err := db.NewClientRepoFromClients(db.NewMemDB(), []client.Client{ci})
if err != nil { if err != nil {
t.Fatalf("Failed to create client identity repo: %v", err) t.Fatalf("Failed to create client identity repo: %v", err)
} }
@ -460,10 +460,10 @@ func TestServerTokenUnrecognizedKey(t *testing.T) {
sm := manager.NewSessionManager(db.NewSessionRepo(db.NewMemDB()), db.NewSessionKeyRepo(db.NewMemDB())) sm := manager.NewSessionManager(db.NewSessionRepo(db.NewMemDB()), db.NewSessionKeyRepo(db.NewMemDB()))
srv := &Server{ srv := &Server{
IssuerURL: url.URL{Scheme: "http", Host: "server.example.com"}, IssuerURL: url.URL{Scheme: "http", Host: "server.example.com"},
KeyManager: km, KeyManager: km,
SessionManager: sm, SessionManager: sm,
ClientIdentityRepo: ciRepo, ClientRepo: ciRepo,
} }
sessionID, err := sm.NewSession("connector_id", ci.Credentials.ID, "bogus", url.URL{}, "", false, []string{"openid", "offline_access"}) sessionID, err := sm.NewSession("connector_id", ci.Credentials.ID, "bogus", url.URL{}, "", false, []string{"openid", "offline_access"})
@ -569,8 +569,8 @@ func TestServerTokenFail(t *testing.T) {
km := &StaticKeyManager{ km := &StaticKeyManager{
signer: tt.signer, signer: tt.signer,
} }
ciRepo, err := db.NewClientIdentityRepoFromClients(db.NewMemDB(), []oidc.ClientIdentity{ ciRepo, err := db.NewClientRepoFromClients(db.NewMemDB(), []client.Client{
oidc.ClientIdentity{Credentials: ccFixture}, client.Client{Credentials: ccFixture},
}) })
if err != nil { if err != nil {
t.Errorf("case %d: failed to create client identity repo: %v", i, err) t.Errorf("case %d: failed to create client identity repo: %v", i, err)
@ -590,12 +590,12 @@ func TestServerTokenFail(t *testing.T) {
refreshTokenRepo := refreshtest.NewTestRefreshTokenRepo() refreshTokenRepo := refreshtest.NewTestRefreshTokenRepo()
srv := &Server{ srv := &Server{
IssuerURL: issuerURL, IssuerURL: issuerURL,
KeyManager: km, KeyManager: km,
SessionManager: sm, SessionManager: sm,
ClientIdentityRepo: ciRepo, ClientRepo: ciRepo,
UserRepo: userRepo, UserRepo: userRepo,
RefreshTokenRepo: refreshTokenRepo, RefreshTokenRepo: refreshTokenRepo,
} }
_, err = sm.NewSessionKey(sessionID) _, err = sm.NewSessionKey(sessionID)
@ -731,9 +731,9 @@ func TestServerRefreshToken(t *testing.T) {
signer: tt.signer, signer: tt.signer,
} }
ciRepo, err := db.NewClientIdentityRepoFromClients(db.NewMemDB(), []oidc.ClientIdentity{ ciRepo, err := db.NewClientRepoFromClients(db.NewMemDB(), []client.Client{
oidc.ClientIdentity{Credentials: credXXX}, client.Client{Credentials: credXXX},
oidc.ClientIdentity{Credentials: credYYY}, client.Client{Credentials: credYYY},
}) })
if err != nil { if err != nil {
t.Errorf("case %d: failed to create client identity repo: %v", i, err) t.Errorf("case %d: failed to create client identity repo: %v", i, err)
@ -748,11 +748,11 @@ func TestServerRefreshToken(t *testing.T) {
refreshTokenRepo := refreshtest.NewTestRefreshTokenRepo() refreshTokenRepo := refreshtest.NewTestRefreshTokenRepo()
srv := &Server{ srv := &Server{
IssuerURL: issuerURL, IssuerURL: issuerURL,
KeyManager: km, KeyManager: km,
ClientIdentityRepo: ciRepo, ClientRepo: ciRepo,
UserRepo: userRepo, UserRepo: userRepo,
RefreshTokenRepo: refreshTokenRepo, RefreshTokenRepo: refreshTokenRepo,
} }
if _, err := refreshTokenRepo.Create("testid-1", tt.clientID); err != nil { if _, err := refreshTokenRepo.Create("testid-1", tt.clientID); err != nil {
@ -784,9 +784,9 @@ func TestServerRefreshToken(t *testing.T) {
signer: signerFixture, signer: signerFixture,
} }
ciRepo, err := db.NewClientIdentityRepoFromClients(db.NewMemDB(), []oidc.ClientIdentity{ ciRepo, err := db.NewClientRepoFromClients(db.NewMemDB(), []client.Client{
oidc.ClientIdentity{Credentials: credXXX}, client.Client{Credentials: credXXX},
oidc.ClientIdentity{Credentials: credYYY}, client.Client{Credentials: credYYY},
}) })
if err != nil { if err != nil {
t.Fatalf("failed to create client identity repo: %v", err) t.Fatalf("failed to create client identity repo: %v", err)
@ -808,11 +808,11 @@ func TestServerRefreshToken(t *testing.T) {
refreshTokenRepo := refreshtest.NewTestRefreshTokenRepo() refreshTokenRepo := refreshtest.NewTestRefreshTokenRepo()
srv := &Server{ srv := &Server{
IssuerURL: issuerURL, IssuerURL: issuerURL,
KeyManager: km, KeyManager: km,
ClientIdentityRepo: ciRepo, ClientRepo: ciRepo,
UserRepo: userRepo, UserRepo: userRepo,
RefreshTokenRepo: refreshTokenRepo, RefreshTokenRepo: refreshTokenRepo,
} }
if _, err := refreshTokenRepo.Create("testid-2", credXXX.ID); err != nil { if _, err := refreshTokenRepo.Create("testid-2", credXXX.ID); err != nil {

View file

@ -73,12 +73,12 @@ var (
) )
type testFixtures struct { type testFixtures struct {
srv *Server srv *Server
userRepo user.UserRepo userRepo user.UserRepo
sessionManager *sessionmanager.SessionManager sessionManager *sessionmanager.SessionManager
emailer *email.TemplatizedEmailer emailer *email.TemplatizedEmailer
redirectURL url.URL redirectURL url.URL
clientIdentityRepo client.ClientIdentityRepo clientRepo client.ClientRepo
} }
func sequentialGenerateCodeFunc() sessionmanager.GenerateCodeFunc { func sequentialGenerateCodeFunc() sessionmanager.GenerateCodeFunc {
@ -136,8 +136,8 @@ func makeTestFixtures() (*testFixtures, error) {
return nil, err return nil, err
} }
clientIdentityRepo, err := db.NewClientIdentityRepoFromClients(db.NewMemDB(), []oidc.ClientIdentity{ clientRepo, err := db.NewClientRepoFromClients(db.NewMemDB(), []client.Client{
oidc.ClientIdentity{ client.Client{
Credentials: oidc.ClientCredentials{ Credentials: oidc.ClientCredentials{
ID: "XXX", ID: "XXX",
Secret: base64.URLEncoding.EncodeToString([]byte("secrete")), Secret: base64.URLEncoding.EncodeToString([]byte("secrete")),
@ -167,14 +167,14 @@ func makeTestFixtures() (*testFixtures, error) {
} }
srv := &Server{ srv := &Server{
IssuerURL: testIssuerURL, IssuerURL: testIssuerURL,
SessionManager: sessionManager, SessionManager: sessionManager,
ClientIdentityRepo: clientIdentityRepo, ClientRepo: clientRepo,
Templates: tpl, Templates: tpl,
UserRepo: userRepo, UserRepo: userRepo,
PasswordInfoRepo: pwRepo, PasswordInfoRepo: pwRepo,
UserManager: manager, UserManager: manager,
KeyManager: km, KeyManager: km,
} }
err = setTemplates(srv, tpl) err = setTemplates(srv, tpl)
@ -201,11 +201,11 @@ func makeTestFixtures() (*testFixtures, error) {
) )
return &testFixtures{ return &testFixtures{
srv: srv, srv: srv,
redirectURL: testRedirectURL, redirectURL: testRedirectURL,
userRepo: userRepo, userRepo: userRepo,
sessionManager: sessionManager, sessionManager: sessionManager,
emailer: emailer, emailer: emailer,
clientIdentityRepo: clientIdentityRepo, clientRepo: clientRepo,
}, nil }, nil
} }

View file

@ -39,10 +39,10 @@ type UserMgmtServer struct {
api *api.UsersAPI api *api.UsersAPI
jwtvFactory JWTVerifierFactory jwtvFactory JWTVerifierFactory
um *manager.UserManager um *manager.UserManager
cir client.ClientIdentityRepo cir client.ClientRepo
} }
func NewUserMgmtServer(userMgmtAPI *api.UsersAPI, jwtvFactory JWTVerifierFactory, um *manager.UserManager, cir client.ClientIdentityRepo) *UserMgmtServer { func NewUserMgmtServer(userMgmtAPI *api.UsersAPI, jwtvFactory JWTVerifierFactory, um *manager.UserManager, cir client.ClientRepo) *UserMgmtServer {
return &UserMgmtServer{ return &UserMgmtServer{
api: userMgmtAPI, api: userMgmtAPI,
jwtvFactory: jwtvFactory, jwtvFactory: jwtvFactory,

View file

@ -88,11 +88,11 @@ func (e Error) Error() string {
// calling User. It is assumed that the clientID has already validated as an // calling User. It is assumed that the clientID has already validated as an
// admin app before calling. // admin app before calling.
type UsersAPI struct { type UsersAPI struct {
manager *manager.UserManager manager *manager.UserManager
localConnectorID string localConnectorID string
clientIdentityRepo client.ClientIdentityRepo clientRepo client.ClientRepo
refreshRepo refresh.RefreshTokenRepo refreshRepo refresh.RefreshTokenRepo
emailer Emailer emailer Emailer
} }
type Emailer interface { type Emailer interface {
@ -107,11 +107,11 @@ type Creds struct {
// TODO(ericchiang): Don't pass a dbMap. See #385. // TODO(ericchiang): Don't pass a dbMap. See #385.
func NewUsersAPI(dbMap *gorp.DbMap, userManager *manager.UserManager, emailer Emailer, localConnectorID string) *UsersAPI { func NewUsersAPI(dbMap *gorp.DbMap, userManager *manager.UserManager, emailer Emailer, localConnectorID string) *UsersAPI {
return &UsersAPI{ return &UsersAPI{
manager: userManager, manager: userManager,
refreshRepo: db.NewRefreshTokenRepo(dbMap), refreshRepo: db.NewRefreshTokenRepo(dbMap),
clientIdentityRepo: db.NewClientIdentityRepo(dbMap), clientRepo: db.NewClientRepo(dbMap),
localConnectorID: localConnectorID, localConnectorID: localConnectorID,
emailer: emailer, emailer: emailer,
} }
} }
@ -157,7 +157,7 @@ func (u *UsersAPI) CreateUser(creds Creds, usr schema.User, redirURL url.URL) (s
return schema.UserCreateResponse{}, mapError(err) return schema.UserCreateResponse{}, mapError(err)
} }
metadata, err := u.clientIdentityRepo.Metadata(creds.ClientID) metadata, err := u.clientRepo.Metadata(creds.ClientID)
if err != nil { if err != nil {
return schema.UserCreateResponse{}, mapError(err) return schema.UserCreateResponse{}, mapError(err)
} }
@ -202,7 +202,7 @@ func (u *UsersAPI) ResendEmailInvitation(creds Creds, userID string, redirURL ur
return schema.ResendEmailInvitationResponse{}, ErrorUnauthorized return schema.ResendEmailInvitationResponse{}, ErrorUnauthorized
} }
metadata, err := u.clientIdentityRepo.Metadata(creds.ClientID) metadata, err := u.clientRepo.Metadata(creds.ClientID)
if err != nil { if err != nil {
return schema.ResendEmailInvitationResponse{}, mapError(err) return schema.ResendEmailInvitationResponse{}, mapError(err)
} }

View file

@ -11,6 +11,7 @@ import (
"github.com/jonboulle/clockwork" "github.com/jonboulle/clockwork"
"github.com/kylelemons/godebug/pretty" "github.com/kylelemons/godebug/pretty"
"github.com/coreos/dex/client"
"github.com/coreos/dex/connector" "github.com/coreos/dex/connector"
"github.com/coreos/dex/db" "github.com/coreos/dex/db"
schema "github.com/coreos/dex/schema/workerschema" schema "github.com/coreos/dex/schema/workerschema"
@ -155,7 +156,7 @@ func makeTestFixtures() (*UsersAPI, *testEmailer) {
mgr := manager.NewUserManager(ur, pwr, ccr, db.TransactionFactory(dbMap), manager.ManagerOptions{}) mgr := manager.NewUserManager(ur, pwr, ccr, db.TransactionFactory(dbMap), manager.ManagerOptions{})
mgr.Clock = clock mgr.Clock = clock
ci := oidc.ClientIdentity{ ci := client.Client{
Credentials: oidc.ClientCredentials{ Credentials: oidc.ClientCredentials{
ID: "XXX", ID: "XXX",
Secret: base64.URLEncoding.EncodeToString([]byte("secrete")), Secret: base64.URLEncoding.EncodeToString([]byte("secrete")),
@ -166,8 +167,8 @@ func makeTestFixtures() (*UsersAPI, *testEmailer) {
}, },
}, },
} }
if _, err := db.NewClientIdentityRepoFromClients(dbMap, []oidc.ClientIdentity{ci}); err != nil { if _, err := db.NewClientRepoFromClients(dbMap, []client.Client{ci}); err != nil {
panic("Failed to create client identity repo: " + err.Error()) panic("Failed to create client repo: " + err.Error())
} }
// Used in TestRevokeRefreshToken test. // Used in TestRevokeRefreshToken test.