diff --git a/cmd/dexctl/driver_api.go b/cmd/dexctl/driver_api.go deleted file mode 100644 index ef05bdee..00000000 --- a/cmd/dexctl/driver_api.go +++ /dev/null @@ -1,71 +0,0 @@ -package main - -import ( - "errors" - "net/http" - - "github.com/coreos/dex/connector" - schema "github.com/coreos/dex/schema/workerschema" - "github.com/coreos/go-oidc/oidc" -) - -func newAPIDriver(pcfg oidc.ProviderConfig, creds oidc.ClientCredentials) (driver, error) { - ccfg := oidc.ClientConfig{ - ProviderConfig: pcfg, - Credentials: creds, - } - oc, err := oidc.NewClient(ccfg) - if err != nil { - return nil, err - } - - trans := &oidc.AuthenticatedTransport{ - TokenRefresher: &oidc.ClientCredsTokenRefresher{ - Issuer: pcfg.Issuer.String(), - OIDCClient: oc, - }, - RoundTripper: http.DefaultTransport, - } - hc := &http.Client{Transport: trans} - svc, err := schema.NewWithBasePath(hc, pcfg.Issuer.String()) - if err != nil { - return nil, err - } - - return &apiDriver{svc: svc}, nil -} - -type apiDriver struct { - svc *schema.Service -} - -func (d *apiDriver) NewClient(meta oidc.ClientMetadata) (*oidc.ClientCredentials, error) { - sc := &schema.Client{ - RedirectURIs: make([]string, len(meta.RedirectURIs)), - } - - for i, u := range meta.RedirectURIs { - sc.RedirectURIs[i] = u.String() - } - - call := d.svc.Clients.Create(sc) - scs, err := call.Do() - if err != nil { - return nil, err - } - - creds := &oidc.ClientCredentials{ - ID: scs.Id, - Secret: scs.Secret, - } - - return creds, nil -} - -func (d *apiDriver) ConnectorConfigs() ([]connector.ConnectorConfig, error) { - return nil, errors.New("unable to get connector configs from HTTP API") -} - -func (d *apiDriver) SetConnectorConfigs(cfgs []connector.ConnectorConfig) error { - return errors.New("unable to set connector configs through HTTP API") -} diff --git a/cmd/dexctl/main.go b/cmd/dexctl/main.go index 038ce5e1..2a2c1f7a 100644 --- a/cmd/dexctl/main.go +++ b/cmd/dexctl/main.go @@ -2,7 +2,6 @@ package main import ( "errors" - "net/http" "os" "strings" @@ -44,7 +43,6 @@ var ( } global struct { - endpoint string creds oidc.ClientCredentials dbURL string help bool @@ -55,9 +53,6 @@ var ( func init() { log.EnableTimestamps() - rootCmd.PersistentFlags().StringVar(&global.endpoint, "endpoint", "", "URL of dex API") - rootCmd.PersistentFlags().StringVar(&global.creds.ID, "client-id", "", "dex API user ID") - rootCmd.PersistentFlags().StringVar(&global.creds.Secret, "client-secret", "", "dex API user password") rootCmd.PersistentFlags().StringVar(&global.dbURL, "db-url", "", "DSN-formatted database connection string") rootCmd.PersistentFlags().BoolVar(&global.logDebug, "log-debug", false, "Log debug-level information") } @@ -79,19 +74,8 @@ func getDriver() (drv driver) { switch { case len(global.dbURL) > 0: drv, err = newDBDriver(global.dbURL) - case len(global.endpoint) > 0: - if len(global.creds.ID) == 0 || len(global.creds.Secret) == 0 { - err = errors.New("--client-id/--client-secret flags unset") - break - } - pcfg, err := oidc.FetchProviderConfig(http.DefaultClient, global.endpoint) - if err != nil { - stderr("Unable to fetch provider config: %v", err) - os.Exit(1) - } - drv, err = newAPIDriver(pcfg, global.creds) default: - err = errors.New("--endpoint/--db-url flags unset") + err = errors.New("--db-url flag unset") } if err != nil { diff --git a/integration/client_api_test.go b/integration/client_api_test.go deleted file mode 100644 index 4a3ad195..00000000 --- a/integration/client_api_test.go +++ /dev/null @@ -1,91 +0,0 @@ -package integration - -import ( - "encoding/base64" - "net/http" - "net/url" - "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 := client.Client{ - // Credentials are for reference, they are actually generated by the client manager - Credentials: oidc.ClientCredentials{ - ID: "authn.example.com", - Secret: base64.URLEncoding.EncodeToString([]byte("secret")), - }, - Metadata: oidc.ClientMetadata{ - RedirectURIs: []url.URL{ - {Scheme: "https://", Host: "authn.example.com", Path: "/callback"}, - }, - }, - } - cis := []client.LoadableClient{{Client: ci}} - - srv, err := mockServer(cis) - if err != nil { - t.Fatalf("Unexpected error setting up server: %v", err) - } - - oidcClient, err := mockClient(srv, ci) - if err != nil { - t.Fatalf("Unexpected error setting up OIDC client: %v", err) - } - - tok, err := oidcClient.ClientCredsToken([]string{"openid"}) - if err != nil { - t.Fatalf("Failed getting client token: %v", err) - } - - callbackURL := "http://example.com/oidc/callback" - trans := &tokenHandlerTransport{ - Handler: srv.HTTPHandler(), - Token: tok.Encode(), - } - hc := &http.Client{ - Transport: trans, - } - iss := srv.IssuerURL.String() - svc, err := schema.NewWithBasePath(hc, iss) - if err != nil { - t.Fatalf("Failed creating API service client: %v", err) - } - - newClientInput := &schema.Client{ - RedirectURIs: []string{callbackURL, "http://example.com"}, - } - - call := svc.Clients.Create(newClientInput) - newClient, err := call.Do() - if err != nil { - t.Fatalf("Call to create client API failed: %v", err) - } - - if newClient.Id == "" { - t.Error("Expected non-empty Client ID") - } - - if newClient.Secret == "" { - t.Error("Expected non-empty Client Secret") - } - - meta, err := srv.ClientManager.Metadata(newClient.Id) - if err != nil { - t.Errorf("Error looking up client metadata: %v", err) - } else if meta == nil { - t.Error("Expected new client to exist in repo") - } - - gotURLs := make([]string, len(meta.RedirectURIs)) - for i, u := range meta.RedirectURIs { - gotURLs[i] = u.String() - } - if !reflect.DeepEqual(newClientInput.RedirectURIs, gotURLs) { - t.Errorf("Callback URL mismatch, want=%s, got=%s", newClientInput.RedirectURIs, gotURLs) - } -} diff --git a/schema/adminschema/mapper_test.go b/schema/adminschema/mapper_test.go index 608e9b53..e1b17b32 100644 --- a/schema/adminschema/mapper_test.go +++ b/schema/adminschema/mapper_test.go @@ -48,7 +48,7 @@ func TestMapSchemaClientToClient(t *testing.T) { Id: "123", Secret: "sec_123", RedirectURIs: []string{ - "ht.d://p * * *", + "", }, }, wantErr: true, diff --git a/schema/adminschema/v1-json.go b/schema/adminschema/v1-json.go index b600e08b..aebdbfae 100644 --- a/schema/adminschema/v1-json.go +++ b/schema/adminschema/v1-json.go @@ -1,4 +1,5 @@ package adminschema + // // This file is automatically generated by schema/generator // @@ -236,4 +237,4 @@ const DiscoveryJSON = `{ } } -` \ No newline at end of file +` diff --git a/schema/generator b/schema/generator index cafd4e54..85026d3e 100755 --- a/schema/generator +++ b/schema/generator @@ -73,3 +73,5 @@ GOPATH=${PWD}/gopath ./bin/google-api-go-generator \ # Finally, fix the import in the bindings to refer to the vendored google-api package goimports -w ${GEN} + +gofmt -w schema/${GOPKG} diff --git a/schema/workerschema/README.md b/schema/workerschema/README.md index 0218864a..eb7468f4 100644 --- a/schema/workerschema/README.md +++ b/schema/workerschema/README.md @@ -8,46 +8,6 @@ __Version:__ v1 ## Models -### Client - - - -``` -{ - id: string, - redirectURIs: [ - string - ] -} -``` - -### ClientPage - - - -``` -{ - clients: [ - Client - ], - nextPageToken: string -} -``` - -### ClientWithSecret - - - -``` -{ - id: string, - redirectURIs: [ - string - ], - secret: string -} -``` - ### Error @@ -232,8 +192,8 @@ A client with associated public metadata. > |Name|Located in|Description|Required|Type| |:-----|:-----|:-----|:-----|:-----| -| clientid | path | | Yes | string | | userid | path | | Yes | string | +| clientid | path | | Yes | string | > __Responses__ @@ -243,58 +203,6 @@ A client with associated public metadata. | default | Unexpected error | | -### GET /clients - -> __Summary__ - -> List Clients - -> __Description__ - -> Retrieve a page of Client objects. - - -> __Parameters__ - -> |Name|Located in|Description|Required|Type| -|:-----|:-----|:-----|:-----|:-----| -| nextPageToken | query | | No | string | - - -> __Responses__ - -> |Code|Description|Type| -|:-----|:-----|:-----| -| 200 | | [ClientPage](#clientpage) | -| default | Unexpected error | | - - -### POST /clients - -> __Summary__ - -> Create Clients - -> __Description__ - -> Register a new Client. - - -> __Parameters__ - -> |Name|Located in|Description|Required|Type| -|:-----|:-----|:-----|:-----|:-----| -| | body | | Yes | [Client](#client) | - - -> __Responses__ - -> |Code|Description|Type| -|:-----|:-----|:-----| -| 200 | | [ClientWithSecret](#clientwithsecret) | -| default | Unexpected error | | - - ### GET /users > __Summary__ diff --git a/schema/workerschema/mapper.go b/schema/workerschema/mapper.go deleted file mode 100644 index 7cdf4ab3..00000000 --- a/schema/workerschema/mapper.go +++ /dev/null @@ -1,58 +0,0 @@ -package workerschema - -import ( - "errors" - "net/url" - - "github.com/coreos/dex/client" - "github.com/coreos/go-oidc/oidc" -) - -func MapSchemaClientToClient(sc Client) (client.Client, error) { - ci := client.Client{ - Credentials: oidc.ClientCredentials{ - ID: sc.Id, - }, - Metadata: oidc.ClientMetadata{ - RedirectURIs: make([]url.URL, len(sc.RedirectURIs)), - }, - } - - for i, ru := range sc.RedirectURIs { - if ru == "" { - return client.Client{}, errors.New("redirect URL empty") - } - - u, err := url.Parse(ru) - if err != nil { - return client.Client{}, errors.New("redirect URL invalid") - } - - ci.Metadata.RedirectURIs[i] = *u - } - - return ci, nil -} - -func MapClientToSchemaClient(c client.Client) Client { - cl := Client{ - Id: c.Credentials.ID, - RedirectURIs: make([]string, len(c.Metadata.RedirectURIs)), - } - for i, u := range c.Metadata.RedirectURIs { - cl.RedirectURIs[i] = u.String() - } - return cl -} - -func MapClientToSchemaClientWithSecret(c client.Client) ClientWithSecret { - cl := ClientWithSecret{ - 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() - } - return cl -} diff --git a/schema/workerschema/v1-gen.go b/schema/workerschema/v1-gen.go index 4a2fbd0c..17906dd6 100644 --- a/schema/workerschema/v1-gen.go +++ b/schema/workerschema/v1-gen.go @@ -45,7 +45,6 @@ func New(client *http.Client) (*Service, error) { return nil, errors.New("client is nil") } s := &Service{client: client, BasePath: basePath} - s.Clients = NewClientsService(s) s.RefreshClient = NewRefreshClientService(s) s.Users = NewUsersService(s) return s, nil @@ -55,22 +54,11 @@ type Service struct { client *http.Client BasePath string // API endpoint base URL - Clients *ClientsService - RefreshClient *RefreshClientService Users *UsersService } -func NewClientsService(s *Service) *ClientsService { - rs := &ClientsService{s: s} - return rs -} - -type ClientsService struct { - s *Service -} - func NewRefreshClientService(s *Service) *RefreshClientService { rs := &RefreshClientService{s: s} return rs @@ -89,26 +77,6 @@ type UsersService struct { s *Service } -type Client struct { - Id string `json:"id,omitempty"` - - RedirectURIs []string `json:"redirectURIs,omitempty"` -} - -type ClientPage struct { - Clients []*Client `json:"clients,omitempty"` - - NextPageToken string `json:"nextPageToken,omitempty"` -} - -type ClientWithSecret struct { - Id string `json:"id,omitempty"` - - RedirectURIs []string `json:"redirectURIs,omitempty"` - - Secret string `json:"secret,omitempty"` -} - type Error struct { Error string `json:"error,omitempty"` @@ -192,148 +160,6 @@ type UsersResponse struct { Users []*User `json:"users,omitempty"` } -// method id "dex.Client.Create": - -type ClientsCreateCall struct { - s *Service - client *Client - opt_ map[string]interface{} -} - -// Create: Register a new Client. -func (r *ClientsService) Create(client *Client) *ClientsCreateCall { - c := &ClientsCreateCall{s: r.s, opt_: make(map[string]interface{})} - c.client = client - return c -} - -// Fields allows partial responses to be retrieved. -// See https://developers.google.com/gdata/docs/2.0/basics#PartialResponse -// for more information. -func (c *ClientsCreateCall) Fields(s ...googleapi.Field) *ClientsCreateCall { - c.opt_["fields"] = googleapi.CombineFields(s) - return c -} - -func (c *ClientsCreateCall) Do() (*ClientWithSecret, error) { - var body io.Reader = nil - body, err := googleapi.WithoutDataWrapper.JSONReader(c.client) - if err != nil { - return nil, err - } - ctype := "application/json" - params := make(url.Values) - params.Set("alt", "json") - if v, ok := c.opt_["fields"]; ok { - params.Set("fields", fmt.Sprintf("%v", v)) - } - urls := googleapi.ResolveRelative(c.s.BasePath, "clients") - urls += "?" + params.Encode() - req, _ := http.NewRequest("POST", urls, body) - googleapi.SetOpaque(req.URL) - req.Header.Set("Content-Type", ctype) - req.Header.Set("User-Agent", "google-api-go-client/0.5") - res, err := c.s.client.Do(req) - if err != nil { - return nil, err - } - defer googleapi.CloseBody(res) - if err := googleapi.CheckResponse(res); err != nil { - return nil, err - } - var ret *ClientWithSecret - if err := json.NewDecoder(res.Body).Decode(&ret); err != nil { - return nil, err - } - return ret, nil - // { - // "description": "Register a new Client.", - // "httpMethod": "POST", - // "id": "dex.Client.Create", - // "path": "clients", - // "request": { - // "$ref": "Client" - // }, - // "response": { - // "$ref": "ClientWithSecret" - // } - // } - -} - -// method id "dex.Client.List": - -type ClientsListCall struct { - s *Service - opt_ map[string]interface{} -} - -// List: Retrieve a page of Client objects. -func (r *ClientsService) List() *ClientsListCall { - c := &ClientsListCall{s: r.s, opt_: make(map[string]interface{})} - return c -} - -// NextPageToken sets the optional parameter "nextPageToken": -func (c *ClientsListCall) NextPageToken(nextPageToken string) *ClientsListCall { - c.opt_["nextPageToken"] = nextPageToken - return c -} - -// Fields allows partial responses to be retrieved. -// See https://developers.google.com/gdata/docs/2.0/basics#PartialResponse -// for more information. -func (c *ClientsListCall) Fields(s ...googleapi.Field) *ClientsListCall { - c.opt_["fields"] = googleapi.CombineFields(s) - return c -} - -func (c *ClientsListCall) Do() (*ClientPage, error) { - var body io.Reader = nil - params := make(url.Values) - params.Set("alt", "json") - if v, ok := c.opt_["nextPageToken"]; ok { - params.Set("nextPageToken", fmt.Sprintf("%v", v)) - } - if v, ok := c.opt_["fields"]; ok { - params.Set("fields", fmt.Sprintf("%v", v)) - } - urls := googleapi.ResolveRelative(c.s.BasePath, "clients") - urls += "?" + params.Encode() - req, _ := http.NewRequest("GET", urls, body) - googleapi.SetOpaque(req.URL) - req.Header.Set("User-Agent", "google-api-go-client/0.5") - res, err := c.s.client.Do(req) - if err != nil { - return nil, err - } - defer googleapi.CloseBody(res) - if err := googleapi.CheckResponse(res); err != nil { - return nil, err - } - var ret *ClientPage - if err := json.NewDecoder(res.Body).Decode(&ret); err != nil { - return nil, err - } - return ret, nil - // { - // "description": "Retrieve a page of Client objects.", - // "httpMethod": "GET", - // "id": "dex.Client.List", - // "parameters": { - // "nextPageToken": { - // "location": "query", - // "type": "string" - // } - // }, - // "path": "clients", - // "response": { - // "$ref": "ClientPage" - // } - // } - -} - // method id "dex.RefreshClient.List": type RefreshClientListCall struct { diff --git a/schema/workerschema/v1-json.go b/schema/workerschema/v1-json.go index 5160e89d..2b628af5 100644 --- a/schema/workerschema/v1-json.go +++ b/schema/workerschema/v1-json.go @@ -1,4 +1,5 @@ package workerschema + // // This file is automatically generated by schema/generator // @@ -39,22 +40,6 @@ const DiscoveryJSON = `{ } } }, - "Client": { - "id": "Client", - "type": "object", - "properties": { - "id": { - "type": "string" - }, - "redirectURIs": { - "required": true, - "type": "array", - "items": { - "type": "string" - } - } - } - }, "RefreshClient": { "id": "Client", "type": "object", @@ -86,40 +71,6 @@ const DiscoveryJSON = `{ } } }, - "ClientWithSecret": { - "id": "Client", - "type": "object", - "properties": { - "id": { - "type": "string" - }, - "secret": { - "type": "string" - }, - "redirectURIs": { - "required": true, - "type": "array", - "items": { - "type": "string" - } - } - } - }, - "ClientPage": { - "id": "ClientPage", - "type": "object", - "properties": { - "clients": { - "type": "array", - "items": { - "$ref": "Client" - } - }, - "nextPageToken": { - "type": "string" - } - } - }, "User": { "id": "User", "type": "object", @@ -244,37 +195,6 @@ const DiscoveryJSON = `{ } }, "resources": { - "Clients": { - "methods": { - "List": { - "id": "dex.Client.List", - "description": "Retrieve a page of Client objects.", - "httpMethod": "GET", - "path": "clients", - "parameters": { - "nextPageToken": { - "type": "string", - "location": "query" - } - }, - "response": { - "$ref": "ClientPage" - } - }, - "Create": { - "id": "dex.Client.Create", - "description": "Register a new Client.", - "httpMethod": "POST", - "path": "clients", - "request": { - "$ref": "Client" - }, - "response": { - "$ref": "ClientWithSecret" - } - } - } - }, "Users": { "methods": { "List": { @@ -420,4 +340,4 @@ const DiscoveryJSON = `{ } } } -` \ No newline at end of file +` diff --git a/schema/workerschema/v1.json b/schema/workerschema/v1.json index 76ca8bf5..47a3a90c 100644 --- a/schema/workerschema/v1.json +++ b/schema/workerschema/v1.json @@ -33,22 +33,6 @@ } } }, - "Client": { - "id": "Client", - "type": "object", - "properties": { - "id": { - "type": "string" - }, - "redirectURIs": { - "required": true, - "type": "array", - "items": { - "type": "string" - } - } - } - }, "RefreshClient": { "id": "Client", "type": "object", @@ -80,40 +64,6 @@ } } }, - "ClientWithSecret": { - "id": "Client", - "type": "object", - "properties": { - "id": { - "type": "string" - }, - "secret": { - "type": "string" - }, - "redirectURIs": { - "required": true, - "type": "array", - "items": { - "type": "string" - } - } - } - }, - "ClientPage": { - "id": "ClientPage", - "type": "object", - "properties": { - "clients": { - "type": "array", - "items": { - "$ref": "Client" - } - }, - "nextPageToken": { - "type": "string" - } - } - }, "User": { "id": "User", "type": "object", @@ -238,37 +188,6 @@ } }, "resources": { - "Clients": { - "methods": { - "List": { - "id": "dex.Client.List", - "description": "Retrieve a page of Client objects.", - "httpMethod": "GET", - "path": "clients", - "parameters": { - "nextPageToken": { - "type": "string", - "location": "query" - } - }, - "response": { - "$ref": "ClientPage" - } - }, - "Create": { - "id": "dex.Client.Create", - "description": "Register a new Client.", - "httpMethod": "POST", - "path": "clients", - "request": { - "$ref": "Client" - }, - "response": { - "$ref": "ClientWithSecret" - } - } - } - }, "Users": { "methods": { "List": { diff --git a/server/client_resource.go b/server/client_resource.go deleted file mode 100644 index b4fad488..00000000 --- a/server/client_resource.go +++ /dev/null @@ -1,102 +0,0 @@ -package server - -import ( - "encoding/json" - "fmt" - "net/http" - "path" - - "github.com/coreos/dex/client/manager" - phttp "github.com/coreos/dex/pkg/http" - "github.com/coreos/dex/pkg/log" - schema "github.com/coreos/dex/schema/workerschema" -) - -type clientResource struct { - manager *manager.ClientManager -} - -func registerClientResource(prefix string, manager *manager.ClientManager) (string, http.Handler) { - mux := http.NewServeMux() - c := &clientResource{ - manager: manager, - } - relPath := "clients" - absPath := path.Join(prefix, relPath) - mux.Handle(absPath, c) - return relPath, mux -} - -func (c *clientResource) ServeHTTP(w http.ResponseWriter, r *http.Request) { - switch r.Method { - case "GET": - c.list(w, r) - case "POST": - c.create(w, r) - default: - msg := fmt.Sprintf("HTTP %s method not supported for this resource", r.Method) - writeAPIError(w, http.StatusMethodNotAllowed, newAPIError(errorInvalidRequest, msg)) - } -} - -func (c *clientResource) list(w http.ResponseWriter, r *http.Request) { - cs, err := c.manager.All() - if err != nil { - writeAPIError(w, http.StatusInternalServerError, newAPIError(errorServerError, "error listing clients")) - return - } - - scs := make([]*schema.Client, len(cs)) - for i, ci := range cs { - sc := schema.MapClientToSchemaClient(ci) - scs[i] = &sc - } - - page := schema.ClientPage{ - Clients: scs, - } - writeResponseWithBody(w, http.StatusOK, page) -} - -func (c *clientResource) create(w http.ResponseWriter, r *http.Request) { - ct := r.Header.Get("content-type") - if ct != "application/json" { - log.Debugf("Unsupported request content-type: %v", ct) - writeAPIError(w, http.StatusBadRequest, newAPIError(errorInvalidRequest, "unsupported content-type")) - return - } - - var sc schema.Client - dec := json.NewDecoder(r.Body) - err := dec.Decode(&sc) - if err != nil { - log.Debugf("Error decoding request body: %v", err) - writeAPIError(w, http.StatusBadRequest, newAPIError(errorInvalidRequest, "unable to decode request body")) - return - } - - 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")) - return - } - - if err := ci.Metadata.Valid(); err != nil { - log.Debugf("ClientMetadata invalid: %v", err) - writeAPIError(w, http.StatusBadRequest, newAPIError(errorInvalidClientMetadata, err.Error())) - return - } - creds, err := c.manager.New(ci, nil) - - if err != nil { - log.Errorf("Failed creating client: %v", err) - writeAPIError(w, http.StatusInternalServerError, newAPIError(errorServerError, "unable to create client")) - return - } - ci.Credentials = *creds - - 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 deleted file mode 100644 index 7fffa78a..00000000 --- a/server/client_resource_test.go +++ /dev/null @@ -1,284 +0,0 @@ -package server - -import ( - "encoding/base64" - "encoding/json" - "fmt" - "io" - "io/ioutil" - "net/http" - "net/http/httptest" - "net/url" - "reflect" - "sort" - "strings" - "testing" - - "github.com/coreos/dex/client" - "github.com/coreos/dex/client/manager" - "github.com/coreos/dex/db" - schema "github.com/coreos/dex/schema/workerschema" - "github.com/coreos/go-oidc/oidc" - "github.com/kylelemons/godebug/pretty" -) - -func makeBody(s string) io.ReadCloser { - return ioutil.NopCloser(strings.NewReader(s)) -} - -func TestCreateInvalidRequest(t *testing.T) { - u := &url.URL{Scheme: "http", Host: "example.com", Path: "clients"} - h := http.Header{"Content-Type": []string{"application/json"}} - dbm := db.NewMemDB() - repo := db.NewClientRepo(dbm) - manager := manager.NewClientManager(repo, db.TransactionFactory(dbm), manager.ManagerOptions{}) - res := &clientResource{manager: manager} - tests := []struct { - req *http.Request - wantCode int - wantBody string - }{ - // invalid content-type - { - req: &http.Request{Method: "POST", URL: u, Header: http.Header{"Content-Type": []string{"application/xml"}}}, - wantCode: http.StatusBadRequest, - wantBody: `{"error":"invalid_request","error_description":"unsupported content-type"}`, - }, - // invalid method - { - req: &http.Request{Method: "DELETE", URL: u, Header: h}, - wantCode: http.StatusMethodNotAllowed, - wantBody: `{"error":"invalid_request","error_description":"HTTP DELETE method not supported for this resource"}`, - }, - // invalid method - { - req: &http.Request{Method: "PUT", URL: u, Header: h}, - wantCode: http.StatusMethodNotAllowed, - wantBody: `{"error":"invalid_request","error_description":"HTTP PUT method not supported for this resource"}`, - }, - // invalid method - { - req: &http.Request{Method: "HEAD", URL: u, Header: h}, - wantCode: http.StatusMethodNotAllowed, - wantBody: `{"error":"invalid_request","error_description":"HTTP HEAD method not supported for this resource"}`, - }, - // unserializable body - { - req: &http.Request{Method: "POST", URL: u, Header: h, Body: makeBody("asdf")}, - wantCode: http.StatusBadRequest, - wantBody: `{"error":"invalid_request","error_description":"unable to decode request body"}`, - }, - // empty body - { - req: &http.Request{Method: "POST", URL: u, Header: h, Body: makeBody("")}, - wantCode: http.StatusBadRequest, - wantBody: `{"error":"invalid_request","error_description":"unable to decode request body"}`, - }, - // missing url field - { - req: &http.Request{Method: "POST", URL: u, Header: h, Body: makeBody(`{"id":"foo"}`)}, - wantCode: http.StatusBadRequest, - wantBody: `{"error":"invalid_client_metadata","error_description":"zero redirect URLs"}`, - }, - // empty url array - { - req: &http.Request{Method: "POST", URL: u, Header: h, Body: makeBody(`{"redirectURIs":[]}`)}, - wantCode: http.StatusBadRequest, - wantBody: `{"error":"invalid_client_metadata","error_description":"zero redirect URLs"}`, - }, - // array with empty string - { - req: &http.Request{Method: "POST", URL: u, Header: h, Body: makeBody(`{"redirectURIs":[""]}`)}, - wantCode: http.StatusBadRequest, - wantBody: `{"error":"invalid_client_metadata","error_description":"missing or invalid field: redirectURIs"}`, - }, - // uri with unusable scheme - { - req: &http.Request{Method: "POST", URL: u, Header: h, Body: makeBody(`{"redirectURIs":["asdf.com"]}`)}, - wantCode: http.StatusBadRequest, - wantBody: `{"error":"invalid_client_metadata","error_description":"no host for uri field redirect_uris"}`, - }, - // uri missing host - { - req: &http.Request{Method: "POST", URL: u, Header: h, Body: makeBody(`{"redirectURIs":["http://"]}`)}, - wantCode: http.StatusBadRequest, - wantBody: `{"error":"invalid_client_metadata","error_description":"no host for uri field redirect_uris"}`, - }, - } - - for i, tt := range tests { - w := httptest.NewRecorder() - res.ServeHTTP(w, tt.req) - - if w.Code != tt.wantCode { - t.Errorf("case %d: invalid response code, want=%d, got=%d", i, tt.wantCode, w.Code) - } - - gotBody := w.Body.String() - if gotBody != tt.wantBody { - t.Errorf("case %d: invalid response body, want=%s, got=%s", i, tt.wantBody, gotBody) - } - } -} - -func TestCreate(t *testing.T) { - dbm := db.NewMemDB() - repo := db.NewClientRepo(dbm) - manager := manager.NewClientManager(repo, db.TransactionFactory(dbm), manager.ManagerOptions{}) - res := &clientResource{manager: manager} - tests := [][]string{ - []string{"http://example.com"}, - []string{"https://example.com"}, - []string{"http://example.com/foo"}, - []string{"http://example.com/bar", "http://example.com/foo"}, - } - endpoint := "http://example.com/clients" - - for i, tt := range tests { - body := strings.NewReader(fmt.Sprintf(`{"redirectURIs":["%s"]}`, strings.Join(tt, `","`))) - r, err := http.NewRequest("POST", endpoint, body) - if err != nil { - t.Fatalf("Failed creating http.Request: %v", err) - } - r.Header.Set("content-type", "application/json") - w := httptest.NewRecorder() - res.ServeHTTP(w, r) - - if w.Code != http.StatusCreated { - t.Errorf("case %d: invalid response code, want=%d, got=%d", i, http.StatusCreated, w.Code) - } - - var client schema.ClientWithSecret - if err := json.Unmarshal(w.Body.Bytes(), &client); err != nil { - t.Errorf("case %d: unexpected error=%v", i, err) - } - if len(client.RedirectURIs) != len(tt) { - t.Errorf("case %d: unexpected number of redirect URIs, want=%d, got=%d", i, len(tt), len(client.RedirectURIs)) - } - - if !reflect.DeepEqual(tt, client.RedirectURIs) { - t.Errorf("case %d: unexpected client redirect URIs: want=%v got=%v", i, tt, client.RedirectURIs) - } - - if client.Id == "" { - t.Errorf("case %d: empty client ID in response", i) - } - - if client.Secret == "" { - t.Errorf("case %d: empty client secret in response", i) - } - - wantLoc := fmt.Sprintf("%s/%s", endpoint, client.Id) - gotLoc := w.Header().Get("Location") - if gotLoc != wantLoc { - t.Errorf("case %d: invalid location header, want=%v, got=%v", i, wantLoc, gotLoc) - } - } -} - -func TestList(t *testing.T) { - - b64Encode := func(s string) string { - return base64.URLEncoding.EncodeToString([]byte(s)) - } - - tests := []struct { - cs []client.Client - want []*schema.Client - }{ - // empty repo - { - cs: []client.Client{}, - want: nil, - }, - // single client - { - cs: []client.Client{ - client.Client{ - Credentials: oidc.ClientCredentials{ID: "example.com", Secret: b64Encode("secret")}, - Metadata: oidc.ClientMetadata{ - RedirectURIs: []url.URL{ - url.URL{Scheme: "http", Host: "example.com"}, - }, - }, - }, - }, - want: []*schema.Client{ - &schema.Client{ - Id: "example.com", - RedirectURIs: []string{"http://example.com"}, - }, - }, - }, - // multi client - { - cs: []client.Client{ - client.Client{ - Credentials: oidc.ClientCredentials{ID: "example.com", Secret: b64Encode("secret")}, - Metadata: oidc.ClientMetadata{ - RedirectURIs: []url.URL{ - url.URL{Scheme: "http", Host: "example.com"}, - }, - }, - }, - client.Client{ - Credentials: oidc.ClientCredentials{ID: "example2.com", Secret: b64Encode("secret")}, - Metadata: oidc.ClientMetadata{ - RedirectURIs: []url.URL{ - url.URL{Scheme: "https", Host: "example2.com", Path: "one/two/three"}, - }, - }, - }, - }, - want: []*schema.Client{ - &schema.Client{ - Id: "example2.com", - RedirectURIs: []string{"https://example2.com/one/two/three"}, - }, - &schema.Client{ - Id: "example.com", - RedirectURIs: []string{"http://example.com"}, - }, - }, - }, - } - - for i, tt := range tests { - f, err := makeTestFixturesWithOptions(testFixtureOptions{ - clients: clientsToLoadableClients(tt.cs), - }) - if err != nil { - t.Fatalf("error making test fixtures: %v", err) - } - - res := &clientResource{manager: f.clientManager} - - r, err := http.NewRequest("GET", "http://example.com/clients", nil) - if err != nil { - t.Fatalf("Failed creating http.Request: %v", err) - } - w := httptest.NewRecorder() - res.ServeHTTP(w, r) - - if w.Code != http.StatusOK { - t.Errorf("case %d: invalid response code, want=%d, got=%d", i, http.StatusOK, w.Code) - } - - var resp schema.ClientPage - if err := json.Unmarshal(w.Body.Bytes(), &resp); err != nil { - t.Errorf("case %d: unexpected error=%v", i, err) - } - sort.Sort(byClientId(tt.want)) - sort.Sort(byClientId(resp.Clients)) - - if diff := pretty.Compare(tt.want, resp.Clients); diff != "" { - t.Errorf("case %d: invalid response body: %s", i, diff) - } - } -} - -type byClientId []*schema.Client - -func (b byClientId) Len() int { return len(b) } -func (b byClientId) Less(i, j int) bool { return b[i].Id < b[j].Id } -func (b byClientId) Swap(i, j int) { b[i], b[j] = b[j], b[i] } diff --git a/server/server.go b/server/server.go index a151f0b3..22cf42d7 100644 --- a/server/server.go +++ b/server/server.go @@ -265,9 +265,6 @@ func (s *Server) HTTPHandler() http.Handler { apiBasePath := path.Join(httpPathAPI, APIVersion) registerDiscoveryResource(apiBasePath, mux) - clientPath, clientHandler := registerClientResource(apiBasePath, s.ClientManager) - mux.Handle(path.Join(apiBasePath, clientPath), s.NewClientTokenAuthHandler(clientHandler)) - usersAPI := usersapi.NewUsersAPI(s.UserManager, s.ClientManager, s.RefreshTokenRepo, s.UserEmailer, s.localConnectorID) handler := NewUserMgmtServer(usersAPI, s.JWTVerifierFactory(), s.UserManager, s.ClientManager).HTTPHandler() diff --git a/test b/test index f66c7323..ff2f9456 100755 --- a/test +++ b/test @@ -18,7 +18,7 @@ if [ ! -d $GOPATH/pkg ]; then echo "WARNING: No cached builds detected. Please run the ./build script to speed up future tests." fi -TESTABLE="admin client client/manager connector db email functional/repo integration pkg/crypto pkg/flag pkg/http pkg/time pkg/html server session session/manager user user/api user/manager user/email" +TESTABLE="admin client client/manager connector db email functional/repo integration pkg/crypto pkg/flag pkg/http pkg/time pkg/html schema/adminschema server session session/manager user user/api user/manager user/email" FORMATTABLE="$TESTABLE cmd/dexctl cmd/dex-worker cmd/dex-overlord examples/app functional pkg/log" # user has not provided PKG override