client: add public client to data model
This commit is contained in:
parent
a530cc8d7c
commit
09e889e7bc
6 changed files with 92 additions and 9 deletions
|
@ -19,9 +19,22 @@ var (
|
||||||
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.")
|
||||||
|
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")
|
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 {
|
||||||
|
@ -138,6 +153,7 @@ func ClientsFromReader(r io.Reader) ([]LoadableClient, error) {
|
||||||
RedirectURIs: redirectURIs,
|
RedirectURIs: redirectURIs,
|
||||||
},
|
},
|
||||||
Admin: client.Admin,
|
Admin: client.Admin,
|
||||||
|
Public: client.Public,
|
||||||
},
|
},
|
||||||
TrustedPeers: client.TrustedPeers,
|
TrustedPeers: client.TrustedPeers,
|
||||||
}
|
}
|
||||||
|
|
|
@ -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,
|
||||||
|
|
31
db/client.go
31
db/client.go
|
@ -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 {
|
||||||
|
@ -77,12 +94,17 @@ func (m *clientModel) Client() (*client.Client, error) {
|
||||||
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
|
||||||
|
}
|
||||||
|
|
|
@ -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 (
|
||||||
|
|
4
db/migrations/0013_add_public_clients.sql
Normal file
4
db/migrations/0013_add_public_clients.sql
Normal file
|
@ -0,0 +1,4 @@
|
||||||
|
-- +migrate Up
|
||||||
|
ALTER TABLE client_identity ADD COLUMN "public" boolean;
|
||||||
|
|
||||||
|
UPDATE "client_identity" SET "public" = false;
|
|
@ -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{
|
||||||
|
|
Reference in a new issue