From 09e889e7bca18f3acbd779c863659c27d03561eb Mon Sep 17 00:00:00 2001 From: Bobby Rullo Date: Wed, 15 Jun 2016 15:39:04 -0700 Subject: [PATCH] client: add public client to data model --- client/client.go | 28 ++++++++++++++----- client/client_test.go | 27 +++++++++++++++++++ db/client.go | 33 +++++++++++++++++++++-- db/migrate_sqlite3.go | 3 ++- db/migrations/0013_add_public_clients.sql | 4 +++ db/migrations/assets.go | 6 +++++ 6 files changed, 92 insertions(+), 9 deletions(-) create mode 100644 db/migrations/0013_add_public_clients.sql diff --git a/client/client.go b/client/client.go index 84073677..76cce81e 100644 --- a/client/client.go +++ b/client/client.go @@ -15,13 +15,26 @@ import ( ) var ( - ErrorInvalidClientID = errors.New("not a valid client ID") - ErrorInvalidRedirectURL = errors.New("not a valid redirect url for the given client") - ErrorCantChooseRedirectURL = errors.New("must provide a redirect url; client has many") - ErrorNoValidRedirectURLs = errors.New("no valid redirect URLs for this client.") - ErrorNotFound = errors.New("no data found") + ErrorInvalidClientID = errors.New("not a valid client ID") + ErrorInvalidRedirectURL = errors.New("not a valid redirect url for the given client") + ErrorCantChooseRedirectURL = errors.New("must provide a redirect url; client has many") + ErrorNoValidRedirectURLs = errors.New("no valid redirect URLs for this client.") + ErrorPublicClientRedirectURIs = errors.New("native clients cannot have redirect URIs") + ErrorPublicClientMissingName = errors.New("native clients must have a name") + + ErrorMissingRedirectURI = errors.New("no client redirect url given") + + ErrorNotFound = errors.New("no data found") ) +type ValidationError struct { + Err error +} + +func (v ValidationError) Error() string { + return v.Err.Error() +} + const ( bcryptHashCost = 10 ) @@ -44,6 +57,7 @@ type Client struct { Credentials oidc.ClientCredentials Metadata oidc.ClientMetadata Admin bool + Public bool } type ClientRepo interface { @@ -106,6 +120,7 @@ func ClientsFromReader(r io.Reader) ([]LoadableClient, error) { Secret string `json:"secret"` RedirectURLs []string `json:"redirectURLs"` Admin bool `json:"admin"` + Public bool `json:"public"` TrustedPeers []string `json:"trustedPeers"` } if err := json.NewDecoder(r).Decode(&c); err != nil { @@ -137,7 +152,8 @@ func ClientsFromReader(r io.Reader) ([]LoadableClient, error) { Metadata: oidc.ClientMetadata{ RedirectURIs: redirectURIs, }, - Admin: client.Admin, + Admin: client.Admin, + Public: client.Public, }, TrustedPeers: client.TrustedPeers, } diff --git a/client/client_test.go b/client/client_test.go index 289bc101..5766b2f8 100644 --- a/client/client_test.go +++ b/client/client_test.go @@ -35,6 +35,13 @@ var ( "trustedPeers":["goodClient1", "goodClient2"] }` + publicClient = `{ + "id": "public_client", + "secret": "` + goodSecret3 + `", + "redirectURLs": ["http://localhost:8080","urn:ietf:wg:oauth:2.0:oob"], + "public": true +}` + badURLClient = `{ "id": "my_id", "secret": "` + goodSecret1 + `", @@ -139,6 +146,26 @@ func TestClientsFromReader(t *testing.T) { }, }, }, + { + json: "[" + publicClient + "]", + want: []LoadableClient{ + { + Client: Client{ + Credentials: oidc.ClientCredentials{ + ID: "public_client", + Secret: goodSecret3, + }, + Metadata: oidc.ClientMetadata{ + RedirectURIs: []url.URL{ + mustParseURL(t, "http://localhost:8080"), + mustParseURL(t, "urn:ietf:wg:oauth:2.0:oob"), + }, + }, + Public: true, + }, + }, + }, + }, { json: "[" + badURLClient + "]", wantErr: true, diff --git a/db/client.go b/db/client.go index eeb80c70..e1a65292 100644 --- a/db/client.go +++ b/db/client.go @@ -5,6 +5,7 @@ import ( "encoding/json" "errors" "fmt" + "net/url" "reflect" "github.com/coreos/go-oidc/oidc" @@ -23,6 +24,10 @@ const ( pgErrorCodeUniqueViolation = "23505" // unique_violation ) +var ( + localHostRedirectURL = mustParseURL("http://localhost:0") +) + func init() { register(table{ name: clientTableName, @@ -44,6 +49,16 @@ func newClientModel(cli client.Client) (*clientModel, error) { if err != nil { return nil, err } + + if cli.Public { + // Metadata.Valid(), and therefore json.Unmarshal(metadata) complains + // when there's no RedirectURIs, so we set them to a fixed value here, + // and remove it when translating back to a client.Client + cli.Metadata.RedirectURIs = []url.URL{ + localHostRedirectURL, + } + } + bmeta, err := json.Marshal(&cli.Metadata) if err != nil { return nil, err @@ -54,6 +69,7 @@ func newClientModel(cli client.Client) (*clientModel, error) { Secret: hashed, Metadata: string(bmeta), DexAdmin: cli.Admin, + Public: cli.Public, } return &cim, nil @@ -64,6 +80,7 @@ type clientModel struct { Secret []byte `db:"secret"` Metadata string `db:"metadata"` DexAdmin bool `db:"dex_admin"` + Public bool `db:"public"` } type trustedPeerModel struct { @@ -76,13 +93,18 @@ func (m *clientModel) Client() (*client.Client, error) { Credentials: oidc.ClientCredentials{ ID: m.ID, }, - Admin: m.DexAdmin, + Admin: m.DexAdmin, + Public: m.Public, } if err := json.Unmarshal([]byte(m.Metadata), &ci.Metadata); err != nil { return nil, err } + if ci.Public { + ci.Metadata.RedirectURIs = []url.URL{} + } + return &ci, nil } @@ -168,7 +190,6 @@ func isAlreadyExistsErr(err error) bool { func (r *clientRepo) New(tx repo.Transaction, cli client.Client) (*oidc.ClientCredentials, error) { cim, err := newClientModel(cli) - if err != nil { return nil, err } @@ -328,3 +349,11 @@ func (r *clientRepo) SetTrustedPeers(tx repo.Transaction, clientID string, clien return nil } + +func mustParseURL(s string) url.URL { + u, err := url.Parse(s) + if err != nil { + panic(err) + } + return *u +} diff --git a/db/migrate_sqlite3.go b/db/migrate_sqlite3.go index 523cce64..07c64546 100644 --- a/db/migrate_sqlite3.go +++ b/db/migrate_sqlite3.go @@ -16,7 +16,8 @@ CREATE TABLE client_identity ( id text NOT NULL UNIQUE, secret blob, metadata text, - dex_admin integer + dex_admin integer, + public integer ); CREATE TABLE connector_config ( diff --git a/db/migrations/0013_add_public_clients.sql b/db/migrations/0013_add_public_clients.sql new file mode 100644 index 00000000..e4f1d3dc --- /dev/null +++ b/db/migrations/0013_add_public_clients.sql @@ -0,0 +1,4 @@ +-- +migrate Up +ALTER TABLE client_identity ADD COLUMN "public" boolean; + +UPDATE "client_identity" SET "public" = false; diff --git a/db/migrations/assets.go b/db/migrations/assets.go index 6798acc1..1a4b5f89 100644 --- a/db/migrations/assets.go +++ b/db/migrations/assets.go @@ -78,6 +78,12 @@ var PostgresMigrations migrate.MigrationSource = &migrate.MemoryMigrationSource{ "-- +migrate Up\nCREATE TABLE IF NOT EXISTS \"trusted_peers\" (\n \"client_id\" text not null,\n \"trusted_client_id\" text not null,\n primary key (\"client_id\", \"trusted_client_id\")) ;\n", }, }, + { + Id: "0013_add_public_clients.sql", + Up: []string{ + "-- +migrate Up\nALTER TABLE client_identity ADD COLUMN \"public\" boolean;\n\nUPDATE \"client_identity\" SET \"public\" = false;\n", + }, + }, { Id: "0013_add_scopes_to_refresh_tokens.sql", Up: []string{