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
55e97d90
Commit
55e97d90
authored
Nov 21, 2016
by
Eric Chiang
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
*: add tests for the RefreshConnector
parent
952e0f81
Hide whitespace changes
Inline
Side-by-side
Showing
2 changed files
with
102 additions
and
23 deletions
+102
-23
connectortest.go
connector/mock/connectortest.go
+25
-19
server_test.go
server/server_test.go
+77
-4
No files found.
connector/mock/connectortest.go
View file @
55e97d90
...
...
@@ -15,20 +15,32 @@ import (
// NewCallbackConnector returns a mock connector which requires no user interaction. It always returns
// the same (fake) identity.
func
NewCallbackConnector
()
connector
.
Connector
{
return
callbackConnector
{}
return
&
Callback
{
Identity
:
connector
.
Identity
{
UserID
:
"0-385-28089-0"
,
Username
:
"Kilgore Trout"
,
Email
:
"kilgore@kilgore.trout"
,
EmailVerified
:
true
,
Groups
:
[]
string
{
"authors"
},
ConnectorData
:
connectorData
,
},
}
}
var
(
_
connector
.
CallbackConnector
=
callbackConnector
{}
_
connector
.
CallbackConnector
=
&
Callback
{}
_
connector
.
PasswordConnector
=
passwordConnector
{}
)
type
callbackConnector
struct
{}
func
(
m
callbackConnector
)
Close
()
error
{
return
nil
}
// Callback is a connector that requires no user interaction and always returns the same identity.
type
Callback
struct
{
// The returned identity.
Identity
connector
.
Identity
}
func
(
m
callbackConnector
)
LoginURL
(
s
connector
.
Scopes
,
callbackURL
,
state
string
)
(
string
,
error
)
{
// LoginURL returns the URL to redirect the user to login with.
func
(
m
*
Callback
)
LoginURL
(
s
connector
.
Scopes
,
callbackURL
,
state
string
)
(
string
,
error
)
{
u
,
err
:=
url
.
Parse
(
callbackURL
)
if
err
!=
nil
{
return
""
,
fmt
.
Errorf
(
"failed to parse callbackURL %q: %v"
,
callbackURL
,
err
)
...
...
@@ -41,20 +53,14 @@ func (m callbackConnector) LoginURL(s connector.Scopes, callbackURL, state strin
var
connectorData
=
[]
byte
(
"foobar"
)
func
(
m
callbackConnector
)
HandleCallback
(
s
connector
.
Scopes
,
r
*
http
.
Request
)
(
connector
.
Identity
,
error
)
{
var
groups
[]
string
if
s
.
Groups
{
groups
=
[]
string
{
"authors"
}
}
// HandleCallback parses the request and returns the user's identity
func
(
m
*
Callback
)
HandleCallback
(
s
connector
.
Scopes
,
r
*
http
.
Request
)
(
connector
.
Identity
,
error
)
{
return
m
.
Identity
,
nil
}
return
connector
.
Identity
{
UserID
:
"0-385-28089-0"
,
Username
:
"Kilgore Trout"
,
Email
:
"kilgore@kilgore.trout"
,
EmailVerified
:
true
,
Groups
:
groups
,
ConnectorData
:
connectorData
,
},
nil
// Refresh updates the identity during a refresh token request.
func
(
m
*
Callback
)
Refresh
(
ctx
context
.
Context
,
s
connector
.
Scopes
,
identity
connector
.
Identity
)
(
connector
.
Identity
,
error
)
{
return
m
.
Identity
,
nil
}
// CallbackConfig holds the configuration parameters for a connector which requires no interaction.
...
...
server/server_test.go
View file @
55e97d90
...
...
@@ -132,10 +132,13 @@ func TestDiscovery(t *testing.T) {
}
}
// TestOAuth2CodeFlow runs integration tests against a test server. The tests stand up a server
// which requires no interaction to login, logs in through a test client, then passes the client
// and returned token to the test.
func
TestOAuth2CodeFlow
(
t
*
testing
.
T
)
{
clientID
:=
"testclient"
clientSecret
:=
"testclientsecret"
requestedScopes
:=
[]
string
{
oidc
.
ScopeOpenID
,
"email"
,
"offline_access"
}
requestedScopes
:=
[]
string
{
oidc
.
ScopeOpenID
,
"email"
,
"
profile"
,
"groups"
,
"
offline_access"
}
t0
:=
time
.
Now
()
...
...
@@ -149,8 +152,14 @@ func TestOAuth2CodeFlow(t *testing.T) {
// so tests can compute the expected "expires_in" field.
idTokensValidFor
:=
time
.
Second
*
30
// Connector used by the tests.
var
conn
*
mock
.
Callback
tests
:=
[]
struct
{
name
string
name
string
// If specified these set of scopes will be used during the test case.
scopes
[]
string
// handleToken provides the OAuth2 token response for the integration test.
handleToken
func
(
context
.
Context
,
*
oidc
.
Provider
,
*
oauth2
.
Config
,
*
oauth2
.
Token
)
error
}{
{
...
...
@@ -265,7 +274,8 @@ func TestOAuth2CodeFlow(t *testing.T) {
},
},
{
name
:
"refresh with unauthorized scopes"
,
name
:
"refresh with unauthorized scopes"
,
scopes
:
[]
string
{
"openid"
,
"email"
},
handleToken
:
func
(
ctx
context
.
Context
,
p
*
oidc
.
Provider
,
config
*
oauth2
.
Config
,
token
*
oauth2
.
Token
)
error
{
v
:=
url
.
Values
{}
v
.
Add
(
"client_id"
,
clientID
)
...
...
@@ -273,7 +283,7 @@ func TestOAuth2CodeFlow(t *testing.T) {
v
.
Add
(
"grant_type"
,
"refresh_token"
)
v
.
Add
(
"refresh_token"
,
token
.
RefreshToken
)
// Request a scope that wasn't requestd initially.
v
.
Add
(
"scope"
,
strings
.
Join
(
append
(
requestedScopes
,
"profile"
),
" "
)
)
v
.
Add
(
"scope"
,
"oidc email profile"
)
resp
,
err
:=
http
.
PostForm
(
p
.
TokenURL
,
v
)
if
err
!=
nil
{
return
err
...
...
@@ -289,6 +299,57 @@ func TestOAuth2CodeFlow(t *testing.T) {
return
nil
},
},
{
// This test ensures that the connector.RefreshConnector interface is being
// used when clients request a refresh token.
name
:
"refresh with identity changes"
,
handleToken
:
func
(
ctx
context
.
Context
,
p
*
oidc
.
Provider
,
config
*
oauth2
.
Config
,
token
*
oauth2
.
Token
)
error
{
// have to use time.Now because the OAuth2 package uses it.
token
.
Expiry
=
time
.
Now
()
.
Add
(
time
.
Second
*
-
10
)
if
token
.
Valid
()
{
return
errors
.
New
(
"token shouldn't be valid"
)
}
ident
:=
connector
.
Identity
{
UserID
:
"fooid"
,
Username
:
"foo"
,
Email
:
"foo@bar.com"
,
EmailVerified
:
true
,
Groups
:
[]
string
{
"foo"
,
"bar"
},
}
conn
.
Identity
=
ident
type
claims
struct
{
Username
string
`json:"name"`
Email
string
`json:"email"`
EmailVerified
bool
`json:"email_verified"`
Groups
[]
string
`json:"groups"`
}
want
:=
claims
{
ident
.
Username
,
ident
.
Email
,
ident
.
EmailVerified
,
ident
.
Groups
}
newToken
,
err
:=
config
.
TokenSource
(
ctx
,
token
)
.
Token
()
if
err
!=
nil
{
return
fmt
.
Errorf
(
"failed to refresh token: %v"
,
err
)
}
rawIDToken
,
ok
:=
newToken
.
Extra
(
"id_token"
)
.
(
string
)
if
!
ok
{
return
fmt
.
Errorf
(
"no id_token in refreshed token"
)
}
idToken
,
err
:=
p
.
NewVerifier
(
ctx
)
.
Verify
(
rawIDToken
)
if
err
!=
nil
{
return
fmt
.
Errorf
(
"failed to verify id token: %v"
,
err
)
}
var
got
claims
if
err
:=
idToken
.
Claims
(
&
got
);
err
!=
nil
{
return
fmt
.
Errorf
(
"failed to unmarshal claims: %v"
,
err
)
}
if
diff
:=
pretty
.
Compare
(
want
,
got
);
diff
!=
""
{
return
fmt
.
Errorf
(
"got identity != want identity: %s"
,
diff
)
}
return
nil
},
},
}
for
_
,
tc
:=
range
tests
{
...
...
@@ -300,6 +361,15 @@ func TestOAuth2CodeFlow(t *testing.T) {
c
.
Issuer
=
c
.
Issuer
+
"/non-root-path"
c
.
Now
=
now
c
.
IDTokensValidFor
=
idTokensValidFor
// Create a new mock callback connector for each test case.
conn
=
mock
.
NewCallbackConnector
()
.
(
*
mock
.
Callback
)
c
.
Connectors
=
[]
Connector
{
{
ID
:
"mock"
,
DisplayName
:
"mock"
,
Connector
:
conn
,
},
}
})
defer
httpServer
.
Close
()
...
...
@@ -375,6 +445,9 @@ func TestOAuth2CodeFlow(t *testing.T) {
Scopes
:
requestedScopes
,
RedirectURL
:
redirectURL
,
}
if
len
(
tc
.
scopes
)
!=
0
{
oauth2Config
.
Scopes
=
tc
.
scopes
}
resp
,
err
:=
http
.
Get
(
oauth2Server
.
URL
+
"/login"
)
if
err
!=
nil
{
...
...
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