diff --git a/README.md b/README.md index 2e6e4c964..07d2f0a96 100644 --- a/README.md +++ b/README.md @@ -113,6 +113,10 @@ If you are using GitHub enterprise, make sure you set the following to the appro Whether you are using GitLab.com or self-hosting GitLab, follow [these steps to add an application](http://doc.gitlab.com/ce/integration/oauth_provider.html) +The GitLab auth provider supports one additional parameter to restrict authentication to a specific group. Restricting by group is normally accompanied with `--email-domain=*` + + -gitlab-group="": restrict logins to members of this group + If you are using self-hosted GitLab, make sure you set the following to the appropriate URL: -login-url="/oauth/authorize" diff --git a/main.go b/main.go index ba3366876..c017cef16 100644 --- a/main.go +++ b/main.go @@ -42,6 +42,7 @@ func main() { flagSet.String("azure-tenant", "common", "go to a tenant-specific or common (tenant-independent) endpoint.") flagSet.String("github-org", "", "restrict logins to members of this organisation") flagSet.String("github-team", "", "restrict logins to members of this team") + flagSet.String("gitlab-group", "", "restrict logins to members of this group") flagSet.Var(&googleGroups, "google-group", "restrict logins to members of this google group (may be given multiple times).") flagSet.String("google-admin-email", "", "the google admin to impersonate for api calls") flagSet.String("google-service-account-json", "", "the path to the service account json credentials") diff --git a/options.go b/options.go index 4777d9d1e..03f66c3a1 100644 --- a/options.go +++ b/options.go @@ -31,6 +31,7 @@ type Options struct { EmailDomains []string `flag:"email-domain" cfg:"email_domains"` GitHubOrg string `flag:"github-org" cfg:"github_org"` GitHubTeam string `flag:"github-team" cfg:"github_team"` + GitLabGroup string `flag:"gitlab-group" cfg:"gitlab_group"` GoogleGroups []string `flag:"google-group" cfg:"google_group"` GoogleAdminEmail string `flag:"google-admin-email" cfg:"google_admin_email"` GoogleServiceAccountJSON string `flag:"google-service-account-json" cfg:"google_service_account_json"` @@ -229,6 +230,8 @@ func parseProviderInfo(o *Options, msgs []string) []string { p.Configure(o.AzureTenant) case *providers.GitHubProvider: p.SetOrgTeam(o.GitHubOrg, o.GitHubTeam) + case *providers.GitLabProvider: + p.SetGroup(o.GitLabGroup) case *providers.GoogleProvider: if o.GoogleServiceAccountJSON != "" { file, err := os.Open(o.GoogleServiceAccountJSON) diff --git a/providers/gitlab.go b/providers/gitlab.go index 708283add..627ae8d70 100644 --- a/providers/gitlab.go +++ b/providers/gitlab.go @@ -5,11 +5,16 @@ import ( "net/http" "net/url" + "encoding/json" + "fmt" "github.com/bitly/oauth2_proxy/api" + "io/ioutil" + "strconv" ) type GitLabProvider struct { *ProviderData + Group string } func NewGitLabProvider(p *ProviderData) *GitLabProvider { @@ -41,8 +46,70 @@ func NewGitLabProvider(p *ProviderData) *GitLabProvider { return &GitLabProvider{ProviderData: p} } +func (p *GitLabProvider) SetGroup(group string) { + p.Group = group +} + +func (p *GitLabProvider) hasGroup(accessToken string) (bool, error) { + + var groups []struct { + Group string `json:"name"` + } + + endpoint := p.ValidateURL.Scheme + "://" + p.ValidateURL.Host + "/api/v3/groups" + for page := 1; page != 0; { + req, _ := http.NewRequest("GET", endpoint, nil) + query := req.URL.Query() + query.Add("access_token", accessToken) + query.Add("page", strconv.Itoa(page)) + req.URL.RawQuery = query.Encode() + resp, err := http.DefaultClient.Do(req) + if err != nil { + return false, err + } + + next := resp.Header["X-Next-Page"] + if len(next) == 1 { + page, _ = strconv.Atoi(next[0]) + } else { + // Last iteration + page = 0 + } + + body, err := ioutil.ReadAll(resp.Body) + resp.Body.Close() + if err != nil { + return false, err + } + if resp.StatusCode != 200 { + return false, fmt.Errorf("got %d from %q %s", resp.StatusCode, endpoint, body) + } + + if err := json.Unmarshal(body, &groups); err != nil { + return false, err + } + + for _, group := range groups { + if p.Group == group.Group { + // Found the group + return true, nil + } + } + } + + log.Printf("Group %s not found in %s", p.Group, groups) + return false, nil +} + func (p *GitLabProvider) GetEmailAddress(s *SessionState) (string, error) { + // if we require a group, check that first + if p.Group != "" { + if ok, err := p.hasGroup(s.AccessToken); err != nil || !ok { + return "", err + } + } + req, err := http.NewRequest("GET", p.ValidateURL.String()+"?access_token="+s.AccessToken, nil) if err != nil {