client: add public client to data model

This commit is contained in:
Bobby Rullo 2016-06-15 15:39:04 -07:00
parent a530cc8d7c
commit 09e889e7bc
6 changed files with 92 additions and 9 deletions

View file

@ -15,13 +15,26 @@ import (
) )
var ( var (
ErrorInvalidClientID = errors.New("not a valid client ID") ErrorInvalidClientID = errors.New("not a valid client ID")
ErrorInvalidRedirectURL = errors.New("not a valid redirect url for the given client") ErrorInvalidRedirectURL = errors.New("not a valid redirect url for the given client")
ErrorCantChooseRedirectURL = errors.New("must provide a redirect url; client has many") ErrorCantChooseRedirectURL = errors.New("must provide a redirect url; client has many")
ErrorNoValidRedirectURLs = errors.New("no valid redirect URLs for this client.") ErrorNoValidRedirectURLs = errors.New("no valid redirect URLs for this client.")
ErrorNotFound = errors.New("no data found") 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 ( const (
bcryptHashCost = 10 bcryptHashCost = 10
) )
@ -44,6 +57,7 @@ type Client struct {
Credentials oidc.ClientCredentials Credentials oidc.ClientCredentials
Metadata oidc.ClientMetadata Metadata oidc.ClientMetadata
Admin bool Admin bool
Public bool
} }
type ClientRepo interface { type ClientRepo interface {
@ -106,6 +120,7 @@ func ClientsFromReader(r io.Reader) ([]LoadableClient, error) {
Secret string `json:"secret"` Secret string `json:"secret"`
RedirectURLs []string `json:"redirectURLs"` RedirectURLs []string `json:"redirectURLs"`
Admin bool `json:"admin"` Admin bool `json:"admin"`
Public bool `json:"public"`
TrustedPeers []string `json:"trustedPeers"` TrustedPeers []string `json:"trustedPeers"`
} }
if err := json.NewDecoder(r).Decode(&c); err != nil { if err := json.NewDecoder(r).Decode(&c); err != nil {
@ -137,7 +152,8 @@ func ClientsFromReader(r io.Reader) ([]LoadableClient, error) {
Metadata: oidc.ClientMetadata{ Metadata: oidc.ClientMetadata{
RedirectURIs: redirectURIs, RedirectURIs: redirectURIs,
}, },
Admin: client.Admin, Admin: client.Admin,
Public: client.Public,
}, },
TrustedPeers: client.TrustedPeers, TrustedPeers: client.TrustedPeers,
} }

View file

@ -35,6 +35,13 @@ var (
"trustedPeers":["goodClient1", "goodClient2"] "trustedPeers":["goodClient1", "goodClient2"]
}` }`
publicClient = `{
"id": "public_client",
"secret": "` + goodSecret3 + `",
"redirectURLs": ["http://localhost:8080","urn:ietf:wg:oauth:2.0:oob"],
"public": true
}`
badURLClient = `{ badURLClient = `{
"id": "my_id", "id": "my_id",
"secret": "` + goodSecret1 + `", "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 + "]", json: "[" + badURLClient + "]",
wantErr: true, wantErr: true,

View file

@ -5,6 +5,7 @@ import (
"encoding/json" "encoding/json"
"errors" "errors"
"fmt" "fmt"
"net/url"
"reflect" "reflect"
"github.com/coreos/go-oidc/oidc" "github.com/coreos/go-oidc/oidc"
@ -23,6 +24,10 @@ const (
pgErrorCodeUniqueViolation = "23505" // unique_violation pgErrorCodeUniqueViolation = "23505" // unique_violation
) )
var (
localHostRedirectURL = mustParseURL("http://localhost:0")
)
func init() { func init() {
register(table{ register(table{
name: clientTableName, name: clientTableName,
@ -44,6 +49,16 @@ func newClientModel(cli client.Client) (*clientModel, error) {
if err != nil { if err != nil {
return nil, err 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) bmeta, err := json.Marshal(&cli.Metadata)
if err != nil { if err != nil {
return nil, err return nil, err
@ -54,6 +69,7 @@ func newClientModel(cli client.Client) (*clientModel, error) {
Secret: hashed, Secret: hashed,
Metadata: string(bmeta), Metadata: string(bmeta),
DexAdmin: cli.Admin, DexAdmin: cli.Admin,
Public: cli.Public,
} }
return &cim, nil return &cim, nil
@ -64,6 +80,7 @@ type clientModel struct {
Secret []byte `db:"secret"` Secret []byte `db:"secret"`
Metadata string `db:"metadata"` Metadata string `db:"metadata"`
DexAdmin bool `db:"dex_admin"` DexAdmin bool `db:"dex_admin"`
Public bool `db:"public"`
} }
type trustedPeerModel struct { type trustedPeerModel struct {
@ -76,13 +93,18 @@ func (m *clientModel) Client() (*client.Client, error) {
Credentials: oidc.ClientCredentials{ Credentials: oidc.ClientCredentials{
ID: m.ID, ID: m.ID,
}, },
Admin: m.DexAdmin, Admin: m.DexAdmin,
Public: m.Public,
} }
if err := json.Unmarshal([]byte(m.Metadata), &ci.Metadata); err != nil { if err := json.Unmarshal([]byte(m.Metadata), &ci.Metadata); err != nil {
return nil, err return nil, err
} }
if ci.Public {
ci.Metadata.RedirectURIs = []url.URL{}
}
return &ci, nil 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) { func (r *clientRepo) New(tx repo.Transaction, cli client.Client) (*oidc.ClientCredentials, error) {
cim, err := newClientModel(cli) cim, err := newClientModel(cli)
if err != nil { if err != nil {
return nil, err return nil, err
} }
@ -328,3 +349,11 @@ func (r *clientRepo) SetTrustedPeers(tx repo.Transaction, clientID string, clien
return nil return nil
} }
func mustParseURL(s string) url.URL {
u, err := url.Parse(s)
if err != nil {
panic(err)
}
return *u
}

View file

@ -16,7 +16,8 @@ CREATE TABLE client_identity (
id text NOT NULL UNIQUE, id text NOT NULL UNIQUE,
secret blob, secret blob,
metadata text, metadata text,
dex_admin integer dex_admin integer,
public integer
); );
CREATE TABLE connector_config ( CREATE TABLE connector_config (

View file

@ -0,0 +1,4 @@
-- +migrate Up
ALTER TABLE client_identity ADD COLUMN "public" boolean;
UPDATE "client_identity" SET "public" = false;

View file

@ -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", "-- +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", Id: "0013_add_scopes_to_refresh_tokens.sql",
Up: []string{ Up: []string{