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
ce9ac761
Commit
ce9ac761
authored
Aug 18, 2017
by
Eric Stroczynski
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
connector/github: abstract scope check and group getter
parent
e92f38f3
Hide whitespace changes
Inline
Side-by-side
Showing
1 changed file
with
43 additions
and
41 deletions
+43
-41
github.go
connector/github/github.go
+43
-41
No files found.
connector/github/github.go
View file @
ce9ac761
...
@@ -24,9 +24,12 @@ import (
...
@@ -24,9 +24,12 @@ import (
)
)
const
(
const
(
apiURL
=
"https://api.github.com"
apiURL
=
"https://api.github.com"
// GitHub requires this scope to access '/user' and '/user/emails' API endpoints.
scopeEmail
=
"user:email"
scopeEmail
=
"user:email"
scopeOrgs
=
"read:org"
// GitHub requires this scope to access '/user/teams' and '/orgs' API endpoints
// which are used when a client includes the 'groups' scope.
scopeOrgs
=
"read:org"
)
)
// Pagination URL patterns
// Pagination URL patterns
...
@@ -133,11 +136,18 @@ type githubConnector struct {
...
@@ -133,11 +136,18 @@ type githubConnector struct {
httpClient
*
http
.
Client
httpClient
*
http
.
Client
}
}
// groupsRequired returns whether dex requires GitHub's 'read:org' scope. Dex
// needs 'read:org' if 'orgs' or 'org' fields are populated in a config file.
// Clients can require 'groups' scope without setting 'orgs'/'org'.
func
(
c
*
githubConnector
)
groupsRequired
(
groupScope
bool
)
bool
{
return
len
(
c
.
orgs
)
>
0
||
c
.
org
!=
""
||
groupScope
}
func
(
c
*
githubConnector
)
oauth2Config
(
scopes
connector
.
Scopes
)
*
oauth2
.
Config
{
func
(
c
*
githubConnector
)
oauth2Config
(
scopes
connector
.
Scopes
)
*
oauth2
.
Config
{
//
The GitHub API requires requires read:org scope to ensure users are members
//
'read:org' scope is required by the GitHub API, and thus for dex to ensure
// of orgs and teams provided in configs.
//
a user is a member
of orgs and teams provided in configs.
githubScopes
:=
[]
string
{
scopeEmail
}
githubScopes
:=
[]
string
{
scopeEmail
}
if
len
(
c
.
orgs
)
>
0
||
c
.
org
!=
""
{
if
c
.
groupsRequired
(
scopes
.
Groups
)
{
githubScopes
=
append
(
githubScopes
,
scopeOrgs
)
githubScopes
=
append
(
githubScopes
,
scopeOrgs
)
}
}
...
@@ -206,22 +216,6 @@ func newHTTPClient(rootCA string) (*http.Client, error) {
...
@@ -206,22 +216,6 @@ func newHTTPClient(rootCA string) (*http.Client, error) {
},
nil
},
nil
}
}
// getGroups retrieves GitHub orgs and teams a user is in, if any.
func
(
c
*
githubConnector
)
getGroups
(
ctx
context
.
Context
,
client
*
http
.
Client
,
groupScope
bool
,
userLogin
string
)
(
groups
[]
string
,
err
error
)
{
// Org and team membership should always be checked if specified by config.
if
len
(
c
.
orgs
)
>
0
{
if
groups
,
err
=
c
.
listGroups
(
ctx
,
client
,
userLogin
);
err
!=
nil
{
return
groups
,
err
}
}
else
if
groupScope
&&
c
.
org
!=
""
{
// Do not check org membership here to preserve legacy behavior.
if
groups
,
err
=
c
.
teams
(
ctx
,
client
,
c
.
org
);
err
!=
nil
{
return
groups
,
fmt
.
Errorf
(
"github: get teams: %v"
,
err
)
}
}
return
groups
,
nil
}
func
(
c
*
githubConnector
)
HandleCallback
(
s
connector
.
Scopes
,
r
*
http
.
Request
)
(
identity
connector
.
Identity
,
err
error
)
{
func
(
c
*
githubConnector
)
HandleCallback
(
s
connector
.
Scopes
,
r
*
http
.
Request
)
(
identity
connector
.
Identity
,
err
error
)
{
q
:=
r
.
URL
.
Query
()
q
:=
r
.
URL
.
Query
()
if
errType
:=
q
.
Get
(
"error"
);
errType
!=
""
{
if
errType
:=
q
.
Get
(
"error"
);
errType
!=
""
{
...
@@ -259,13 +253,12 @@ func (c *githubConnector) HandleCallback(s connector.Scopes, r *http.Request) (i
...
@@ -259,13 +253,12 @@ func (c *githubConnector) HandleCallback(s connector.Scopes, r *http.Request) (i
EmailVerified
:
true
,
EmailVerified
:
true
,
}
}
groups
,
err
:=
c
.
getGroups
(
ctx
,
client
,
s
.
Groups
,
user
.
Login
)
// Only set identity.Groups if 'orgs', 'org', or 'groups' scope are specified.
if
err
!=
nil
{
if
c
.
groupsRequired
(
s
.
Groups
)
{
return
identity
,
err
groups
,
err
:=
c
.
getGroups
(
ctx
,
client
,
s
.
Groups
,
user
.
Login
)
}
if
err
!=
nil
{
// Preserve behavior of only setting identity.Groups if 'orgs' or groups scope
return
identity
,
err
// and 'org' are specified.
}
if
len
(
c
.
orgs
)
>
0
||
(
s
.
Groups
&&
c
.
org
!=
""
)
{
identity
.
Groups
=
groups
identity
.
Groups
=
groups
}
}
...
@@ -304,26 +297,35 @@ func (c *githubConnector) Refresh(ctx context.Context, s connector.Scopes, ident
...
@@ -304,26 +297,35 @@ func (c *githubConnector) Refresh(ctx context.Context, s connector.Scopes, ident
identity
.
Username
=
username
identity
.
Username
=
username
identity
.
Email
=
user
.
Email
identity
.
Email
=
user
.
Email
groups
,
err
:=
c
.
getGroups
(
ctx
,
client
,
s
.
Groups
,
user
.
Login
)
// Only set identity.Groups if 'orgs', 'org', or 'groups' scope are specified.
if
err
!=
nil
{
if
c
.
groupsRequired
(
s
.
Groups
)
{
return
identity
,
err
groups
,
err
:=
c
.
getGroups
(
ctx
,
client
,
s
.
Groups
,
user
.
Login
)
}
if
err
!=
nil
{
// Preserve behavior of only setting identity.Groups if 'orgs' or groups scope
return
identity
,
err
// and 'org' are specified.
}
if
len
(
c
.
orgs
)
>
0
||
(
s
.
Groups
&&
c
.
org
!=
""
)
{
identity
.
Groups
=
groups
identity
.
Groups
=
groups
}
}
return
identity
,
nil
return
identity
,
nil
}
}
// listGroups enforces org and team constraints on user authorization
// getGroups retrieves GitHub orgs and teams a user is in, if any.
func
(
c
*
githubConnector
)
getGroups
(
ctx
context
.
Context
,
client
*
http
.
Client
,
groupScope
bool
,
userLogin
string
)
([]
string
,
error
)
{
if
len
(
c
.
orgs
)
>
0
{
return
c
.
groupsForOrgs
(
ctx
,
client
,
userLogin
)
}
else
if
c
.
org
!=
""
{
return
c
.
teamsForOrg
(
ctx
,
client
,
c
.
org
)
}
return
nil
,
nil
}
// groupsForOrgs enforces org and team constraints on user authorization
// Cases in which user is authorized:
// Cases in which user is authorized:
// N orgs, no teams: user is member of at least 1 org
// N orgs, no teams: user is member of at least 1 org
// N orgs, M teams per org: user is member of any team from at least 1 org
// N orgs, M teams per org: user is member of any team from at least 1 org
// N-1 orgs, M teams per org, 1 org with no teams: user is member of any team
// N-1 orgs, M teams per org, 1 org with no teams: user is member of any team
// from at least 1 org, or member of org with no teams
// from at least 1 org, or member of org with no teams
func
(
c
*
githubConnector
)
listGroup
s
(
ctx
context
.
Context
,
client
*
http
.
Client
,
userName
string
)
(
groups
[]
string
,
err
error
)
{
func
(
c
*
githubConnector
)
groupsForOrg
s
(
ctx
context
.
Context
,
client
*
http
.
Client
,
userName
string
)
(
groups
[]
string
,
err
error
)
{
var
inOrgNoTeams
bool
var
inOrgNoTeams
bool
for
_
,
org
:=
range
c
.
orgs
{
for
_
,
org
:=
range
c
.
orgs
{
inOrg
,
err
:=
c
.
userInOrg
(
ctx
,
client
,
userName
,
org
.
Name
)
inOrg
,
err
:=
c
.
userInOrg
(
ctx
,
client
,
userName
,
org
.
Name
)
...
@@ -334,7 +336,7 @@ func (c *githubConnector) listGroups(ctx context.Context, client *http.Client, u
...
@@ -334,7 +336,7 @@ func (c *githubConnector) listGroups(ctx context.Context, client *http.Client, u
continue
continue
}
}
teams
,
err
:=
c
.
teams
(
ctx
,
client
,
org
.
Name
)
teams
,
err
:=
c
.
teams
ForOrg
(
ctx
,
client
,
org
.
Name
)
if
err
!=
nil
{
if
err
!=
nil
{
return
groups
,
err
return
groups
,
err
}
}
...
@@ -551,11 +553,11 @@ type team struct {
...
@@ -551,11 +553,11 @@ type team struct {
}
`json:"organization"`
}
`json:"organization"`
}
}
// teams queries the GitHub API for team membership within a specific organization.
// teams
ForOrg
queries the GitHub API for team membership within a specific organization.
//
//
// The HTTP passed client is expected to be constructed by the golang.org/x/oauth2 package,
// The HTTP passed client is expected to be constructed by the golang.org/x/oauth2 package,
// which inserts a bearer token as part of the request.
// which inserts a bearer token as part of the request.
func
(
c
*
githubConnector
)
teams
(
ctx
context
.
Context
,
client
*
http
.
Client
,
orgName
string
)
([]
string
,
error
)
{
func
(
c
*
githubConnector
)
teams
ForOrg
(
ctx
context
.
Context
,
client
*
http
.
Client
,
orgName
string
)
([]
string
,
error
)
{
apiURL
,
groups
:=
c
.
apiURL
+
"/user/teams"
,
[]
string
{}
apiURL
,
groups
:=
c
.
apiURL
+
"/user/teams"
,
[]
string
{}
for
{
for
{
// https://developer.github.com/v3/orgs/teams/#list-user-teams
// https://developer.github.com/v3/orgs/teams/#list-user-teams
...
@@ -564,7 +566,7 @@ func (c *githubConnector) teams(ctx context.Context, client *http.Client, orgNam
...
@@ -564,7 +566,7 @@ func (c *githubConnector) teams(ctx context.Context, client *http.Client, orgNam
err
error
err
error
)
)
if
apiURL
,
err
=
get
(
ctx
,
client
,
apiURL
,
&
teams
);
err
!=
nil
{
if
apiURL
,
err
=
get
(
ctx
,
client
,
apiURL
,
&
teams
);
err
!=
nil
{
return
nil
,
err
return
nil
,
fmt
.
Errorf
(
"github: get teams: %v"
,
err
)
}
}
for
_
,
team
:=
range
teams
{
for
_
,
team
:=
range
teams
{
...
...
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