@ -1,7 +1,7 @@
package main
import (
"encoding/base64 "
"encoding/json "
"fmt"
"github.com/coreos/dex/connector"
@ -18,144 +18,108 @@ import (
// Config is the config format for the main application.
type Config struct {
Issuer string ` yaml :"issuer"`
Storage Storage ` yaml :"storage"`
Connectors [ ] Connector ` yaml :"connectors"`
Web Web ` yaml :"web"`
OAuth2 OAuth2 ` yaml :"oauth2"`
GRPC GRPC ` yaml :"grpc"`
Issuer string ` json :"issuer"`
Storage Storage ` json :"storage"`
Connectors [ ] Connector ` json :"connectors"`
Web Web ` json :"web"`
OAuth2 OAuth2 ` json :"oauth2"`
GRPC GRPC ` json :"grpc"`
Templates server . TemplateConfig ` yaml :"templates"`
Templates server . TemplateConfig ` json :"templates"`
// StaticClients cause the server to use this list of clients rather than
// querying the storage. Write operations, like creating a client, will fail.
StaticClients [ ] storage . Client ` yaml :"staticClients"`
StaticClients [ ] storage . Client ` json :"staticClients"`
// If enabled, the server will maintain a list of passwords which can be used
// to identify a user.
EnablePasswordDB bool ` yaml :"enablePasswordDB"`
EnablePasswordDB bool ` json :"enablePasswordDB"`
// StaticPasswords cause the server use this list of passwords rather than
// querying the storage. Cannot be specified without enabling a passwords
// database.
//
// The "password" type is identical to the storage.Password type, but does
// unmarshaling into []byte correctly.
StaticPasswords [ ] password ` yaml:"staticPasswords" `
}
type password struct {
Email string ` yaml:"email" `
Username string ` yaml:"username" `
UserID string ` yaml:"userID" `
// Because our YAML parser doesn't base64, we have to do it ourselves.
//
// TODO(ericchiang): switch to github.com/ghodss/yaml
Hash string ` yaml:"hash" `
}
// decode the hash appropriately and convert to the storage passwords.
func ( p password ) toPassword ( ) ( storage . Password , error ) {
hash , err := base64 . StdEncoding . DecodeString ( p . Hash )
if err != nil {
return storage . Password { } , fmt . Errorf ( "decoding hash: %v" , err )
}
return storage . Password {
Email : p . Email ,
Username : p . Username ,
UserID : p . UserID ,
Hash : hash ,
} , nil
StaticPasswords [ ] storage . Password ` json:"staticPasswords" `
}
// OAuth2 describes enabled OAuth2 extensions.
type OAuth2 struct {
ResponseTypes [ ] string ` yaml :"responseTypes"`
ResponseTypes [ ] string ` json:"responseTypes" `
// If specified, do not prompt the user to approve client authorization. The
// act of logging in implies authorization.
SkipApprovalScreen bool ` yaml :"skipApprovalScreen"`
SkipApprovalScreen bool ` json:"skipApprovalScreen" `
}
// Web is the config format for the HTTP server.
type Web struct {
HTTP string ` yaml :"http"`
HTTPS string ` yaml :"https"`
TLSCert string ` yaml :"tlsCert"`
TLSKey string ` yaml :"tlsKey"`
HTTP string ` json:"http" `
HTTPS string ` json:"https" `
TLSCert string ` json:"tlsCert" `
TLSKey string ` json:"tlsKey" `
}
// GRPC is the config for the gRPC API.
type GRPC struct {
// The port to listen on.
Addr string ` yaml :"addr"`
TLSCert string ` yaml :"tlsCert"`
TLSKey string ` yaml :"tlsKey"`
TLSClientCA string ` yaml :"tlsClientCA"`
Addr string ` json:"addr" `
TLSCert string ` json:"tlsCert" `
TLSKey string ` json:"tlsKey" `
TLSClientCA string ` json:"tlsClientCA" `
}
// Storage holds app's storage configuration.
type Storage struct {
Type string ` yaml :"type"`
Config StorageConfig ` yaml :"config"`
Type string ` json :"type"`
Config StorageConfig ` json :"config"`
}
// UnmarshalYAML allows Storage to unmarshal its config field dynamically
// depending on the type of storage.
func ( s * Storage ) UnmarshalYAML ( unmarshal func ( interface { } ) error ) error {
var storageMeta struct {
Type string ` yaml:"type" `
// StorageConfig is a configuration that can create a storage.
type StorageConfig interface {
Open ( ) ( storage . Storage , error )
}
var storages = map [ string ] func ( ) StorageConfig {
"kubernetes" : func ( ) StorageConfig { return new ( kubernetes . Config ) } ,
"memory" : func ( ) StorageConfig { return new ( memory . Config ) } ,
"sqlite3" : func ( ) StorageConfig { return new ( sql . SQLite3 ) } ,
"postgres" : func ( ) StorageConfig { return new ( sql . Postgres ) } ,
}
// UnmarshalJSON allows Storage to implement the unmarshaler interface to
// dynamically determine the type of the storage config.
func ( s * Storage ) UnmarshalJSON ( b [ ] byte ) error {
var store struct {
Type string ` json:"type" `
Config json . RawMessage ` json:"config" `
}
if err := unmarshal ( & storageMeta ) ; err != nil {
return err
if err := json . U nmarshal( b , & store ) ; err != nil {
return fmt . Errorf ( "parse storage: %v" , err )
}
s . Type = storageMeta . Type
// TODO(ericchiang): replace this with a registration process.
var err error
switch storageMeta . Type {
case "kubernetes" :
var config struct {
Config kubernetes . Config ` yaml:"config" `
}
err = unmarshal ( & config )
s . Config = & config . Config
case "memory" :
var config struct {
Config memory . Config ` yaml:"config" `
}
err = unmarshal ( & config )
s . Config = & config . Config
case "sqlite3" :
var config struct {
Config sql . SQLite3 ` yaml:"config" `
}
err = unmarshal ( & config )
s . Config = & config . Config
case "postgres" :
var config struct {
Config sql . Postgres ` yaml:"config" `
}
err = unmarshal ( & config )
s . Config = & config . Config
default :
return fmt . Errorf ( "unknown storage type %q" , storageMeta . Type )
f , ok := storages [ store . Type ]
if ! ok {
return fmt . Errorf ( "unknown storage type %q" , store . Type )
}
return err
}
// StorageConfig is a configuration that can create a storage.
type StorageConfig interface {
Open ( ) ( storage . Storage , error )
storageConfig := f ( )
if len ( store . Config ) != 0 {
if err := json . Unmarshal ( [ ] byte ( store . Config ) , storageConfig ) ; err != nil {
return fmt . Errorf ( "parse storace config: %v" , err )
}
}
* s = Storage {
Type : store . Type ,
Config : storageConfig ,
}
return nil
}
// Connector is a magical type that can unmarshal YAML dynamically. The
// Type field determines the connector type, which is then customized for Config.
type Connector struct {
Type string ` yaml:"type" `
Name string ` yaml:"name" `
ID string ` yaml:"id" `
Type string ` json:"type" `
Name string ` json:"name" `
ID string ` json:"id" `
Config ConnectorConfig ` yaml :"config"`
Config ConnectorConfig ` json:"config" `
}
// ConnectorConfig is a configuration that can open a connector.
@ -163,55 +127,43 @@ type ConnectorConfig interface {
Open ( ) ( connector . Connector , error )
}
// UnmarshalYAML allows Connector to unmarshal its config field dynamically
// depending on the type of connector.
func ( c * Connector ) UnmarshalYAML ( unmarshal func ( interface { } ) error ) error {
var connectorMetadata struct {
Type string ` yaml:"type" `
Name string ` yaml:"name" `
ID string ` yaml:"id" `
var connectors = map [ string ] func ( ) ConnectorConfig {
"mockCallback" : func ( ) ConnectorConfig { return new ( mock . CallbackConfig ) } ,
"mockPassword" : func ( ) ConnectorConfig { return new ( mock . PasswordConfig ) } ,
"ldap" : func ( ) ConnectorConfig { return new ( ldap . Config ) } ,
"github" : func ( ) ConnectorConfig { return new ( github . Config ) } ,
"oidc" : func ( ) ConnectorConfig { return new ( oidc . Config ) } ,
}
// UnmarshalJSON allows Connector to implement the unmarshaler interface to
// dynamically determine the type of the connector config.
func ( c * Connector ) UnmarshalJSON ( b [ ] byte ) error {
var conn struct {
Type string ` json:"type" `
Name string ` json:"name" `
ID string ` json:"id" `
Config json . RawMessage ` json:"config" `
}
if err := unmarshal ( & connectorMetadata ) ; err != nil {
return err
if err := json . U nmarshal( b , & conn ) ; err != nil {
return fmt . Errorf ( "parse connector: %v" , err )
}
c . Type = connectorMetadata . Type
c . Name = connectorMetadata . Name
c . ID = connectorMetadata . ID
var err error
switch c . Type {
case "mockCallback" :
var config struct {
Config mock . CallbackConfig ` yaml:"config" `
}
err = unmarshal ( & config )
c . Config = & config . Config
case "mockPassword" :
var config struct {
Config mock . PasswordConfig ` yaml:"config" `
}
err = unmarshal ( & config )
c . Config = & config . Config
case "ldap" :
var config struct {
Config ldap . Config ` yaml:"config" `
}
err = unmarshal ( & config )
c . Config = & config . Config
case "github" :
var config struct {
Config github . Config ` yaml:"config" `
}
err = unmarshal ( & config )
c . Config = & config . Config
case "oidc" :
var config struct {
Config oidc . Config ` yaml:"config" `
f , ok := connectors [ conn . Type ]
if ! ok {
return fmt . Errorf ( "unknown connector type %q" , conn . Type )
}
connConfig := f ( )
if len ( conn . Config ) != 0 {
if err := json . Unmarshal ( [ ] byte ( conn . Config ) , connConfig ) ; err != nil {
return fmt . Errorf ( "parse connector config: %v" , err )
}
err = unmarshal ( & config )
c . Config = & config . Config
default :
return fmt . Errorf ( "unknown connector type %q" , c . Type )
}
return err
* c = Connector {
Type : conn . Type ,
Name : conn . Type ,
ID : conn . ID ,
Config : connConfig ,
}
return nil
}