Skip to content
Projects
Groups
Snippets
Help
Loading...
Sign in
Toggle navigation
D
dex
Project
Project
Details
Activity
Cycle Analytics
Repository
Repository
Files
Commits
Branches
Tags
Contributors
Graph
Compare
Charts
Issues
0
Issues
0
List
Board
Labels
Milestones
Merge Requests
0
Merge Requests
0
Wiki
Wiki
Snippets
Snippets
Members
Members
Collapse sidebar
Close sidebar
Activity
Graph
Charts
Create a new issue
Commits
Issue Boards
Open sidebar
go
dex
Commits
8c9c2518
Commit
8c9c2518
authored
Apr 17, 2017
by
rithu john
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
server: account for dynamically changing connector object in storage.
parent
2b8caf9b
Hide whitespace changes
Inline
Side-by-side
Showing
10 changed files
with
400 additions
and
137 deletions
+400
-137
config.go
cmd/dex/config.go
+28
-34
config_test.go
cmd/dex/config_test.go
+1
-1
serve.go
cmd/dex/serve.go
+29
-31
handlers.go
server/handlers.go
+30
-21
server.go
server/server.go
+129
-22
server_test.go
server/server_test.go
+13
-23
conformance.go
storage/conformance/conformance.go
+4
-0
static_test.go
storage/memory/static_test.go
+91
-0
static.go
storage/static.go
+70
-0
storage.go
storage/storage.go
+5
-5
No files found.
cmd/dex/config.go
View file @
8c9c2518
...
...
@@ -9,13 +9,6 @@ import (
"github.com/Sirupsen/logrus"
"golang.org/x/crypto/bcrypt"
"github.com/coreos/dex/connector"
"github.com/coreos/dex/connector/github"
"github.com/coreos/dex/connector/gitlab"
"github.com/coreos/dex/connector/ldap"
"github.com/coreos/dex/connector/mock"
"github.com/coreos/dex/connector/oidc"
"github.com/coreos/dex/connector/saml"
"github.com/coreos/dex/server"
"github.com/coreos/dex/storage"
"github.com/coreos/dex/storage/kubernetes"
...
...
@@ -25,17 +18,20 @@ import (
// Config is the config format for the main application.
type
Config
struct
{
Issuer
string
`json:"issuer"`
Storage
Storage
`json:"storage"`
Connectors
[]
Connector
`json:"connectors"`
Web
Web
`json:"web"`
OAuth2
OAuth2
`json:"oauth2"`
GRPC
GRPC
`json:"grpc"`
Expiry
Expiry
`json:"expiry"`
Logger
Logger
`json:"logger"`
Issuer
string
`json:"issuer"`
Storage
Storage
`json:"storage"`
Web
Web
`json:"web"`
OAuth2
OAuth2
`json:"oauth2"`
GRPC
GRPC
`json:"grpc"`
Expiry
Expiry
`json:"expiry"`
Logger
Logger
`json:"logger"`
Frontend
server
.
WebConfig
`json:"frontend"`
// StaticConnectors are user defined connectors specified in the ConfigMap
// Write operations, like updating a connector, will fail.
StaticConnectors
[]
Connector
`json:"connectors"`
// 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
`json:"staticClients"`
...
...
@@ -170,24 +166,7 @@ type Connector struct {
Name
string
`json:"name"`
ID
string
`json:"id"`
Config
ConnectorConfig
`json:"config"`
}
// ConnectorConfig is a configuration that can open a connector.
type
ConnectorConfig
interface
{
Open
(
logrus
.
FieldLogger
)
(
connector
.
Connector
,
error
)
}
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
)
},
"gitlab"
:
func
()
ConnectorConfig
{
return
new
(
gitlab
.
Config
)
},
"oidc"
:
func
()
ConnectorConfig
{
return
new
(
oidc
.
Config
)
},
"saml"
:
func
()
ConnectorConfig
{
return
new
(
saml
.
Config
)
},
// Keep around for backwards compatibility.
"samlExperimental"
:
func
()
ConnectorConfig
{
return
new
(
saml
.
Config
)
},
Config
server
.
ConnectorConfig
`json:"config"`
}
// UnmarshalJSON allows Connector to implement the unmarshaler interface to
...
...
@@ -203,7 +182,7 @@ func (c *Connector) UnmarshalJSON(b []byte) error {
if
err
:=
json
.
Unmarshal
(
b
,
&
conn
);
err
!=
nil
{
return
fmt
.
Errorf
(
"parse connector: %v"
,
err
)
}
f
,
ok
:=
connectors
[
conn
.
Type
]
f
,
ok
:=
server
.
ConnectorsConfig
[
conn
.
Type
]
if
!
ok
{
return
fmt
.
Errorf
(
"unknown connector type %q"
,
conn
.
Type
)
}
...
...
@@ -224,6 +203,21 @@ func (c *Connector) UnmarshalJSON(b []byte) error {
return
nil
}
// ToStorageConnector converts an object to storage connector type.
func
ToStorageConnector
(
c
Connector
)
(
storage
.
Connector
,
error
)
{
data
,
err
:=
json
.
Marshal
(
c
.
Config
)
if
err
!=
nil
{
return
storage
.
Connector
{},
fmt
.
Errorf
(
"failed to marshal connector config: %v"
,
err
)
}
return
storage
.
Connector
{
ID
:
c
.
ID
,
Type
:
c
.
Type
,
Name
:
c
.
Name
,
Config
:
data
,
},
nil
}
// Expiry holds configuration for the validity period of components.
type
Expiry
struct
{
// SigningKeys defines the duration of time after which the SigningKeys will be rotated.
...
...
cmd/dex/config_test.go
View file @
8c9c2518
...
...
@@ -86,7 +86,7 @@ logger:
},
},
},
Connectors
:
[]
Connector
{
Static
Connectors
:
[]
Connector
{
{
Type
:
"mockCallback"
,
ID
:
"mock"
,
...
...
cmd/dex/serve.go
View file @
8c9c2518
...
...
@@ -74,7 +74,6 @@ func serve(cmd *cobra.Command, args []string) error {
errMsg
string
}{
{
c
.
Issuer
==
""
,
"no issuer specified in config file"
},
{
len
(
c
.
Connectors
)
==
0
&&
!
c
.
EnablePasswordDB
,
"no connectors supplied in config file"
},
{
!
c
.
EnablePasswordDB
&&
len
(
c
.
StaticPasswords
)
!=
0
,
"cannot specify static passwords without enabling password db"
},
{
c
.
Storage
.
Config
==
nil
,
"no storage suppied in config file"
},
{
c
.
Web
.
HTTP
==
""
&&
c
.
Web
.
HTTPS
==
""
,
"must supply a HTTP/HTTPS address to listen on"
},
...
...
@@ -128,34 +127,6 @@ func serve(cmd *cobra.Command, args []string) error {
}
}
connectors
:=
make
([]
server
.
Connector
,
len
(
c
.
Connectors
))
for
i
,
conn
:=
range
c
.
Connectors
{
if
conn
.
ID
==
""
{
return
fmt
.
Errorf
(
"invalid config: no ID field for connector %d"
,
i
)
}
if
conn
.
Config
==
nil
{
return
fmt
.
Errorf
(
"invalid config: no config field for connector %q"
,
conn
.
ID
)
}
if
conn
.
Name
==
""
{
return
fmt
.
Errorf
(
"invalid config: no Name field for connector %q"
,
conn
.
ID
)
}
logger
.
Infof
(
"config connector: %s"
,
conn
.
ID
)
connectorLogger
:=
logger
.
WithField
(
"connector"
,
conn
.
Name
)
c
,
err
:=
conn
.
Config
.
Open
(
connectorLogger
)
if
err
!=
nil
{
return
fmt
.
Errorf
(
"failed to create connector %s: %v"
,
conn
.
ID
,
err
)
}
connectors
[
i
]
=
server
.
Connector
{
ID
:
conn
.
ID
,
DisplayName
:
conn
.
Name
,
Connector
:
c
,
}
}
if
c
.
EnablePasswordDB
{
logger
.
Infof
(
"config connector: local passwords enabled"
)
}
s
,
err
:=
c
.
Storage
.
Config
.
Open
(
logger
)
if
err
!=
nil
{
return
fmt
.
Errorf
(
"failed to initialize storage: %v"
,
err
)
...
...
@@ -176,6 +147,35 @@ func serve(cmd *cobra.Command, args []string) error {
s
=
storage
.
WithStaticPasswords
(
s
,
passwords
)
}
if
c
.
EnablePasswordDB
{
c
.
StaticConnectors
=
append
(
c
.
StaticConnectors
,
Connector
{
ID
:
server
.
LocalConnector
,
Name
:
"Email"
,
Type
:
server
.
LocalConnector
,
})
logger
.
Infof
(
"config connector: local passwords enabled"
)
}
storageConnectors
:=
make
([]
storage
.
Connector
,
len
(
c
.
StaticConnectors
))
for
i
,
c
:=
range
c
.
StaticConnectors
{
if
c
.
ID
==
""
||
c
.
Name
==
""
||
c
.
Type
==
""
{
return
fmt
.
Errorf
(
"invalid config: ID, Type and Name fields are required for a connector"
)
}
if
c
.
Config
==
nil
{
return
fmt
.
Errorf
(
"invalid config: no config field for connector %q"
,
c
.
ID
)
}
logger
.
Infof
(
"config connector: %s"
,
c
.
ID
)
// convert to a storage connector object
conn
,
err
:=
ToStorageConnector
(
c
)
if
err
!=
nil
{
return
fmt
.
Errorf
(
"failed to initialize storage connectors: %v"
,
err
)
}
storageConnectors
[
i
]
=
conn
}
s
=
storage
.
WithStaticConnectors
(
s
,
storageConnectors
)
if
len
(
c
.
OAuth2
.
ResponseTypes
)
>
0
{
logger
.
Infof
(
"config response types accepted: %s"
,
c
.
OAuth2
.
ResponseTypes
)
}
...
...
@@ -194,10 +194,8 @@ func serve(cmd *cobra.Command, args []string) error {
SkipApprovalScreen
:
c
.
OAuth2
.
SkipApprovalScreen
,
AllowedOrigins
:
c
.
Web
.
AllowedOrigins
,
Issuer
:
c
.
Issuer
,
Connectors
:
connectors
,
Storage
:
s
,
Web
:
c
.
Frontend
,
EnablePasswordDB
:
c
.
EnablePasswordDB
,
Logger
:
logger
,
Now
:
now
,
}
...
...
server/handlers.go
View file @
8c9c2518
...
...
@@ -167,24 +167,31 @@ func (s *Server) handleAuthorization(w http.ResponseWriter, r *http.Request) {
return
}
if
len
(
s
.
connectors
)
==
1
{
for
id
:=
range
s
.
connectors
{
connectors
,
e
:=
s
.
storage
.
ListConnectors
()
if
e
!=
nil
{
s
.
logger
.
Errorf
(
"Failed to get list of connectors: %v"
,
err
)
s
.
renderError
(
w
,
http
.
StatusInternalServerError
,
"Failed to retrieve connector list."
)
return
}
if
len
(
connectors
)
==
1
{
for
_
,
c
:=
range
connectors
{
// TODO(ericchiang): Make this pass on r.URL.RawQuery and let something latter
// on create the auth request.
http
.
Redirect
(
w
,
r
,
s
.
absPath
(
"/auth"
,
id
)
+
"?req="
+
authReq
.
ID
,
http
.
StatusFound
)
http
.
Redirect
(
w
,
r
,
s
.
absPath
(
"/auth"
,
c
.
ID
)
+
"?req="
+
authReq
.
ID
,
http
.
StatusFound
)
return
}
}
connectorInfos
:=
make
([]
connectorInfo
,
len
(
s
.
connectors
))
connectorInfos
:=
make
([]
connectorInfo
,
len
(
connectors
))
i
:=
0
for
id
,
conn
:=
range
s
.
connectors
{
for
_
,
conn
:=
range
connectors
{
connectorInfos
[
i
]
=
connectorInfo
{
ID
:
id
,
Name
:
conn
.
Display
Name
,
ID
:
conn
.
ID
,
Name
:
conn
.
Name
,
// TODO(ericchiang): Make this pass on r.URL.RawQuery and let something latter
// on create the auth request.
URL
:
s
.
absPath
(
"/auth"
,
id
)
+
"?req="
+
authReq
.
ID
,
URL
:
s
.
absPath
(
"/auth"
,
conn
.
ID
)
+
"?req="
+
authReq
.
ID
,
}
i
++
}
...
...
@@ -196,10 +203,10 @@ func (s *Server) handleAuthorization(w http.ResponseWriter, r *http.Request) {
func
(
s
*
Server
)
handleConnectorLogin
(
w
http
.
ResponseWriter
,
r
*
http
.
Request
)
{
connID
:=
mux
.
Vars
(
r
)[
"connector"
]
conn
,
ok
:=
s
.
connectors
[
connID
]
if
!
ok
{
s
.
logger
.
Errorf
(
"Failed to create authorization request
."
)
s
.
renderError
(
w
,
http
.
StatusBadRequest
,
"Requested resource does not exist
.
"
)
conn
,
err
:=
s
.
getConnector
(
connID
)
if
err
!=
nil
{
s
.
logger
.
Errorf
(
"Failed to create authorization request
: %v"
,
err
)
s
.
renderError
(
w
,
http
.
StatusBadRequest
,
"Requested resource does not exist"
)
return
}
...
...
@@ -339,8 +346,9 @@ func (s *Server) handleConnectorCallback(w http.ResponseWriter, r *http.Request)
return
}
conn
,
ok
:=
s
.
connectors
[
authReq
.
ConnectorID
]
if
!
ok
{
conn
,
err
:=
s
.
getConnector
(
authReq
.
ConnectorID
)
if
err
!=
nil
{
s
.
logger
.
Errorf
(
"Failed to get connector with id %q : %v"
,
authReq
.
ConnectorID
,
err
)
s
.
renderError
(
w
,
http
.
StatusInternalServerError
,
"Requested resource does not exist."
)
return
}
...
...
@@ -649,13 +657,14 @@ func (s *Server) handleAuthCode(w http.ResponseWriter, r *http.Request, client s
// Ensure the connector supports refresh tokens.
//
// Connectors like `saml` do not implement RefreshConnector.
conn
,
ok
:=
s
.
connectors
[
authCode
.
ConnectorID
]
if
!
ok
{
s
.
logger
.
Errorf
(
"connector
ID not found: %q"
,
authCode
.
ConnectorID
)
conn
,
err
:=
s
.
getConnector
(
authCode
.
ConnectorID
)
if
err
!=
nil
{
s
.
logger
.
Errorf
(
"connector
with ID %q not found: %v"
,
authCode
.
ConnectorID
,
err
)
s
.
tokenErrHelper
(
w
,
errServerError
,
""
,
http
.
StatusInternalServerError
)
return
false
}
_
,
ok
=
conn
.
Connector
.
(
connector
.
RefreshConnector
)
_
,
ok
:=
conn
.
Connector
.
(
connector
.
RefreshConnector
)
if
!
ok
{
return
false
}
...
...
@@ -841,9 +850,9 @@ func (s *Server) handleRefreshToken(w http.ResponseWriter, r *http.Request, clie
scopes
=
requestedScopes
}
conn
,
ok
:=
s
.
connectors
[
refresh
.
ConnectorID
]
if
!
ok
{
s
.
logger
.
Errorf
(
"connector
ID not found: %q"
,
refresh
.
ConnectorID
)
conn
,
err
:=
s
.
getConnector
(
refresh
.
ConnectorID
)
if
err
!=
nil
{
s
.
logger
.
Errorf
(
"connector
with ID %q not found: %v"
,
refresh
.
ConnectorID
,
err
)
s
.
tokenErrHelper
(
w
,
errServerError
,
""
,
http
.
StatusInternalServerError
)
return
}
...
...
server/server.go
View file @
8c9c2518
...
...
@@ -2,11 +2,13 @@ package server
import
(
"context"
"encoding/json"
"errors"
"fmt"
"net/http"
"net/url"
"path"
"sync"
"sync/atomic"
"time"
...
...
@@ -17,14 +19,23 @@ import (
"github.com/gorilla/mux"
"github.com/coreos/dex/connector"
"github.com/coreos/dex/connector/github"
"github.com/coreos/dex/connector/gitlab"
"github.com/coreos/dex/connector/ldap"
"github.com/coreos/dex/connector/mock"
"github.com/coreos/dex/connector/oidc"
"github.com/coreos/dex/connector/saml"
"github.com/coreos/dex/storage"
)
// Connector is a connector with metadata.
// LocalConnector is the local passwordDB connector which is an internal
// connector maintained by the server.
const
LocalConnector
=
"local"
// Connector is a connector with resource version metadata.
type
Connector
struct
{
ID
string
DisplayName
string
Connector
connector
.
Connector
ResourceVersion
string
Connector
connector
.
Connector
}
// Config holds the server's configuration options.
...
...
@@ -36,9 +47,6 @@ type Config struct {
// The backing persistence layer.
Storage
storage
.
Storage
// Strategies for federated identity.
Connectors
[]
Connector
// Valid values are "code" to enable the code flow and "token" to enable the implicit
// flow. If no response types are supplied this value defaults to "code".
SupportedResponseTypes
[]
string
...
...
@@ -60,8 +68,6 @@ type Config struct {
// If specified, the server will use this function for determining time.
Now
func
()
time
.
Time
EnablePasswordDB
bool
Web
WebConfig
Logger
logrus
.
FieldLogger
...
...
@@ -103,7 +109,9 @@ func value(val, defaultValue time.Duration) time.Duration {
type
Server
struct
{
issuerURL
url
.
URL
// Read-only map of connector IDs to connectors.
// mutex for the connectors map.
mu
sync
.
Mutex
// Map of connector IDs to connectors.
connectors
map
[
string
]
Connector
storage
storage
.
Storage
...
...
@@ -137,17 +145,7 @@ func newServer(ctx context.Context, c Config, rotationStrategy rotationStrategy)
if
err
!=
nil
{
return
nil
,
fmt
.
Errorf
(
"server: can't parse issuer URL"
)
}
if
c
.
EnablePasswordDB
{
c
.
Connectors
=
append
(
c
.
Connectors
,
Connector
{
ID
:
"local"
,
DisplayName
:
"Email"
,
Connector
:
newPasswordDB
(
c
.
Storage
),
})
}
if
len
(
c
.
Connectors
)
==
0
{
return
nil
,
errors
.
New
(
"server: no connectors specified"
)
}
if
c
.
Storage
==
nil
{
return
nil
,
errors
.
New
(
"server: storage cannot be nil"
)
}
...
...
@@ -195,8 +193,21 @@ func newServer(ctx context.Context, c Config, rotationStrategy rotationStrategy)
logger
:
c
.
Logger
,
}
for
_
,
conn
:=
range
c
.
Connectors
{
s
.
connectors
[
conn
.
ID
]
=
conn
// Retrieves connector objects in backend storage. This list includes the static connectors
// defined in the ConfigMap and dynamic connectors retrieved from the storage.
storageConnectors
,
err
:=
c
.
Storage
.
ListConnectors
()
if
err
!=
nil
{
return
nil
,
fmt
.
Errorf
(
"server: failed to list connector objects from storage: %v"
,
err
)
}
if
len
(
storageConnectors
)
==
0
&&
len
(
s
.
connectors
)
==
0
{
return
nil
,
errors
.
New
(
"server: no connectors specified"
)
}
for
_
,
conn
:=
range
storageConnectors
{
if
_
,
err
:=
s
.
OpenConnector
(
conn
);
err
!=
nil
{
return
nil
,
fmt
.
Errorf
(
"server: Failed to open connector %s: %v"
,
conn
.
ID
,
err
)
}
}
r
:=
mux
.
NewRouter
()
...
...
@@ -362,3 +373,99 @@ func (s *Server) startGarbageCollection(ctx context.Context, frequency time.Dura
}()
return
}
// ConnectorConfig is a configuration that can open a connector.
type
ConnectorConfig
interface
{
Open
(
logrus
.
FieldLogger
)
(
connector
.
Connector
,
error
)
}
// ConnectorsConfig variable provides an easy way to return a config struct
// depending on the connector type.
var
ConnectorsConfig
=
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
)
},
"gitlab"
:
func
()
ConnectorConfig
{
return
new
(
gitlab
.
Config
)
},
"oidc"
:
func
()
ConnectorConfig
{
return
new
(
oidc
.
Config
)
},
"saml"
:
func
()
ConnectorConfig
{
return
new
(
saml
.
Config
)
},
// Keep around for backwards compatibility.
"samlExperimental"
:
func
()
ConnectorConfig
{
return
new
(
saml
.
Config
)
},
}
// openConnector will parse the connector config and open the connector.
func
openConnector
(
logger
logrus
.
FieldLogger
,
conn
storage
.
Connector
)
(
connector
.
Connector
,
error
)
{
var
c
connector
.
Connector
f
,
ok
:=
ConnectorsConfig
[
conn
.
Type
]
if
!
ok
{
return
c
,
fmt
.
Errorf
(
"unknown connector type %q"
,
conn
.
Type
)
}
connConfig
:=
f
()
if
len
(
conn
.
Config
)
!=
0
{
data
:=
[]
byte
(
string
(
conn
.
Config
))
if
err
:=
json
.
Unmarshal
(
data
,
connConfig
);
err
!=
nil
{
return
c
,
fmt
.
Errorf
(
"parse connector config: %v"
,
err
)
}
}
c
,
err
:=
connConfig
.
Open
(
logger
)
if
err
!=
nil
{
return
c
,
fmt
.
Errorf
(
"failed to create connector %s: %v"
,
conn
.
ID
,
err
)
}
return
c
,
nil
}
// OpenConnector updates server connector map with specified connector object.
func
(
s
*
Server
)
OpenConnector
(
conn
storage
.
Connector
)
(
Connector
,
error
)
{
var
c
connector
.
Connector
if
conn
.
Type
==
LocalConnector
{
c
=
newPasswordDB
(
s
.
storage
)
}
else
{
var
err
error
c
,
err
=
openConnector
(
s
.
logger
.
WithField
(
"connector"
,
conn
.
Name
),
conn
)
if
err
!=
nil
{
return
Connector
{},
fmt
.
Errorf
(
"failed to open connector: %v"
,
err
)
}
}
connector
:=
Connector
{
ResourceVersion
:
conn
.
ResourceVersion
,
Connector
:
c
,
}
s
.
mu
.
Lock
()
s
.
connectors
[
conn
.
ID
]
=
connector
s
.
mu
.
Unlock
()
return
connector
,
nil
}
// getConnector retrieves the connector object with the given id from the storage
// and updates the connector list for server if necessary.
func
(
s
*
Server
)
getConnector
(
id
string
)
(
Connector
,
error
)
{
storageConnector
,
err
:=
s
.
storage
.
GetConnector
(
id
)
if
err
!=
nil
{
return
Connector
{},
fmt
.
Errorf
(
"failed to get connector object from storage: %v"
,
err
)
}
var
conn
Connector
var
ok
bool
s
.
mu
.
Lock
()
conn
,
ok
=
s
.
connectors
[
id
]
s
.
mu
.
Unlock
()
if
!
ok
||
storageConnector
.
ResourceVersion
!=
conn
.
ResourceVersion
{
// Connector object does not exist in server connectors map or
// has been updated in the storage. Need to get latest.
conn
,
err
:=
s
.
OpenConnector
(
storageConnector
)
if
err
!=
nil
{
return
Connector
{},
fmt
.
Errorf
(
"failed to open connector: %v"
,
err
)
}
return
conn
,
nil
}
return
conn
,
nil
}
server/server_test.go
View file @
8c9c2518
...
...
@@ -89,13 +89,6 @@ func newTestServer(ctx context.Context, t *testing.T, updateConfig func(c *Confi
config
:=
Config
{
Issuer
:
s
.
URL
,
Storage
:
memory
.
New
(
logger
),
Connectors
:
[]
Connector
{
{
ID
:
"mock"
,
DisplayName
:
"Mock"
,
Connector
:
mock
.
NewCallbackConnector
(
logger
),
},
},
Web
:
WebConfig
{
Dir
:
filepath
.
Join
(
os
.
Getenv
(
"GOPATH"
),
"src/github.com/coreos/dex/web"
),
},
...
...
@@ -106,6 +99,16 @@ func newTestServer(ctx context.Context, t *testing.T, updateConfig func(c *Confi
}
s
.
URL
=
config
.
Issuer
connector
:=
storage
.
Connector
{
ID
:
"mock"
,
Type
:
"mockCallback"
,
Name
:
"Mock"
,
ResourceVersion
:
"1"
,
}
if
err
:=
config
.
Storage
.
CreateConnector
(
connector
);
err
!=
nil
{
t
.
Fatalf
(
"create connector: %v"
,
err
)
}
var
err
error
if
server
,
err
=
newServer
(
ctx
,
config
,
staticRotationStrategy
(
testKey
));
err
!=
nil
{
t
.
Fatal
(
err
)
...
...
@@ -416,29 +419,16 @@ func TestOAuth2CodeFlow(t *testing.T) {
defer
cancel
()
// Setup a dex server.
logger
:=
&
logrus
.
Logger
{
Out
:
os
.
Stderr
,
Formatter
:
&
logrus
.
TextFormatter
{
DisableColors
:
true
},
Level
:
logrus
.
DebugLevel
,
}
httpServer
,
s
:=
newTestServer
(
ctx
,
t
,
func
(
c
*
Config
)
{
c
.
Issuer
=
c
.
Issuer
+
"/non-root-path"
c
.
Now
=
now
c
.
IDTokensValidFor
=
idTokensValidFor
// Testing connector that redirects without interaction with
// the user.
conn
=
mock
.
NewCallbackConnector
(
logger
)
.
(
*
mock
.
Callback
)
c
.
Connectors
=
[]
Connector
{
{
ID
:
"mock"
,
DisplayName
:
"mock"
,
Connector
:
conn
,
},
}
})
defer
httpServer
.
Close
()
mockConn
:=
s
.
connectors
[
"mock"
]
conn
=
mockConn
.
Connector
.
(
*
mock
.
Callback
)
// Query server's provider metadata.
p
,
err
:=
oidc
.
NewProvider
(
ctx
,
httpServer
.
URL
)
if
err
!=
nil
{
...
...
storage/conformance/conformance.go
View file @
8c9c2518
...
...
@@ -628,6 +628,10 @@ func testConnectorCRUD(t *testing.T, s storage.Storage) {
c1
.
Type
=
"oidc"
getAndCompare
(
id1
,
c1
)
if
_
,
err
:=
s
.
ListConnectors
();
err
!=
nil
{
t
.
Fatalf
(
"failed to list connectors: %v"
,
err
)
}
if
err
:=
s
.
DeleteConnector
(
c1
.
ID
);
err
!=
nil
{
t
.
Fatalf
(
"failed to delete connector: %v"
,
err
)
}
...
...
storage/memory/static_test.go
View file @
8c9c2518
...
...
@@ -190,3 +190,94 @@ func TestStaticPasswords(t *testing.T) {
}
}
}
func
TestStaticConnectors
(
t
*
testing
.
T
)
{
logger
:=
&
logrus
.
Logger
{
Out
:
os
.
Stderr
,
Formatter
:
&
logrus
.
TextFormatter
{
DisableColors
:
true
},
Level
:
logrus
.
DebugLevel
,
}
backing
:=
New
(
logger
)
config1
:=
[]
byte
(
`{"issuer": "https://accounts.google.com"}`
)
config2
:=
[]
byte
(
`{"host": "ldap.example.com:636"}`
)
config3
:=
[]
byte
(
`{"issuer": "https://example.com"}`
)
c1
:=
storage
.
Connector
{
ID
:
storage
.
NewID
(),
Type
:
"oidc"
,
Name
:
"oidc"
,
ResourceVersion
:
"1"
,
Config
:
config1
}
c2
:=
storage
.
Connector
{
ID
:
storage
.
NewID
(),
Type
:
"ldap"
,
Name
:
"ldap"
,
ResourceVersion
:
"1"
,
Config
:
config2
}
c3
:=
storage
.
Connector
{
ID
:
storage
.
NewID
(),
Type
:
"saml"
,
Name
:
"saml"
,
ResourceVersion
:
"1"
,
Config
:
config3
}
backing
.
CreateConnector
(
c1
)
s
:=
storage
.
WithStaticConnectors
(
backing
,
[]
storage
.
Connector
{
c2
})
tests
:=
[]
struct
{
name
string
action
func
()
error
wantErr
bool
}{
{
name
:
"get connector from static storage"
,
action
:
func
()
error
{
_
,
err
:=
s
.
GetConnector
(
c2
.
ID
)
return
err
},
},
{
name
:
"get connector from backing storage"
,
action
:
func
()
error
{
_
,
err
:=
s
.
GetConnector
(
c1
.
ID
)
return
err
},
},
{
name
:
"update static connector"
,
action
:
func
()
error
{
updater
:=
func
(
c
storage
.
Connector
)
(
storage
.
Connector
,
error
)
{
c
.
Name
=
"New"
return
c
,
nil
}
return
s
.
UpdateConnector
(
c2
.
ID
,
updater
)
},
wantErr
:
true
,
},
{
name
:
"update non-static connector"
,
action
:
func
()
error
{
updater
:=
func
(
c
storage
.
Connector
)
(
storage
.
Connector
,
error
)
{
c
.
Name
=
"New"
return
c
,
nil
}
return
s
.
UpdateConnector
(
c1
.
ID
,
updater
)
},
},
{
name
:
"list connectors"
,
action
:
func
()
error
{
connectors
,
err
:=
s
.
ListConnectors
()
if
err
!=
nil
{
return
err
}
if
n
:=
len
(
connectors
);
n
!=
2
{
return
fmt
.
Errorf
(
"expected 2 connectors got %d"
,
n
)
}
return
nil
},
},
{
name
:
"create connector"
,
action
:
func
()
error
{
return
s
.
CreateConnector
(
c3
)
},
},
}
for
_
,
tc
:=
range
tests
{
err
:=
tc
.
action
()
if
err
!=
nil
&&
!
tc
.
wantErr
{
t
.
Errorf
(
"%s: %v"
,
tc
.
name
,
err
)
}
if
err
==
nil
&&
tc
.
wantErr
{
t
.
Errorf
(
"%s: expected error, didn't get one"
,
tc
.
name
)
}
}
}
storage/static.go
View file @
8c9c2518
...
...
@@ -150,3 +150,73 @@ func (s staticPasswordsStorage) UpdatePassword(email string, updater func(old Pa
}
return
s
.
Storage
.
UpdatePassword
(
email
,
updater
)
}
// staticConnectorsStorage represents a storage with read-only set of connectors.
type
staticConnectorsStorage
struct
{
Storage
// A read-only set of connectors.
connectors
[]
Connector
connectorsByID
map
[
string
]
Connector
}
// WithStaticConnectors returns a storage with a read-only set of Connectors. Write actions,
// such as updating existing Connectors, will fail.
func
WithStaticConnectors
(
s
Storage
,
staticConnectors
[]
Connector
)
Storage
{
connectorsByID
:=
make
(
map
[
string
]
Connector
,
len
(
staticConnectors
))
for
_
,
c
:=
range
staticConnectors
{
connectorsByID
[
c
.
ID
]
=
c
}
return
staticConnectorsStorage
{
s
,
staticConnectors
,
connectorsByID
}
}
func
(
s
staticConnectorsStorage
)
isStatic
(
id
string
)
bool
{
_
,
ok
:=
s
.
connectorsByID
[
id
]
return
ok
}
func
(
s
staticConnectorsStorage
)
GetConnector
(
id
string
)
(
Connector
,
error
)
{
if
connector
,
ok
:=
s
.
connectorsByID
[
id
];
ok
{
return
connector
,
nil
}
return
s
.
Storage
.
GetConnector
(
id
)
}
func
(
s
staticConnectorsStorage
)
ListConnectors
()
([]
Connector
,
error
)
{
connectors
,
err
:=
s
.
Storage
.
ListConnectors
()
if
err
!=
nil
{
return
nil
,
err
}
n
:=
0
for
_
,
connector
:=
range
connectors
{
// If an entry has the same id as those provided in the static
// values, prefer the static value.
if
!
s
.
isStatic
(
connector
.
ID
)
{
connectors
[
n
]
=
connector
n
++
}
}
return
append
(
connectors
[
:
n
],
s
.
connectors
...
),
nil
}
func
(
s
staticConnectorsStorage
)
CreateConnector
(
c
Connector
)
error
{
if
s
.
isStatic
(
c
.
ID
)
{
return
errors
.
New
(
"static connectors: read-only cannot create connector"
)
}
return
s
.
Storage
.
CreateConnector
(
c
)
}
func
(
s
staticConnectorsStorage
)
DeleteConnector
(
id
string
)
error
{
if
s
.
isStatic
(
id
)
{
return
errors
.
New
(
"static connectors: read-only cannot delete connector"
)
}
return
s
.
Storage
.
DeleteConnector
(
id
)
}
func
(
s
staticConnectorsStorage
)
UpdateConnector
(
id
string
,
updater
func
(
old
Connector
)
(
Connector
,
error
))
error
{
if
s
.
isStatic
(
id
)
{
return
errors
.
New
(
"static connectors: read-only cannot update connector"
)
}
return
s
.
Storage
.
UpdateConnector
(
id
,
updater
)
}
storage/storage.go
View file @
8c9c2518
...
...
@@ -298,17 +298,17 @@ type Password struct {
// Connector is an object that contains the metadata about connectors used to login to Dex.
type
Connector
struct
{
// ID that will uniquely identify the connector object.
ID
string
ID
string
`json:"id"`
// The Type of the connector. E.g. 'oidc' or 'ldap'
Type
string
Type
string
`json:"type"`
// The Name of the connector that is used when displaying it to the end user.
Name
string
Name
string
`json:"name"`
// ResourceVersion is the static versioning used to keep track of dynamic configuration
// changes to the connector object made by the API calls.
ResourceVersion
string
ResourceVersion
string
`json:"resourceVersion"`
// Config holds all the configuration information specific to the connector type. Since there
// no generic struct we can use for this purpose, it is stored as a byte stream.
Config
[]
byte
Config
[]
byte
`json:"email"`
}
// VerificationKey is a rotated signing key which can still be used to verify
...
...
Write
Preview
Markdown
is supported
0%
Try again
or
attach a new file
Attach a file
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Cancel
Please
register
or
sign in
to comment