From 104c9761c656d8bf7e1084d517c648443afeaebe Mon Sep 17 00:00:00 2001 From: Bobby Rullo Date: Wed, 15 Jun 2016 10:48:20 -0700 Subject: [PATCH 1/7] schema: remove clients API The only thing using this AFAIK is dexctl in api_driver mode, which no-one uses - it's a sort of weird API which requires a client to create other clients, and gives all clients the ability to list all other clients. So we are removing it. --- schema/workerschema/README.md | 94 +----------------- schema/workerschema/v1-gen.go | 174 --------------------------------- schema/workerschema/v1-json.go | 81 --------------- schema/workerschema/v1.json | 81 --------------- 4 files changed, 1 insertion(+), 429 deletions(-) diff --git a/schema/workerschema/README.md b/schema/workerschema/README.md index 0218864a..5421d932 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 @@ -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__ @@ -310,8 +218,8 @@ A client with associated public metadata. > |Name|Located in|Description|Required|Type| |:-----|:-----|:-----|:-----|:-----| -| nextPageToken | query | | No | string | | maxResults | query | | No | integer | +| nextPageToken | query | | No | string | > __Responses__ 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..8c7b43fc 100644 --- a/schema/workerschema/v1-json.go +++ b/schema/workerschema/v1-json.go @@ -39,22 +39,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 +70,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 +194,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": { 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": { From adb2ccf872f3a256e6011ac476a33396615a6c1a Mon Sep 17 00:00:00 2001 From: Bobby Rullo Date: Wed, 15 Jun 2016 10:55:27 -0700 Subject: [PATCH 2/7] test: add schema/adminschema to tests --- test | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) 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 From 8942a497021a95f5c52be57c21d659c508c120af Mon Sep 17 00:00:00 2001 From: Bobby Rullo Date: Wed, 15 Jun 2016 11:05:37 -0700 Subject: [PATCH 3/7] server: remove client_resource api ...and dependent code. --- integration/client_api_test.go | 91 ----------- schema/workerschema/mapper.go | 58 ------- server/client_resource.go | 102 ------------ server/client_resource_test.go | 284 --------------------------------- server/server.go | 3 - 5 files changed, 538 deletions(-) delete mode 100644 integration/client_api_test.go delete mode 100644 schema/workerschema/mapper.go delete mode 100644 server/client_resource.go delete mode 100644 server/client_resource_test.go 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/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/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() From 59dc4a94007c0f6e3710773727a5a3ea82db8722 Mon Sep 17 00:00:00 2001 From: Bobby Rullo Date: Wed, 15 Jun 2016 11:31:20 -0700 Subject: [PATCH 4/7] dexctl: remove api driver API Driver is dead: This API turns out to not be super useful, requiring an existing client to create other clients is weird. Long live API Driver? Let's use Dynamic Client API and the bootstrap API to create a better API Driver! LONG LIVE API DRIVER. --- cmd/dexctl/driver_api.go | 71 ---------------------------------------- cmd/dexctl/main.go | 18 +--------- 2 files changed, 1 insertion(+), 88 deletions(-) delete mode 100644 cmd/dexctl/driver_api.go 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 { From 82c5c2704810ff75408a0e2832eae13d3ee1cc0a Mon Sep 17 00:00:00 2001 From: Bobby Rullo Date: Wed, 15 Jun 2016 11:41:26 -0700 Subject: [PATCH 5/7] schema: generator now gofmts everything. --- schema/generator | 2 ++ 1 file changed, 2 insertions(+) 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} From ce421a4dab9e9d94e9bc242036c20320dfa8085b Mon Sep 17 00:00:00 2001 From: Bobby Rullo Date: Wed, 15 Jun 2016 11:41:57 -0700 Subject: [PATCH 6/7] schema: gofmt the generated code. --- schema/adminschema/v1-json.go | 3 ++- schema/workerschema/README.md | 4 ++-- schema/workerschema/v1-json.go | 3 ++- 3 files changed, 6 insertions(+), 4 deletions(-) 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/workerschema/README.md b/schema/workerschema/README.md index 5421d932..eb7468f4 100644 --- a/schema/workerschema/README.md +++ b/schema/workerschema/README.md @@ -192,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__ @@ -218,8 +218,8 @@ A client with associated public metadata. > |Name|Located in|Description|Required|Type| |:-----|:-----|:-----|:-----|:-----| -| maxResults | query | | No | integer | | nextPageToken | query | | No | string | +| maxResults | query | | No | integer | > __Responses__ diff --git a/schema/workerschema/v1-json.go b/schema/workerschema/v1-json.go index 8c7b43fc..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 // @@ -339,4 +340,4 @@ const DiscoveryJSON = `{ } } } -` \ No newline at end of file +` From c9c33befb5e961a51a0a41957b4c953124983b73 Mon Sep 17 00:00:00 2001 From: Bobby Rullo Date: Wed, 15 Jun 2016 14:31:02 -0700 Subject: [PATCH 7/7] adminschema: fix test go 1.5.4 accepts just about anything as a URL, so instead just trigger with blank URL --- schema/adminschema/mapper_test.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) 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,