mirror of
https://github.com/oauth2-proxy/oauth2-proxy.git
synced 2025-04-27 12:32:10 +02:00
Support new option "github-user" (#421)
* feat(github): support new option "github-user" * feat(github): rename github-user to github-users * feat(github): update docs for github-users option * feat(github): remove unneeded code * feat(github): remove logging * feat(github-user): use github-user as flagset options * feat(github-user): remove optionns.go * feat(github-user): add github-user flagset * feat(github): improve readability in the docs * feat(github-user): refactored SetUsers method * Update flag description Co-authored-by: Joel Speed <Joel.speed@hotmail.co.uk>
This commit is contained in:
parent
a17c48810f
commit
d8d43bb51b
@ -99,6 +99,7 @@
|
|||||||
- [#494](https://github.com/oauth2-proxy/oauth2-proxy/pull/494) Upstream websockets TLS certificate validation now depends on ssl-upstream-insecure-skip-verify (@yaroslavros)
|
- [#494](https://github.com/oauth2-proxy/oauth2-proxy/pull/494) Upstream websockets TLS certificate validation now depends on ssl-upstream-insecure-skip-verify (@yaroslavros)
|
||||||
- [#497](https://github.com/oauth2-proxy/oauth2-proxy/pull/497) Restrict access using Github collaborators (@jsclayton)
|
- [#497](https://github.com/oauth2-proxy/oauth2-proxy/pull/497) Restrict access using Github collaborators (@jsclayton)
|
||||||
- [#414](https://github.com/oauth2-proxy/oauth2-proxy/pull/414) Always encrypt sessions regardless of config (@ti-mo)
|
- [#414](https://github.com/oauth2-proxy/oauth2-proxy/pull/414) Always encrypt sessions regardless of config (@ti-mo)
|
||||||
|
- [#421](https://github.com/oauth2-proxy/oauth2-proxy/pull/421) Allow logins by usernames even if they do not belong to the specified org and team or collaborators (@yyoshiki41)
|
||||||
|
|
||||||
# v5.1.1
|
# v5.1.1
|
||||||
|
|
||||||
|
@ -24,7 +24,7 @@ _oauth2_proxy() {
|
|||||||
COMPREPLY=( $(compgen -W 'X-Real-IP X-Forwarded-For X-ProxyUser-IP' -- ${cur}) )
|
COMPREPLY=( $(compgen -W 'X-Real-IP X-Forwarded-For X-ProxyUser-IP' -- ${cur}) )
|
||||||
return 0
|
return 0
|
||||||
;;
|
;;
|
||||||
--@(http-address|https-address|redirect-url|upstream|basic-auth-password|skip-auth-regex|flush-interval|extra-jwt-issuers|email-domain|whitelist-domain|keycloak-group|azure-tenant|bitbucket-team|bitbucket-repository|github-org|github-team|github-repo|github-token|gitlab-group|google-group|google-admin-email|google-service-account-json|client-id|client_secret|banner|footer|proxy-prefix|ping-path|cookie-name|cookie-secret|cookie-domain|cookie-path|cookie-expire|cookie-refresh|cookie-samesite|redist-sentinel-master-name|redist-sentinel-connection-urls|redist-cluster-connection-urls|logging-max-size|logging-max-age|logging-max-backups|standard-logging-format|request-logging-format|exclude-logging-paths|auth-logging-format|oidc-issuer-url|oidc-jwks-url|login-url|redeem-url|profile-url|resource|validate-url|scope|approval-prompt|signature-key|acr-values|jwt-key|pubjwk-url))
|
--@(http-address|https-address|redirect-url|upstream|basic-auth-password|skip-auth-regex|flush-interval|extra-jwt-issuers|email-domain|whitelist-domain|keycloak-group|azure-tenant|bitbucket-team|bitbucket-repository|github-org|github-team|github-repo|github-token|gitlab-group|github-user|google-group|google-admin-email|google-service-account-json|client-id|client_secret|banner|footer|proxy-prefix|ping-path|cookie-name|cookie-secret|cookie-domain|cookie-path|cookie-expire|cookie-refresh|cookie-samesite|redist-sentinel-master-name|redist-sentinel-connection-urls|redist-cluster-connection-urls|logging-max-size|logging-max-age|logging-max-backups|standard-logging-format|request-logging-format|exclude-logging-paths|auth-logging-format|oidc-issuer-url|oidc-jwks-url|login-url|redeem-url|profile-url|resource|validate-url|scope|approval-prompt|signature-key|acr-values|jwt-key|pubjwk-url))
|
||||||
return 0
|
return 0
|
||||||
;;
|
;;
|
||||||
esac
|
esac
|
||||||
|
@ -103,6 +103,8 @@ Note: When using the Azure Auth provider with nginx and the cookie session store
|
|||||||
|
|
||||||
The GitHub auth provider supports two additional ways to restrict authentication to either organization and optional team level access, or to collaborators of a repository. Restricting by these options is normally accompanied with `--email-domain=*`
|
The GitHub auth provider supports two additional ways to restrict authentication to either organization and optional team level access, or to collaborators of a repository. Restricting by these options is normally accompanied with `--email-domain=*`
|
||||||
|
|
||||||
|
NOTE: When `--github-user` is set, the specified users are allowed to login even if they do not belong to the specified org and team or collaborators.
|
||||||
|
|
||||||
To restrict by organization only, include the following flag:
|
To restrict by organization only, include the following flag:
|
||||||
|
|
||||||
-github-org="": restrict logins to members of this organisation
|
-github-org="": restrict logins to members of this organisation
|
||||||
@ -119,6 +121,10 @@ If you'd like to allow access to users with **read only** access to a **public**
|
|||||||
|
|
||||||
-github-token="": the token to use when verifying repository collaborators
|
-github-token="": the token to use when verifying repository collaborators
|
||||||
|
|
||||||
|
To allow a user to login with their username even if they do not belong to the specified org and team or collaborators, separated by a comma
|
||||||
|
|
||||||
|
-github-user="": allow logins by username, separated by a comma
|
||||||
|
|
||||||
If you are using GitHub enterprise, make sure you set the following to the appropriate url:
|
If you are using GitHub enterprise, make sure you set the following to the appropriate url:
|
||||||
|
|
||||||
-login-url="http(s)://<enterprise github host>/login/oauth/authorize"
|
-login-url="http(s)://<enterprise github host>/login/oauth/authorize"
|
||||||
|
@ -56,6 +56,7 @@ An example [oauth2-proxy.cfg]({{ site.gitweb }}/contrib/oauth2-proxy.cfg.example
|
|||||||
| `--github-team` | string | restrict logins to members of any of these teams (slug), separated by a comma | |
|
| `--github-team` | string | restrict logins to members of any of these teams (slug), separated by a comma | |
|
||||||
| `--github-repo` | string | restrict logins to collaborators of this repository formatted as `orgname/repo` | |
|
| `--github-repo` | string | restrict logins to collaborators of this repository formatted as `orgname/repo` | |
|
||||||
| `--github-token` | string | the token to use when verifying repository collaborators (must have push access to the repository) | |
|
| `--github-token` | string | the token to use when verifying repository collaborators (must have push access to the repository) | |
|
||||||
|
| `--github-user` | string \| list | To allow users to login by username even if they do not belong to the specified org and team or collaborators | |
|
||||||
| `--gitlab-group` | string | restrict logins to members of any of these groups (slug), separated by a comma | |
|
| `--gitlab-group` | string | restrict logins to members of any of these groups (slug), separated by a comma | |
|
||||||
| `--google-admin-email` | string | the google admin to impersonate for api calls | |
|
| `--google-admin-email` | string | the google admin to impersonate for api calls | |
|
||||||
| `--google-group` | string | restrict logins to members of this google group (may be given multiple times). | |
|
| `--google-group` | string | restrict logins to members of this google group (may be given multiple times). | |
|
||||||
|
@ -48,6 +48,7 @@ type Options struct {
|
|||||||
GitHubTeam string `flag:"github-team" cfg:"github_team"`
|
GitHubTeam string `flag:"github-team" cfg:"github_team"`
|
||||||
GitHubRepo string `flag:"github-repo" cfg:"github_repo"`
|
GitHubRepo string `flag:"github-repo" cfg:"github_repo"`
|
||||||
GitHubToken string `flag:"github-token" cfg:"github_token"`
|
GitHubToken string `flag:"github-token" cfg:"github_token"`
|
||||||
|
GitHubUsers []string `flag:"github-user" cfg:"github_users"`
|
||||||
GitLabGroup string `flag:"gitlab-group" cfg:"gitlab_group"`
|
GitLabGroup string `flag:"gitlab-group" cfg:"gitlab_group"`
|
||||||
GoogleGroups []string `flag:"google-group" cfg:"google_group"`
|
GoogleGroups []string `flag:"google-group" cfg:"google_group"`
|
||||||
GoogleAdminEmail string `flag:"google-admin-email" cfg:"google_admin_email"`
|
GoogleAdminEmail string `flag:"google-admin-email" cfg:"google_admin_email"`
|
||||||
@ -228,6 +229,7 @@ func NewFlagSet() *pflag.FlagSet {
|
|||||||
flagSet.String("github-team", "", "restrict logins to members of this team")
|
flagSet.String("github-team", "", "restrict logins to members of this team")
|
||||||
flagSet.String("github-repo", "", "restrict logins to collaborators of this repository")
|
flagSet.String("github-repo", "", "restrict logins to collaborators of this repository")
|
||||||
flagSet.String("github-token", "", "the token to use when verifying repository collaborators (must have push access to the repository)")
|
flagSet.String("github-token", "", "the token to use when verifying repository collaborators (must have push access to the repository)")
|
||||||
|
flagSet.StringSlice("github-user", []string{}, "allow users with these usernames to login even if they do not belong to the specified org and team or collaborators (may be given multiple times)")
|
||||||
flagSet.String("gitlab-group", "", "restrict logins to members of this group")
|
flagSet.String("gitlab-group", "", "restrict logins to members of this group")
|
||||||
flagSet.StringSlice("google-group", []string{}, "restrict logins to members of this google group (may be given multiple times).")
|
flagSet.StringSlice("google-group", []string{}, "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-admin-email", "", "the google admin to impersonate for api calls")
|
||||||
|
@ -309,6 +309,7 @@ func parseProviderInfo(o *options.Options, msgs []string) []string {
|
|||||||
case *providers.GitHubProvider:
|
case *providers.GitHubProvider:
|
||||||
p.SetOrgTeam(o.GitHubOrg, o.GitHubTeam)
|
p.SetOrgTeam(o.GitHubOrg, o.GitHubTeam)
|
||||||
p.SetRepo(o.GitHubRepo, o.GitHubToken)
|
p.SetRepo(o.GitHubRepo, o.GitHubToken)
|
||||||
|
p.SetUsers(o.GitHubUsers)
|
||||||
case *providers.KeycloakProvider:
|
case *providers.KeycloakProvider:
|
||||||
p.SetGroup(o.KeycloakGroup)
|
p.SetGroup(o.KeycloakGroup)
|
||||||
case *providers.GoogleProvider:
|
case *providers.GoogleProvider:
|
||||||
|
@ -3,6 +3,7 @@ package providers
|
|||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
"encoding/json"
|
"encoding/json"
|
||||||
|
"errors"
|
||||||
"fmt"
|
"fmt"
|
||||||
"io/ioutil"
|
"io/ioutil"
|
||||||
"net/http"
|
"net/http"
|
||||||
@ -23,6 +24,7 @@ type GitHubProvider struct {
|
|||||||
Team string
|
Team string
|
||||||
Repo string
|
Repo string
|
||||||
Token string
|
Token string
|
||||||
|
Users []string
|
||||||
}
|
}
|
||||||
|
|
||||||
var _ Provider = (*GitHubProvider)(nil)
|
var _ Provider = (*GitHubProvider)(nil)
|
||||||
@ -80,6 +82,11 @@ func (p *GitHubProvider) SetRepo(repo, token string) {
|
|||||||
p.Token = token
|
p.Token = token
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// SetUsers configures allowed usernames
|
||||||
|
func (p *GitHubProvider) SetUsers(users []string) {
|
||||||
|
p.Users = users
|
||||||
|
}
|
||||||
|
|
||||||
func (p *GitHubProvider) hasOrg(ctx context.Context, accessToken string) (bool, error) {
|
func (p *GitHubProvider) hasOrg(ctx context.Context, accessToken string) (bool, error) {
|
||||||
// https://developer.github.com/v3/orgs/#list-your-organizations
|
// https://developer.github.com/v3/orgs/#list-your-organizations
|
||||||
|
|
||||||
@ -317,6 +324,46 @@ func (p *GitHubProvider) hasRepo(ctx context.Context, accessToken string) (bool,
|
|||||||
return repo.Permissions.Push || (repo.Private && repo.Permissions.Pull), nil
|
return repo.Permissions.Push || (repo.Private && repo.Permissions.Pull), nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (p *GitHubProvider) hasUser(ctx context.Context, accessToken string) (bool, error) {
|
||||||
|
// https://developer.github.com/v3/users/#get-the-authenticated-user
|
||||||
|
|
||||||
|
var user struct {
|
||||||
|
Login string `json:"login"`
|
||||||
|
Email string `json:"email"`
|
||||||
|
}
|
||||||
|
|
||||||
|
endpoint := &url.URL{
|
||||||
|
Scheme: p.ValidateURL.Scheme,
|
||||||
|
Host: p.ValidateURL.Host,
|
||||||
|
Path: path.Join(p.ValidateURL.Path, "/user"),
|
||||||
|
}
|
||||||
|
req, _ := http.NewRequestWithContext(ctx, "GET", endpoint.String(), nil)
|
||||||
|
req.Header = getGitHubHeader(accessToken)
|
||||||
|
resp, err := http.DefaultClient.Do(req)
|
||||||
|
if err != nil {
|
||||||
|
return false, err
|
||||||
|
}
|
||||||
|
defer resp.Body.Close()
|
||||||
|
|
||||||
|
body, err := ioutil.ReadAll(resp.Body)
|
||||||
|
if err != nil {
|
||||||
|
return false, err
|
||||||
|
}
|
||||||
|
if resp.StatusCode != 200 {
|
||||||
|
return false, fmt.Errorf("got %d from %q %s",
|
||||||
|
resp.StatusCode, stripToken(endpoint.String()), body)
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := json.Unmarshal(body, &user); err != nil {
|
||||||
|
return false, err
|
||||||
|
}
|
||||||
|
|
||||||
|
if p.isVerifiedUser(user.Login) {
|
||||||
|
return true, nil
|
||||||
|
}
|
||||||
|
return false, nil
|
||||||
|
}
|
||||||
|
|
||||||
func (p *GitHubProvider) isCollaborator(ctx context.Context, username, accessToken string) (bool, error) {
|
func (p *GitHubProvider) isCollaborator(ctx context.Context, username, accessToken string) (bool, error) {
|
||||||
//https://developer.github.com/v3/repos/collaborators/#check-if-a-user-is-a-collaborator
|
//https://developer.github.com/v3/repos/collaborators/#check-if-a-user-is-a-collaborator
|
||||||
|
|
||||||
@ -356,7 +403,21 @@ func (p *GitHubProvider) GetEmailAddress(ctx context.Context, s *sessions.Sessio
|
|||||||
Verified bool `json:"verified"`
|
Verified bool `json:"verified"`
|
||||||
}
|
}
|
||||||
|
|
||||||
// if we require an Org or Team, check that first
|
// If usernames are set, check that first
|
||||||
|
verifiedUser := false
|
||||||
|
if len(p.Users) > 0 {
|
||||||
|
var err error
|
||||||
|
verifiedUser, err = p.hasUser(ctx, s.AccessToken)
|
||||||
|
if err != nil {
|
||||||
|
return "", err
|
||||||
|
}
|
||||||
|
// org and repository options are not configured
|
||||||
|
if !verifiedUser && p.Org == "" && p.Repo == "" {
|
||||||
|
return "", errors.New("missing github user")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// If a user is verified by username options, skip the following restrictions
|
||||||
|
if !verifiedUser {
|
||||||
if p.Org != "" {
|
if p.Org != "" {
|
||||||
if p.Team != "" {
|
if p.Team != "" {
|
||||||
if ok, err := p.hasOrgAndTeam(ctx, s.AccessToken); err != nil || !ok {
|
if ok, err := p.hasOrgAndTeam(ctx, s.AccessToken); err != nil || !ok {
|
||||||
@ -372,6 +433,7 @@ func (p *GitHubProvider) GetEmailAddress(ctx context.Context, s *sessions.Sessio
|
|||||||
return "", err
|
return "", err
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
endpoint := &url.URL{
|
endpoint := &url.URL{
|
||||||
Scheme: p.ValidateURL.Scheme,
|
Scheme: p.ValidateURL.Scheme,
|
||||||
@ -456,7 +518,7 @@ func (p *GitHubProvider) GetUserName(ctx context.Context, s *sessions.SessionSta
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Now that we have the username we can check collaborator status
|
// Now that we have the username we can check collaborator status
|
||||||
if p.Org == "" && p.Repo != "" && p.Token != "" {
|
if !p.isVerifiedUser(user.Login) && p.Org == "" && p.Repo != "" && p.Token != "" {
|
||||||
if ok, err := p.isCollaborator(ctx, user.Login, p.Token); err != nil || !ok {
|
if ok, err := p.isCollaborator(ctx, user.Login, p.Token); err != nil || !ok {
|
||||||
return "", err
|
return "", err
|
||||||
}
|
}
|
||||||
@ -469,3 +531,13 @@ func (p *GitHubProvider) GetUserName(ctx context.Context, s *sessions.SessionSta
|
|||||||
func (p *GitHubProvider) ValidateSessionState(ctx context.Context, s *sessions.SessionState) bool {
|
func (p *GitHubProvider) ValidateSessionState(ctx context.Context, s *sessions.SessionState) bool {
|
||||||
return validateToken(ctx, p, s.AccessToken, getGitHubHeader(s.AccessToken))
|
return validateToken(ctx, p, s.AccessToken, getGitHubHeader(s.AccessToken))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// isVerifiedUser
|
||||||
|
func (p *GitHubProvider) isVerifiedUser(username string) bool {
|
||||||
|
for _, u := range p.Users {
|
||||||
|
if username == u {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
@ -318,3 +318,78 @@ func TestGitHubProviderGetUserNameWithRepoAndTokenWithoutPushAccess(t *testing.T
|
|||||||
assert.NotEqual(t, nil, err)
|
assert.NotEqual(t, nil, err)
|
||||||
assert.Equal(t, "", email)
|
assert.Equal(t, "", email)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestGitHubProviderGetEmailAddressWithUsername(t *testing.T) {
|
||||||
|
b := testGitHubBackend(map[string][]string{
|
||||||
|
"/user": {`{"email": "michael.bland@gsa.gov", "login": "mbland"}`},
|
||||||
|
"/user/emails": {`[ {"email": "michael.bland@gsa.gov", "verified": true, "primary": true} ]`},
|
||||||
|
})
|
||||||
|
defer b.Close()
|
||||||
|
|
||||||
|
bURL, _ := url.Parse(b.URL)
|
||||||
|
p := testGitHubProvider(bURL.Host)
|
||||||
|
p.SetUsers([]string{"mbland", "octocat"})
|
||||||
|
|
||||||
|
session := CreateAuthorizedSession()
|
||||||
|
email, err := p.GetEmailAddress(context.Background(), session)
|
||||||
|
assert.Equal(t, nil, err)
|
||||||
|
assert.Equal(t, "michael.bland@gsa.gov", email)
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestGitHubProviderGetEmailAddressWithNotAllowedUsername(t *testing.T) {
|
||||||
|
b := testGitHubBackend(map[string][]string{
|
||||||
|
"/user": {`{"email": "michael.bland@gsa.gov", "login": "mbland"}`},
|
||||||
|
"/user/emails": {`[ {"email": "michael.bland@gsa.gov", "verified": true, "primary": true} ]`},
|
||||||
|
})
|
||||||
|
defer b.Close()
|
||||||
|
|
||||||
|
bURL, _ := url.Parse(b.URL)
|
||||||
|
p := testGitHubProvider(bURL.Host)
|
||||||
|
p.SetUsers([]string{"octocat"})
|
||||||
|
|
||||||
|
session := CreateAuthorizedSession()
|
||||||
|
email, err := p.GetEmailAddress(context.Background(), session)
|
||||||
|
assert.NotEqual(t, nil, err)
|
||||||
|
assert.Equal(t, "", email)
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestGitHubProviderGetEmailAddressWithUsernameAndNotBelongToOrg(t *testing.T) {
|
||||||
|
b := testGitHubBackend(map[string][]string{
|
||||||
|
"/user": {`{"email": "michael.bland@gsa.gov", "login": "mbland"}`},
|
||||||
|
"/user/emails": {`[ {"email": "michael.bland@gsa.gov", "verified": true, "primary": true} ]`},
|
||||||
|
"/user/orgs": {
|
||||||
|
`[ {"login":"testorg"} ]`,
|
||||||
|
`[ ]`,
|
||||||
|
},
|
||||||
|
})
|
||||||
|
defer b.Close()
|
||||||
|
|
||||||
|
bURL, _ := url.Parse(b.URL)
|
||||||
|
p := testGitHubProvider(bURL.Host)
|
||||||
|
p.SetOrgTeam("not_belong_to", "")
|
||||||
|
p.SetUsers([]string{"mbland"})
|
||||||
|
|
||||||
|
session := CreateAuthorizedSession()
|
||||||
|
email, err := p.GetEmailAddress(context.Background(), session)
|
||||||
|
assert.Equal(t, nil, err)
|
||||||
|
assert.Equal(t, "michael.bland@gsa.gov", email)
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestGitHubProviderGetEmailAddressWithUsernameAndNoAccessToPrivateRepo(t *testing.T) {
|
||||||
|
b := testGitHubBackend(map[string][]string{
|
||||||
|
"/user": {`{"email": "michael.bland@gsa.gov", "login": "mbland"}`},
|
||||||
|
"/user/emails": {`[ {"email": "michael.bland@gsa.gov", "verified": true, "primary": true} ]`},
|
||||||
|
"/repo/oauth2-proxy/oauth2-proxy": {},
|
||||||
|
})
|
||||||
|
defer b.Close()
|
||||||
|
|
||||||
|
bURL, _ := url.Parse(b.URL)
|
||||||
|
p := testGitHubProvider(bURL.Host)
|
||||||
|
p.SetRepo("oauth2-proxy/oauth2-proxy", "")
|
||||||
|
p.SetUsers([]string{"mbland"})
|
||||||
|
|
||||||
|
session := CreateAuthorizedSession()
|
||||||
|
email, err := p.GetEmailAddress(context.Background(), session)
|
||||||
|
assert.Equal(t, nil, err)
|
||||||
|
assert.Equal(t, "michael.bland@gsa.gov", email)
|
||||||
|
}
|
||||||
|
Loading…
x
Reference in New Issue
Block a user