-
Notifications
You must be signed in to change notification settings - Fork 1.7k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
connector/github: multiple orgs, query by teams #1013
Conversation
connector/github/github.go
Outdated
@@ -34,16 +34,22 @@ type Config struct { | |||
ClientID string `json:"clientID"` | |||
ClientSecret string `json:"clientSecret"` | |||
RedirectURI string `json:"redirectURI"` | |||
Org string `json:"org"` |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
We should keep this field and behavior around to not break existing users.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
You've still removed the feature.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@estroz btw this behavior has changed. You'll start prefixing "(org):" to any group.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Adding legacy logic now.
connector/github/github.go
Outdated
HostName string `json:"hostName"` | ||
RootCA string `json:"rootCA"` | ||
} | ||
|
||
// Org holds org-team filters, in which teams are optional |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Need to explain the impact of providing the teams field. Also the logic about what teams or orgs a user has to belong to to login.
connector/github/github.go
Outdated
// 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-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 | ||
func (c *githubConnector) isUserAuthorized(ctx context.Context, client *http.Client, userName string) (groups []string, isAuthorized bool, err error) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Method name could probably be more specific. Maybe just listGroups
Also two orgs can have teams with the same name. We probably need to come up with some naming schema for the groups. e.g. ( org ):( team )
"coreos:engineers"
connector/github/github.go
Outdated
err = fmt.Errorf("unexpected return status (code %d): %q", resp.StatusCode, resp.Status) | ||
} | ||
|
||
// 204 if user is a member |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Ha. Fun API.
connector/github/github.go
Outdated
|
||
err = nil | ||
switch resp.StatusCode { | ||
case http.StatusNoContent, http.StatusFound, http.StatusNotFound: |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Probably want to map these to a message about what they represent https://developer.github.com/v3/orgs/members/#check-membership
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Status(Not)Found mean the same thing in our context (requester == user), so I'll return a 'user not in org' error
connector/github/github.go
Outdated
} | ||
defer resp.Body.Close() | ||
|
||
err = nil |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
err is already nil
connector/github/github.go
Outdated
isAuthorized = true | ||
groups = append(groups, teams...) | ||
c.logger.Debugf("github: user %q in org %q", userName, org.Name) | ||
continue |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Really confused by this continue
and the one following it. Not clear what you're trying to do here.
connector/github/github.go
Outdated
return userTeams, true, nil | ||
} | ||
|
||
wantedTeams := make(map[string]struct{}) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
wantedTeams
is probably a bad name here. Can probably just write this as a method
func filterTeams(whiteList, userTeams []string) []string
connector/github/github.go
Outdated
// 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. | ||
func (c *githubConnector) userInOrgTeams(ctx context.Context, client *http.Client, userName string, org Org) ([]string, bool, error) { | ||
if inOrg, err := c.userInOrg(ctx, client, userName, org.Name); err != nil || !inOrg { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Can this logic just be done in isUserAuthorized
?
e1e8a52
to
c950d82
Compare
connector/github/github.go
Outdated
} | ||
|
||
if teams, err = c.teams(ctx, client, org.Name); err != nil { | ||
c.logger.Errorf("teams: %s", err) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Just return this error immediately.
connector/github/github.go
Outdated
for _, org := range c.orgs { | ||
inOrg, err = c.userInOrg(ctx, client, userName, org.Name) | ||
if err != nil { | ||
c.logger.Errorf("user in org: %s", err) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Just return this error immediately.
connector/github/github.go
Outdated
// User is in at least in one org. No team restrictions are imposed from | ||
// config. We add all the teams in the organization the user is a member of | ||
// to 'groups' to preserve legacy behavior. | ||
if len(org.Teams) == 0 { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
if len(org.Teams) > 0 {
teams = filterTeams(teams, org.Teams)
}
groups = append(groups, joinOrgToTeams(org.Name, teams)...)
No need to have these two continue
statements.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
How about something like:
orgPrefix := org.Name + ":"
if len(org.Teams) > 0 {
for _, teamName := range filterTeams(teams, org.Teams) {
groups = append(groups, orgPrefix+teamName)
}
} else {
groups = append(groups, orgPrefix)
}
If teams are specified in the config file, then check if the user is a team member and add those teams to groups. Otherwise, use an org-level group "orgName:"
to indicate that the user is an org member.
connector/github/github.go
Outdated
|
||
// Orgs might have the same team names. We append the orgName to team name | ||
// delimited by a colon, "org:team", to make team names unique across orgs. | ||
func joinOrgToTeams(orgName string, teamNames []string) []string { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
doesn't need to be it's own method.
connector/github/github.go
Outdated
groups = append(groups, orgPrefix+teamName) | ||
c.logger.Debugf("github: user %q in org %q team %q", userName, org.Name, teamName) | ||
} | ||
} else { // No team restrictions. Append "org:" to groups |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I really don't like this group value. I'd rather people just evaluate teams, not orgs for auth-Z decisions.
connector/github/github.go
Outdated
@@ -34,16 +34,22 @@ type Config struct { | |||
ClientID string `json:"clientID"` | |||
ClientSecret string `json:"clientSecret"` | |||
RedirectURI string `json:"redirectURI"` | |||
Org string `json:"org"` |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@estroz btw this behavior has changed. You'll start prefixing "(org):" to any group.
054ed7b
to
2d48bc8
Compare
Documentation/github-connector.md
Outdated
teams: | ||
- read-team | ||
- blue-team | ||
- name: my-organization-no-teams |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Need to document why these two are different.
Documentation/github-connector.md
Outdated
# Optional organization to pull teams from, communicate through the | ||
# "groups" scope. | ||
# Optional organizations and teams, communicated through the | ||
# "groups" scope. The 'org' field is kept for legacy users. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Lots of "legacy" references. I'm starting to feel old :)
We can probably just drop these docs.
2d48bc8
to
670e23e
Compare
Documentation/github-connector.md
Outdated
# this field to restrict access to the specified GitHub org. | ||
# org: my-organization | ||
# | ||
# New 'orgs' field. 'orgs' supports organizations with or without teams. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Few nits.
- This field is 'new' in the release notes, for docs it just is.
- "restrict access" is probably the wrong phrase.
- Let's just remove the
NOTE: ...
line andLegacy 'org' field...
block.
Maybe something like this:
# Query the following organizations for group information if the "groups"
# scope is provided. Group claims are formatted as "(org):(team)". For
# example if a user is part of the "engineering" team of the "coreos" org,
# the group claim would include "coreos:engineering"
#
# A user MUST be a member of at least one of the following orgs to
# authenticate with dex.
orgs:
- name: my-org
# Include all teams as claims.
- name: my-org-with-teams
# A white list of teams. Only include group claims for these teams.
teams:
- read-team
- blue-team
46ed036
to
589aa35
Compare
@estroz lgtm. Have you tested this? |
@ericchiang yes, although I'm going through the process once more to be sure. |
connector/github/github.go
Outdated
// from at least 1 org, or member of org with no teams | ||
func (c *githubConnector) listGroups(ctx context.Context, client *http.Client, userName string) ([]string, bool, error) { | ||
var ( | ||
inOrg, inOrgNoTeams bool |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
inOrgNoTeams is never set. It's always false.
connector/github/github.go
Outdated
func (c *githubConnector) listGroups(ctx context.Context, client *http.Client, userName string) ([]string, bool, error) { | ||
var ( | ||
inOrg, inOrgNoTeams bool | ||
teams, groups []string |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
inOrg
, teams
, and err
do not need to be defined outside the for loop. Just define them as used in the for loop,
589aa35
to
cba6ba0
Compare
@ericchiang tested with both |
// 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 | ||
// from at least 1 org, or member of org with no teams | ||
func (c *githubConnector) listGroups(ctx context.Context, client *http.Client, userName string) (groups []string, err error) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
If a user isn't in any of the orgs, this will still return []string{}, nil
and the user will login successfully.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Good catch. Fixing now.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Fixed and tested.
Documentation: examples of GitHub `orgs` field with multiple orgs and org with teams; note legacy behavior
cba6ba0
to
9d15480
Compare
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
lgtm
connector/github: multiple orgs, query by teams
connector/github: support for multiple orgs and teams within orgs; allow filtering by both orgs and teams
Documentation: examples of GitHub
orgs
field with multiple orgs and org with teamsThe GitHub connector should give more fine-grained control over which organizations and teams have access to an application. Restricting authorization to (multiple)
teams
within (multiple)orgs
by specifying them in theconnector
config section gives dex users such control.Tests: set up organizations with/out teams and a test user; went through scenarios listed in this function description
Note: this PR obsoletes #1008 because similar errors are returned if user is not a org member