From 95757e87793208cf640a444a405977a01d39a3a5 Mon Sep 17 00:00:00 2001 From: Bobby Rullo Date: Thu, 14 Apr 2016 15:57:53 -0700 Subject: [PATCH] *: Client Repo now deals with custom Client object This is instead of oidc.ClientIdentity. This makes it easier to add new fields custom to dex to the client. --- admin/api.go | 37 ++++++++++----- client/client.go | 47 ++++++++++++++++++- db/client.go | 70 +++++++++++++++++----------- db/refresh.go | 8 ++-- functional/db_test.go | 42 ++++++++++++++--- functional/repo/client_repo_test.go | 6 +-- functional/repo/refresh_repo_test.go | 5 +- integration/admin_api_test.go | 15 +++--- integration/client_api_test.go | 5 +- integration/oidc_test.go | 15 +++--- integration/user_api_test.go | 6 +-- refresh/repo.go | 4 +- server/admin.go | 2 +- server/auth_middleware_test.go | 4 +- server/client_registration.go | 9 +++- server/client_resource.go | 10 ++-- server/client_resource_test.go | 13 +++--- server/config.go | 41 ++-------------- server/http_test.go | 8 ++-- server/server_test.go | 38 +++++++-------- server/testutil.go | 4 +- user/api/api_test.go | 8 ++-- 22 files changed, 241 insertions(+), 156 deletions(-) diff --git a/admin/api.go b/admin/api.go index d65c71f9..22875c3e 100644 --- a/admin/api.go +++ b/admin/api.go @@ -116,25 +116,38 @@ func (a *AdminAPI) GetState() (adminschema.State, error) { return state, nil } -type ClientRegistrationRequest struct { - IsAdmin bool `json:"isAdmin"` - Client oidc.ClientMetadata `json:"client"` -} +func (a *AdminAPI) CreateClient(req adminschema.ClientCreateRequest) (adminschema.ClientCreateResponse, error) { + cli, err := adminschema.MapSchemaClientToClient(*req.Client) + if err != nil { + // TODO should be 400s + return adminschema.ClientCreateResponse{}, mapError(err) + } -func (a *AdminAPI) CreateClient(req ClientRegistrationRequest) (oidc.ClientRegistrationResponse, error) { - if err := req.Client.Valid(); err != nil { - return oidc.ClientRegistrationResponse{}, mapError(err) + if err := cli.Metadata.Valid(); err != nil { + // TODO make sure this is not 500 + return adminschema.ClientCreateResponse{}, mapError(err) } + // metadata is guarenteed to have at least one redirect_uri by earlier validation. - id, err := oidc.GenClientID(req.Client.RedirectURIs[0].Host) + id, err := oidc.GenClientID(cli.Metadata.RedirectURIs[0].Host) if err != nil { - return oidc.ClientRegistrationResponse{}, mapError(err) + return adminschema.ClientCreateResponse{}, mapError(err) } - c, err := a.clientIdentityRepo.New(id, req.Client, req.IsAdmin) + + cli.Credentials.ID = id + + creds, err := a.clientIdentityRepo.New(cli) 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 + + req.Client.Id = creds.ID + req.Client.Secret = creds.Secret + return adminschema.ClientCreateResponse{ + Client: req.Client, + }, nil + + // github.com/coreos/dex/integrationoidc.ClientRegistrationResponse{ClientID: c.ID, ClientSecret: c.Secret, ClientMetadata: req.Client.Metadata}, nil } func mapError(e error) error { diff --git a/client/client.go b/client/client.go index 20531603..5a222c81 100644 --- a/client/client.go +++ b/client/client.go @@ -1,7 +1,9 @@ package client import ( + "encoding/json" "errors" + "io" "net/url" "reflect" @@ -15,7 +17,15 @@ var ( ErrorNotFound = errors.New("no data found") ) +type Client struct { + Credentials oidc.ClientCredentials + Metadata oidc.ClientMetadata + Admin bool +} + type ClientIdentityRepo interface { + Get(clientID string) (Client, error) + // Metadata returns one matching ClientMetadata if the given client // exists, otherwise nil. The returned error will be non-nil only // if the repo was unable to determine client existence. @@ -28,12 +38,12 @@ type ClientIdentityRepo interface { Authenticate(creds oidc.ClientCredentials) (bool, error) // All returns all registered Client Identities. - All() ([]oidc.ClientIdentity, error) + All() ([]Client, error) // New registers a ClientIdentity with the repo for the given metadata. // An unused ID must be provided. A corresponding secret will be returned // 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 @@ -64,3 +74,36 @@ func ValidRedirectURL(rURL *url.URL, redirectURLs []url.URL) (url.URL, error) { } 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 { + 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 +} diff --git a/db/client.go b/db/client.go index a0f54846..86e81afb 100644 --- a/db/client.go +++ b/db/client.go @@ -41,21 +41,29 @@ func init() { }) } -func newClientIdentityModel(id string, secret []byte, meta *oidc.ClientMetadata) (*clientIdentityModel, error) { - hashed, err := bcrypt.GenerateFromPassword(secret, bcryptHashCost) +func newClientIdentityModel(cli client.Client) (*clientIdentityModel, error) { + secretBytes, err := base64.URLEncoding.DecodeString(cli.Credentials.Secret) if err != nil { return nil, err } - bmeta, err := json.Marshal(meta) + hashed, err := bcrypt.GenerateFromPassword([]byte( + secretBytes), + bcryptHashCost) + if err != nil { + return nil, err + } + + bmeta, err := json.Marshal(&cli.Metadata) if err != nil { return nil, err } cim := clientIdentityModel{ - ID: id, + ID: cli.Credentials.ID, Secret: hashed, Metadata: string(bmeta), + DexAdmin: cli.Admin, } return &cim, nil @@ -68,12 +76,13 @@ type clientIdentityModel struct { DexAdmin bool `db:"dex_admin"` } -func (m *clientIdentityModel) ClientIdentity() (*oidc.ClientIdentity, error) { - ci := oidc.ClientIdentity{ +func (m *clientIdentityModel) Client() (*client.Client, error) { + ci := client.Client{ Credentials: oidc.ClientCredentials{ ID: m.ID, Secret: string(m.Secret), }, + Admin: m.DexAdmin, } if err := json.Unmarshal([]byte(m.Metadata), &ci.Metadata); err != nil { @@ -91,7 +100,7 @@ func newClientIdentityRepo(dbm *gorp.DbMap) *clientIdentityRepo { return &clientIdentityRepo{db: &db{dbm}} } -func NewClientIdentityRepoFromClients(dbm *gorp.DbMap, clients []oidc.ClientIdentity) (client.ClientIdentityRepo, error) { +func NewClientIdentityRepoFromClients(dbm *gorp.DbMap, clients []client.Client) (client.ClientIdentityRepo, error) { repo := newClientIdentityRepo(dbm) tx, err := repo.begin() if err != nil { @@ -103,12 +112,7 @@ func NewClientIdentityRepoFromClients(dbm *gorp.DbMap, clients []oidc.ClientIden if c.Credentials.Secret == "" { return nil, fmt.Errorf("client %q has no secret", c.Credentials.ID) } - dec, err := base64.URLEncoding.DecodeString(c.Credentials.Secret) - 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) + cm, err := newClientIdentityModel(c) if err != nil { return nil, err } @@ -127,27 +131,37 @@ type clientIdentityRepo struct { *db } -func (r *clientIdentityRepo) Metadata(clientID string) (*oidc.ClientMetadata, error) { +func (r *clientIdentityRepo) Get(clientID string) (client.Client, error) { m, err := r.executor(nil).Get(clientIdentityModel{}, clientID) + if err == sql.ErrNoRows || m == nil { - return nil, client.ErrorNotFound + return client.Client{}, client.ErrorNotFound } if err != nil { - return nil, err + return client.Client{}, err } cim, ok := m.(*clientIdentityModel) if !ok { - log.Errorf("expected clientIdentityModel but found %v", reflect.TypeOf(m)) - return nil, errors.New("unrecognized model") + log.Errorf("expected clientModel but found %v", reflect.TypeOf(m)) + return client.Client{}, errors.New("unrecognized model") } - ci, err := cim.ClientIdentity() + ci, err := cim.Client() + if err != nil { + return client.Client{}, err + } + + return *ci, nil +} + +func (r *clientIdentityRepo) Metadata(clientID string) (*oidc.ClientMetadata, error) { + c, err := r.Get(clientID) if err != nil { return nil, err } - return &ci.Metadata, nil + return &c.Metadata, nil } func (r *clientIdentityRepo) IsDexAdmin(clientID string) (bool, error) { @@ -238,17 +252,17 @@ func isAlreadyExistsErr(err error) bool { return false } -func (r *clientIdentityRepo) New(id string, meta oidc.ClientMetadata, admin bool) (*oidc.ClientCredentials, error) { +func (r *clientIdentityRepo) New(cli client.Client) (*oidc.ClientCredentials, error) { secret, err := pcrypto.RandBytes(maxSecretLength) if err != nil { return nil, err } - cim, err := newClientIdentityModel(id, secret, &meta) + cli.Credentials.Secret = base64.URLEncoding.EncodeToString(secret) + cim, err := newClientIdentityModel(cli) if err != nil { return nil, err } - cim.DexAdmin = admin if err := r.executor(nil).Insert(cim); err != nil { if isAlreadyExistsErr(err) { @@ -258,14 +272,14 @@ func (r *clientIdentityRepo) New(id string, meta oidc.ClientMetadata, admin bool } cc := oidc.ClientCredentials{ - ID: id, - Secret: base64.URLEncoding.EncodeToString(secret), + ID: cli.Credentials.ID, + Secret: cli.Credentials.Secret, } return &cc, nil } -func (r *clientIdentityRepo) All() ([]oidc.ClientIdentity, error) { +func (r *clientIdentityRepo) All() ([]client.Client, error) { qt := r.quote(clientIdentityTableName) q := fmt.Sprintf("SELECT * FROM %s", qt) objs, err := r.executor(nil).Select(&clientIdentityModel{}, q) @@ -273,14 +287,14 @@ func (r *clientIdentityRepo) All() ([]oidc.ClientIdentity, error) { return nil, err } - cs := make([]oidc.ClientIdentity, len(objs)) + cs := make([]client.Client, len(objs)) for i, obj := range objs { m, ok := obj.(*clientIdentityModel) if !ok { return nil, errors.New("unable to cast client identity to clientIdentityModel") } - ci, err := m.ClientIdentity() + ci, err := m.Client() if err != nil { return nil, err } diff --git a/db/refresh.go b/db/refresh.go index 86495748..1e71c60f 100644 --- a/db/refresh.go +++ b/db/refresh.go @@ -11,10 +11,10 @@ import ( "github.com/go-gorp/gorp" "golang.org/x/crypto/bcrypt" + "github.com/coreos/dex/client" "github.com/coreos/dex/pkg/log" "github.com/coreos/dex/refresh" "github.com/coreos/dex/repo" - "github.com/coreos/go-oidc/oidc" ) const ( @@ -186,7 +186,7 @@ func (r *refreshTokenRepo) RevokeTokensForClient(userID, clientID string) error 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 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)) @@ -196,9 +196,9 @@ func (r *refreshTokenRepo) ClientsWithRefreshTokens(userID string) ([]oidc.Clien return nil, err } - c := make([]oidc.ClientIdentity, len(clients)) + c := make([]client.Client, len(clients)) for i, client := range clients { - ident, err := client.ClientIdentity() + ident, err := client.Client() if err != nil { return nil, err } diff --git a/functional/db_test.go b/functional/db_test.go index 97efdfe5..7690a5b0 100644 --- a/functional/db_test.go +++ b/functional/db_test.go @@ -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 { t.Fatalf(err.Error()) } @@ -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) } @@ -237,7 +247,12 @@ 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") } } @@ -251,7 +266,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 { t.Fatalf(err.Error()) } @@ -299,7 +319,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 { t.Fatalf(err.Error()) } @@ -322,7 +347,12 @@ func TestDBClientIdentityAll(t *testing.T) { 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 { t.Fatalf(err.Error()) } diff --git a/functional/repo/client_repo_test.go b/functional/repo/client_repo_test.go index fff50ca1..62097b3e 100644 --- a/functional/repo/client_repo_test.go +++ b/functional/repo/client_repo_test.go @@ -14,8 +14,8 @@ import ( ) var ( - testClients = []oidc.ClientIdentity{ - oidc.ClientIdentity{ + testClients = []client.Client{ + client.Client{ Credentials: oidc.ClientCredentials{ ID: "client1", Secret: base64.URLEncoding.EncodeToString([]byte("secret-1")), @@ -30,7 +30,7 @@ var ( }, }, }, - oidc.ClientIdentity{ + client.Client{ Credentials: oidc.ClientCredentials{ ID: "client2", Secret: base64.URLEncoding.EncodeToString([]byte("secret-2")), diff --git a/functional/repo/refresh_repo_test.go b/functional/repo/refresh_repo_test.go index 197f59ad..40862663 100644 --- a/functional/repo/refresh_repo_test.go +++ b/functional/repo/refresh_repo_test.go @@ -11,12 +11,13 @@ import ( "github.com/go-gorp/gorp" "github.com/kylelemons/godebug/pretty" + "github.com/coreos/dex/client" "github.com/coreos/dex/db" "github.com/coreos/dex/refresh" "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 if dsn := os.Getenv("DEX_TEST_DSN"); dsn == "" { dbMap = db.NewMemDB() @@ -35,7 +36,7 @@ func newRefreshRepo(t *testing.T, users []user.UserWithRemoteIdentities, clients func TestRefreshTokenRepo(t *testing.T) { clientID := "client1" userID := "user1" - clients := []oidc.ClientIdentity{ + clients := []client.Client{ { Credentials: oidc.ClientCredentials{ ID: clientID, diff --git a/integration/admin_api_test.go b/integration/admin_api_test.go index 861e9788..0714b72f 100644 --- a/integration/admin_api_test.go +++ b/integration/admin_api_test.go @@ -276,21 +276,24 @@ func TestCreateClient(t *testing.T) { for i, tt := range tests { err := func() error { f := makeAdminAPITestFixtures() - req := &adminschema.ClientCreateRequestClient{} - for _, redirectURI := range tt.client.RedirectURIs { - req.Redirect_uris = append(req.Redirect_uris, redirectURI.String()) + req := &adminschema.ClientCreateRequest{ + Client: &adminschema.Client{}, } - resp, err := f.adClient.Client.Create(&adminschema.ClientCreateRequest{Client: req}).Do() + + for _, redirectURI := range tt.client.RedirectURIs { + req.Client.RedirectURIs = append(req.Client.RedirectURIs, redirectURI.String()) + } + resp, err := f.adClient.Client.Create(req).Do() if err != nil { if tt.wantError { return nil } return err } - if resp.Client_id == "" { + if resp.Client.Id == "" { return errors.New("no client id returned") } - if resp.Client_secret == "" { + if resp.Client.Secret == "" { return errors.New("no client secret returned") } return nil diff --git a/integration/client_api_test.go b/integration/client_api_test.go index 3b80eb33..fd3e63bb 100644 --- a/integration/client_api_test.go +++ b/integration/client_api_test.go @@ -7,12 +7,13 @@ import ( "reflect" "testing" + "github.com/coreos/dex/client" schema "github.com/coreos/dex/schema/workerschema" "github.com/coreos/go-oidc/oidc" ) func TestClientCreate(t *testing.T) { - ci := oidc.ClientIdentity{ + ci := client.Client{ Credentials: oidc.ClientCredentials{ ID: "72de74a9", 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) if err != nil { diff --git a/integration/oidc_test.go b/integration/oidc_test.go index 48e7d862..1195e118 100644 --- a/integration/oidc_test.go +++ b/integration/oidc_test.go @@ -9,6 +9,7 @@ import ( "testing" "time" + "github.com/coreos/dex/client" "github.com/coreos/dex/connector" "github.com/coreos/dex/db" phttp "github.com/coreos/dex/pkg/http" @@ -22,7 +23,7 @@ import ( "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() k, err := key.GeneratePrivateKey() if err != nil { @@ -50,7 +51,7 @@ func mockServer(cis []oidc.ClientIdentity) (*server.Server, error) { 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() sClient := &phttp.HandlerClient{Handler: hdlr} @@ -75,7 +76,7 @@ func mockClient(srv *server.Server, ci oidc.ClientIdentity) (*oidc.Client, error 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 if user != nil { expectedSub, expectedName = user.ID, user.DisplayName @@ -116,7 +117,7 @@ func TestHTTPExchangeTokenRefreshToken(t *testing.T) { ID: "local", } - ci := oidc.ClientIdentity{ + ci := client.Client{ Credentials: oidc.ClientCredentials{ ID: "72de74a9", Secret: base64.URLEncoding.EncodeToString([]byte("XXX")), @@ -124,7 +125,7 @@ func TestHTTPExchangeTokenRefreshToken(t *testing.T) { } dbMap := db.NewMemDB() - cir, err := db.NewClientIdentityRepoFromClients(dbMap, []oidc.ClientIdentity{ci}) + cir, err := db.NewClientIdentityRepoFromClients(dbMap, []client.Client{ci}) if err != nil { t.Fatalf("Failed to create client identity repo: " + err.Error()) } @@ -262,13 +263,13 @@ func TestHTTPExchangeTokenRefreshToken(t *testing.T) { } func TestHTTPClientCredsToken(t *testing.T) { - ci := oidc.ClientIdentity{ + ci := client.Client{ Credentials: oidc.ClientCredentials{ ID: "72de74a9", Secret: base64.URLEncoding.EncodeToString([]byte("XXX")), }, } - cis := []oidc.ClientIdentity{ci} + cis := []client.Client{ci} srv, err := mockServer(cis) if err != nil { diff --git a/integration/user_api_test.go b/integration/user_api_test.go index 14a2f161..6795f73d 100644 --- a/integration/user_api_test.go +++ b/integration/user_api_test.go @@ -102,8 +102,8 @@ func makeUserAPITestFixtures() *userAPITestFixtures { dbMap, _, _, um := makeUserObjects(userUsers, userPasswords) cir := func() client.ClientIdentityRepo { - repo, err := db.NewClientIdentityRepoFromClients(dbMap, []oidc.ClientIdentity{ - oidc.ClientIdentity{ + repo, err := db.NewClientIdentityRepoFromClients(dbMap, []client.Client{ + client.Client{ Credentials: oidc.ClientCredentials{ ID: testClientID, Secret: testClientSecret, @@ -114,7 +114,7 @@ func makeUserAPITestFixtures() *userAPITestFixtures { }, }, }, - oidc.ClientIdentity{ + client.Client{ Credentials: oidc.ClientCredentials{ ID: userBadClientID, Secret: base64.URLEncoding.EncodeToString([]byte("secret")), diff --git a/refresh/repo.go b/refresh/repo.go index 607169fe..8f196a15 100644 --- a/refresh/repo.go +++ b/refresh/repo.go @@ -4,7 +4,7 @@ import ( "crypto/rand" "errors" - "github.com/coreos/go-oidc/oidc" + "github.com/coreos/dex/client" ) const ( @@ -54,5 +54,5 @@ type RefreshTokenRepo interface { RevokeTokensForClient(userID, clientID string) error // 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) } diff --git a/server/admin.go b/server/admin.go index 21bd28d5..43d29c4f 100644 --- a/server/admin.go +++ b/server/admin.go @@ -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) { - var req admin.ClientRegistrationRequest + var req = adminschema.ClientCreateRequest{} if err := json.NewDecoder(r.Body).Decode(&req); err != nil { writeInvalidRequest(w, "cannot parse JSON body") return diff --git a/server/auth_middleware_test.go b/server/auth_middleware_test.go index 0b0f1b2a..3dfb1695 100644 --- a/server/auth_middleware_test.go +++ b/server/auth_middleware_test.go @@ -26,7 +26,7 @@ func TestClientToken(t *testing.T) { now := time.Now() tomorrow := now.Add(24 * time.Hour) validClientID := "valid-client" - ci := oidc.ClientIdentity{ + ci := client.Client{ Credentials: oidc.ClientCredentials{ ID: validClientID, 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.NewClientIdentityRepoFromClients(db.NewMemDB(), []client.Client{ci}) if err != nil { t.Fatalf("Failed to create client identity repo: %v", err) } diff --git a/server/client_registration.go b/server/client_registration.go index 13dacbeb..f1c21e99 100644 --- a/server/client_registration.go +++ b/server/client_registration.go @@ -4,7 +4,9 @@ import ( "encoding/json" "net/http" + "github.com/coreos/dex/client" "github.com/coreos/dex/pkg/log" + "github.com/coreos/go-oidc/oauth2" "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") } - creds, err := s.ClientIdentityRepo.New(id, clientMetadata, false) + creds, err := s.ClientIdentityRepo.New(client.Client{ + Credentials: oidc.ClientCredentials{ + ID: id, + }, + Metadata: clientMetadata, + }) if err != nil { log.Errorf("Failed to create new client identity: %v", err) return nil, newAPIError(oauth2.ErrorServerError, "unable to save client metadata") diff --git a/server/client_resource.go b/server/client_resource.go index c5134779..c69e9062 100644 --- a/server/client_resource.go +++ b/server/client_resource.go @@ -49,7 +49,7 @@ func (c *clientResource) list(w http.ResponseWriter, r *http.Request) { scs := make([]*schema.Client, len(cs)) for i, ci := range cs { - sc := schema.MapClientIdentityToSchemaClient(ci) + sc := schema.MapClientToSchemaClient(ci) scs[i] = &sc } @@ -76,7 +76,7 @@ func (c *clientResource) create(w http.ResponseWriter, r *http.Request) { return } - ci, err := schema.MapSchemaClientToClientIdentity(sc) + ci, err := schema.MapSchemaClientToClient(sc) if err != nil { log.Debugf("Invalid request data: %v", err) 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 } - creds, err := c.repo.New(clientID, ci.Metadata, false) + ci.Credentials.ID = clientID + creds, err := c.repo.New(ci) + if err != nil { log.Errorf("Failed creating client: %v", err) 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 - ssc := schema.MapClientIdentityToSchemaClientWithSecret(ci) + ssc := schema.MapClientToSchemaClientWithSecret(ci) w.Header().Add("Location", phttp.NewResourceLocation(r.URL, ci.Credentials.ID)) writeResponseWithBody(w, http.StatusCreated, ssc) } diff --git a/server/client_resource_test.go b/server/client_resource_test.go index ab527120..ceb4232e 100644 --- a/server/client_resource_test.go +++ b/server/client_resource_test.go @@ -14,6 +14,7 @@ import ( "strings" "testing" + "github.com/coreos/dex/client" "github.com/coreos/dex/db" schema "github.com/coreos/dex/schema/workerschema" "github.com/coreos/go-oidc/oidc" @@ -177,7 +178,7 @@ func TestList(t *testing.T) { } tests := []struct { - cs []oidc.ClientIdentity + cs []client.Client want []*schema.Client }{ // empty repo @@ -187,8 +188,8 @@ func TestList(t *testing.T) { }, // single client { - cs: []oidc.ClientIdentity{ - oidc.ClientIdentity{ + cs: []client.Client{ + client.Client{ Credentials: oidc.ClientCredentials{ID: "foo", Secret: b64Encode("bar")}, Metadata: oidc.ClientMetadata{ RedirectURIs: []url.URL{ @@ -206,8 +207,8 @@ func TestList(t *testing.T) { }, // multi client { - cs: []oidc.ClientIdentity{ - oidc.ClientIdentity{ + cs: []client.Client{ + client.Client{ Credentials: oidc.ClientCredentials{ID: "foo", Secret: b64Encode("bar")}, Metadata: oidc.ClientMetadata{ RedirectURIs: []url.URL{ @@ -215,7 +216,7 @@ func TestList(t *testing.T) { }, }, }, - oidc.ClientIdentity{ + client.Client{ Credentials: oidc.ClientCredentials{ID: "biz", Secret: b64Encode("bang")}, Metadata: oidc.ClientMetadata{ RedirectURIs: []url.URL{ diff --git a/server/config.go b/server/config.go index 50df485c..bb505510 100644 --- a/server/config.go +++ b/server/config.go @@ -13,10 +13,10 @@ import ( "time" "github.com/coreos/go-oidc/key" - "github.com/coreos/go-oidc/oidc" "github.com/coreos/pkg/health" "github.com/go-gorp/gorp" + "github.com/coreos/dex/client" "github.com/coreos/dex/connector" "github.com/coreos/dex/db" "github.com/coreos/dex/email" @@ -214,47 +214,14 @@ func loadUsersFromReader(r io.Reader) (users []user.UserWithRemoteIdentities, pw return } -// loadClients parses the clients.json file and returns the clients to be created. -func loadClients(filepath string) ([]oidc.ClientIdentity, error) { +// loadClients parses the clients.json file and returns a list of clients. +func loadClients(filepath string) ([]client.Client, error) { f, err := os.Open(filepath) if err != nil { return nil, err } defer f.Close() - return loadClientsFromReader(f) -} - -func loadClientsFromReader(r io.Reader) ([]oidc.ClientIdentity, error) { - var c []struct { - ID string `json:"id"` - Secret string `json:"secret"` - RedirectURLs []string `json:"redirectURLs"` - } - if err := json.NewDecoder(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 + return client.ClientsFromReader(f) } func (cfg *MultiServerConfig) Configure(srv *Server) error { diff --git a/server/http_test.go b/server/http_test.go index 319f9d91..c96a6bbb 100644 --- a/server/http_test.go +++ b/server/http_test.go @@ -79,8 +79,8 @@ func TestHandleAuthFuncResponsesSingleRedirectURL(t *testing.T) { IssuerURL: url.URL{Scheme: "http", Host: "server.example.com"}, SessionManager: manager.NewSessionManager(db.NewSessionRepo(db.NewMemDB()), db.NewSessionKeyRepo(db.NewMemDB())), ClientIdentityRepo: func() client.ClientIdentityRepo { - repo, err := db.NewClientIdentityRepoFromClients(db.NewMemDB(), []oidc.ClientIdentity{ - oidc.ClientIdentity{ + repo, err := db.NewClientIdentityRepoFromClients(db.NewMemDB(), []client.Client{ + client.Client{ Credentials: oidc.ClientCredentials{ ID: "XXX", Secret: base64.URLEncoding.EncodeToString([]byte("secrete")), @@ -231,8 +231,8 @@ func TestHandleAuthFuncResponsesMultipleRedirectURLs(t *testing.T) { IssuerURL: url.URL{Scheme: "http", Host: "server.example.com"}, SessionManager: manager.NewSessionManager(db.NewSessionRepo(db.NewMemDB()), db.NewSessionKeyRepo(db.NewMemDB())), ClientIdentityRepo: func() client.ClientIdentityRepo { - repo, err := db.NewClientIdentityRepoFromClients(db.NewMemDB(), []oidc.ClientIdentity{ - oidc.ClientIdentity{ + repo, err := db.NewClientIdentityRepoFromClients(db.NewMemDB(), []client.Client{ + client.Client{ Credentials: oidc.ClientCredentials{ ID: "XXX", Secret: base64.URLEncoding.EncodeToString([]byte("secrete")), diff --git a/server/server_test.go b/server/server_test.go index 0aec6468..17a2e814 100644 --- a/server/server_test.go +++ b/server/server_test.go @@ -130,7 +130,7 @@ func TestServerNewSession(t *testing.T) { state := "pants" nonce := "oncenay" - ci := oidc.ClientIdentity{ + ci := client.Client{ Credentials: oidc.ClientCredentials{ ID: "XXX", Secret: "secrete", @@ -179,7 +179,7 @@ func TestServerNewSession(t *testing.T) { } func TestServerLogin(t *testing.T) { - ci := oidc.ClientIdentity{ + ci := client.Client{ Credentials: oidc.ClientCredentials{ ID: "XXX", Secret: clientTestSecret, @@ -195,7 +195,7 @@ func TestServerLogin(t *testing.T) { }, } ciRepo := func() client.ClientIdentityRepo { - repo, err := db.NewClientIdentityRepoFromClients(db.NewMemDB(), []oidc.ClientIdentity{ci}) + repo, err := db.NewClientIdentityRepoFromClients(db.NewMemDB(), []client.Client{ci}) if err != nil { t.Fatalf("Failed to create client identity repo: %v", err) } @@ -245,8 +245,8 @@ func TestServerLogin(t *testing.T) { func TestServerLoginUnrecognizedSessionKey(t *testing.T) { ciRepo := func() client.ClientIdentityRepo { - repo, err := db.NewClientIdentityRepoFromClients(db.NewMemDB(), []oidc.ClientIdentity{ - oidc.ClientIdentity{ + repo, err := db.NewClientIdentityRepoFromClients(db.NewMemDB(), []client.Client{ + client.Client{ Credentials: oidc.ClientCredentials{ ID: "XXX", Secret: clientTestSecret, }, @@ -281,7 +281,7 @@ func TestServerLoginUnrecognizedSessionKey(t *testing.T) { } func TestServerLoginDisabledUser(t *testing.T) { - ci := oidc.ClientIdentity{ + ci := client.Client{ Credentials: oidc.ClientCredentials{ ID: "XXX", Secret: clientTestSecret, @@ -297,7 +297,7 @@ func TestServerLoginDisabledUser(t *testing.T) { }, } ciRepo := func() client.ClientIdentityRepo { - repo, err := db.NewClientIdentityRepoFromClients(db.NewMemDB(), []oidc.ClientIdentity{ci}) + repo, err := db.NewClientIdentityRepoFromClients(db.NewMemDB(), []client.Client{ci}) if err != nil { t.Fatalf("Failed to create client identity repo: %v", err) } @@ -355,14 +355,14 @@ func TestServerLoginDisabledUser(t *testing.T) { } func TestServerCodeToken(t *testing.T) { - ci := oidc.ClientIdentity{ + ci := client.Client{ Credentials: oidc.ClientCredentials{ ID: "XXX", Secret: clientTestSecret, }, } ciRepo := func() client.ClientIdentityRepo { - repo, err := db.NewClientIdentityRepoFromClients(db.NewMemDB(), []oidc.ClientIdentity{ci}) + repo, err := db.NewClientIdentityRepoFromClients(db.NewMemDB(), []client.Client{ci}) if err != nil { t.Fatalf("Failed to create client identity repo: %v", err) } @@ -441,14 +441,14 @@ func TestServerCodeToken(t *testing.T) { } func TestServerTokenUnrecognizedKey(t *testing.T) { - ci := oidc.ClientIdentity{ + ci := client.Client{ Credentials: oidc.ClientCredentials{ ID: "XXX", Secret: clientTestSecret, }, } ciRepo := func() client.ClientIdentityRepo { - repo, err := db.NewClientIdentityRepoFromClients(db.NewMemDB(), []oidc.ClientIdentity{ci}) + repo, err := db.NewClientIdentityRepoFromClients(db.NewMemDB(), []client.Client{ci}) if err != nil { t.Fatalf("Failed to create client identity repo: %v", err) } @@ -569,8 +569,8 @@ func TestServerTokenFail(t *testing.T) { km := &StaticKeyManager{ signer: tt.signer, } - ciRepo, err := db.NewClientIdentityRepoFromClients(db.NewMemDB(), []oidc.ClientIdentity{ - oidc.ClientIdentity{Credentials: ccFixture}, + ciRepo, err := db.NewClientIdentityRepoFromClients(db.NewMemDB(), []client.Client{ + client.Client{Credentials: ccFixture}, }) if err != nil { t.Errorf("case %d: failed to create client identity repo: %v", i, err) @@ -731,9 +731,9 @@ func TestServerRefreshToken(t *testing.T) { signer: tt.signer, } - ciRepo, err := db.NewClientIdentityRepoFromClients(db.NewMemDB(), []oidc.ClientIdentity{ - oidc.ClientIdentity{Credentials: credXXX}, - oidc.ClientIdentity{Credentials: credYYY}, + ciRepo, err := db.NewClientIdentityRepoFromClients(db.NewMemDB(), []client.Client{ + client.Client{Credentials: credXXX}, + client.Client{Credentials: credYYY}, }) if err != nil { t.Errorf("case %d: failed to create client identity repo: %v", i, err) @@ -784,9 +784,9 @@ func TestServerRefreshToken(t *testing.T) { signer: signerFixture, } - ciRepo, err := db.NewClientIdentityRepoFromClients(db.NewMemDB(), []oidc.ClientIdentity{ - oidc.ClientIdentity{Credentials: credXXX}, - oidc.ClientIdentity{Credentials: credYYY}, + ciRepo, err := db.NewClientIdentityRepoFromClients(db.NewMemDB(), []client.Client{ + client.Client{Credentials: credXXX}, + client.Client{Credentials: credYYY}, }) if err != nil { t.Fatalf("failed to create client identity repo: %v", err) diff --git a/server/testutil.go b/server/testutil.go index c1d5ce31..71cd05a2 100644 --- a/server/testutil.go +++ b/server/testutil.go @@ -136,8 +136,8 @@ func makeTestFixtures() (*testFixtures, error) { return nil, err } - clientIdentityRepo, err := db.NewClientIdentityRepoFromClients(db.NewMemDB(), []oidc.ClientIdentity{ - oidc.ClientIdentity{ + clientIdentityRepo, err := db.NewClientIdentityRepoFromClients(db.NewMemDB(), []client.Client{ + client.Client{ Credentials: oidc.ClientCredentials{ ID: "XXX", Secret: base64.URLEncoding.EncodeToString([]byte("secrete")), diff --git a/user/api/api_test.go b/user/api/api_test.go index de5b62d0..66d82552 100644 --- a/user/api/api_test.go +++ b/user/api/api_test.go @@ -11,6 +11,7 @@ import ( "github.com/jonboulle/clockwork" "github.com/kylelemons/godebug/pretty" + "github.com/coreos/dex/client" "github.com/coreos/dex/connector" "github.com/coreos/dex/db" 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.Clock = clock - ci := oidc.ClientIdentity{ + ci := client.Client{ Credentials: oidc.ClientCredentials{ ID: "XXX", Secret: base64.URLEncoding.EncodeToString([]byte("secrete")), @@ -166,8 +167,9 @@ func makeTestFixtures() (*UsersAPI, *testEmailer) { }, }, } - if _, err := db.NewClientIdentityRepoFromClients(dbMap, []oidc.ClientIdentity{ci}); err != nil { - panic("Failed to create client identity repo: " + err.Error()) + + if _, err := db.NewClientIdentityRepoFromClients(dbMap, []client.Client{ci}); err != nil { + panic("Failed to create client repo: " + err.Error()) } // Used in TestRevokeRefreshToken test.