From 8b61559b8d293d67df68ace0d892ea5f8ced0682 Mon Sep 17 00:00:00 2001 From: Henry Jenkins Date: Sun, 11 Aug 2019 16:07:03 +0100 Subject: [PATCH 1/6] Fix links in docs - Fixed a bunch of references to the repo, which were 404ing - Fixed a couple of things that 301/302ed - Fixed some in page references --- docs/0_index.md | 4 ++-- docs/2_auth.md | 8 ++++---- docs/6_request_signatures.md | 2 +- docs/_config.yml | 1 + docs/configuration/configuration.md | 2 +- docs/configuration/sessions.md | 4 ++-- 6 files changed, 11 insertions(+), 10 deletions(-) diff --git a/docs/0_index.md b/docs/0_index.md index 30c3555..376bd75 100644 --- a/docs/0_index.md +++ b/docs/0_index.md @@ -12,7 +12,7 @@ to validate accounts by email, domain or group. **Note:** This repository was forked from [bitly/OAuth2_Proxy](https://github.com/bitly/oauth2_proxy) on 27/11/2018. Versions v3.0.0 and up are from this fork and will have diverged from any changes in the original fork. -A list of changes can be seen in the [CHANGELOG](CHANGELOG.md). +A list of changes can be seen in the [CHANGELOG]({{ site.gitweb }}/CHANGELOG.md). [![Build Status](https://secure.travis-ci.org/pusher/oauth2_proxy.svg?branch=master)](http://travis-ci.org/pusher/oauth2_proxy) @@ -20,4 +20,4 @@ A list of changes can be seen in the [CHANGELOG](CHANGELOG.md). ## Architecture -![OAuth2 Proxy Architecture](https://cloud.githubusercontent.com/assets/45028/8027702/bd040b7a-0d6a-11e5-85b9-f8d953d04f39.png) \ No newline at end of file +![OAuth2 Proxy Architecture](https://cloud.githubusercontent.com/assets/45028/8027702/bd040b7a-0d6a-11e5-85b9-f8d953d04f39.png) diff --git a/docs/2_auth.md b/docs/2_auth.md index eba5f0c..e6c5cc6 100644 --- a/docs/2_auth.md +++ b/docs/2_auth.md @@ -103,7 +103,7 @@ If you are using GitHub enterprise, make sure you set the following to the appro ### GitLab Auth Provider -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). Make sure to enable at least the `openid`, `profile` and `email` scopes. +Whether you are using GitLab.com or self-hosting GitLab, follow [these steps to add an application](https://docs.gitlab.com/ce/integration/oauth_provider.html). Make sure to enable at least the `openid`, `profile` and `email` scopes. Restricting by group membership is possible with the following option: @@ -126,7 +126,7 @@ For LinkedIn, the registration steps are: ### Microsoft Azure AD Provider -For adding an application to the Microsoft Azure AD follow [these steps to add an application](https://azure.microsoft.com/en-us/documentation/articles/active-directory-integrating-applications/). +For adding an application to the Microsoft Azure AD follow [these steps to add an application](https://docs.microsoft.com/en-us/azure/active-directory/develop/quickstart-register-app). Take note of your `TenantId` if applicable for your situation. The `TenantId` can be used to override the default `common` authorization server with a tenant specific server. @@ -277,7 +277,7 @@ To authorize by email domain use `--email-domain=yourcompany.com`. To authorize ## Adding a new Provider -Follow the examples in the [`providers` package](providers/) to define a new +Follow the examples in the [`providers` package]({{ site.gitweb }}/providers/) to define a new `Provider` instance. Add a new `case` to -[`providers.New()`](providers/providers.go) to allow `oauth2_proxy` to use the +[`providers.New()`]({{ site.gitweb }}/providers/providers.go) to allow `oauth2_proxy` to use the new `Provider`. diff --git a/docs/6_request_signatures.md b/docs/6_request_signatures.md index 9feb961..0aa60aa 100644 --- a/docs/6_request_signatures.md +++ b/docs/6_request_signatures.md @@ -11,7 +11,7 @@ If `signature_key` is defined, proxied requests will be signed with the `GAP-Signature` header, which is a [Hash-based Message Authentication Code (HMAC)](https://en.wikipedia.org/wiki/Hash-based_message_authentication_code) of selected request information and the request body [see `SIGNATURE_HEADERS` -in `oauthproxy.go`](./oauthproxy.go). +in `oauthproxy.go`]({{ site.gitweb }}/oauthproxy.go). `signature_key` must be of the form `algorithm:secretkey`, (ie: `signature_key = "sha1:secret0"`) diff --git a/docs/_config.yml b/docs/_config.yml index dcc24a1..87f026c 100644 --- a/docs/_config.yml +++ b/docs/_config.yml @@ -18,6 +18,7 @@ description: >- # this means to ignore newlines until "baseurl:" OAuth2_Proxy documentation site baseurl: "/oauth2_proxy" # the subpath of your site, e.g. /blog url: "https://pusher.github.io" # the base hostname & protocol for your site, e.g. http://example.com +gitweb: "https://github.com/pusher/oauth2_proxy/blob/master" # Build settings markdown: kramdown diff --git a/docs/configuration/configuration.md b/docs/configuration/configuration.md index ea677d5..ef2680f 100644 --- a/docs/configuration/configuration.md +++ b/docs/configuration/configuration.md @@ -14,7 +14,7 @@ To generate a strong cookie secret use `python -c 'import os,base64; print base6 ### Config File -An example [oauth2_proxy.cfg](https://github.com/pusher/oauth2_proxy/blob/master/contrib/oauth2_proxy.cfg.example) config file is in the contrib directory. It can be used by specifying `-config=/etc/oauth2_proxy.cfg` +An example [oauth2_proxy.cfg]({{ site.gitweb }}/contrib/oauth2_proxy.cfg.example) config file is in the contrib directory. It can be used by specifying `-config=/etc/oauth2_proxy.cfg` ### Command Line Options diff --git a/docs/configuration/sessions.md b/docs/configuration/sessions.md index 0ffe392..4cfb09d 100644 --- a/docs/configuration/sessions.md +++ b/docs/configuration/sessions.md @@ -15,8 +15,8 @@ The OAuth2 Proxy uses a Cookie to track user sessions and will store the session data in one of the available session storage backends. At present the available backends are (as passed to `--session-store-type`): -- [cookie](cookie-storage) (default) -- [redis](redis-storage) +- [cookie](#cookie-storage) (default) +- [redis](#redis-storage) ### Cookie Storage From d5d4878a2934355f944eaf131d9fd8d12aba69b9 Mon Sep 17 00:00:00 2001 From: Adam Eijdenberg Date: Thu, 20 Jun 2019 14:17:15 +1000 Subject: [PATCH 2/6] Made setting of proxied headers deterministic based on configuration alone Previously some headers that are normally set by the proxy (and may be replied upstream for authorization decisiions) were not being set depending on values in the users sesssion. This change ensure that if a given header is sometimes set, it will always be either set or removed. It might be worth considerating always deleting these headers if we didn't add them. --- CHANGELOG.md | 1 + oauthproxy.go | 44 ++++++++++++++++++++++++++++++++++++-------- 2 files changed, 37 insertions(+), 8 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 824e37c..fec05c6 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -39,6 +39,7 @@ - [#234](https://github.com/pusher/oauth2_proxy/pull/234) Added option `-ssl-upstream-insecure-skip-validation` to skip validation of upstream SSL certificates (@jansinger) - [#224](https://github.com/pusher/oauth2_proxy/pull/224) Check Google group membership using hasMember to support nested groups and external users (@jpalpant) - [#231](https://github.com/pusher/oauth2_proxy/pull/231) Add optional group membership and email domain checks to the GitLab provider (@Overv) +- [#226](https://github.com/pusher/oauth2_proxy/pull/226) Made setting of proxied headers deterministic based on configuration alone (@aeijdenberg) - [#178](https://github.com/pusher/oauth2_proxy/pull/178) Add Silence Ping Logging and Exclude Logging Paths flags (@kskewes) - [#209](https://github.com/pusher/oauth2_proxy/pull/209) Improve docker build caching of layers (@dekimsey) - [#186](https://github.com/pusher/oauth2_proxy/pull/186) Make config consistent (@JoelSpeed) diff --git a/oauthproxy.go b/oauthproxy.go index f3d9fd9..2418e73 100644 --- a/oauthproxy.go +++ b/oauthproxy.go @@ -820,32 +820,60 @@ func (p *OAuthProxy) addHeadersForProxying(rw http.ResponseWriter, req *http.Req req.Header["X-Forwarded-User"] = []string{session.User} if session.Email != "" { req.Header["X-Forwarded-Email"] = []string{session.Email} + } else { + req.Header.Del("X-Forwarded-Email") } } + if p.PassUserHeaders { req.Header["X-Forwarded-User"] = []string{session.User} if session.Email != "" { req.Header["X-Forwarded-Email"] = []string{session.Email} + } else { + req.Header.Del("X-Forwarded-Email") } } + if p.SetXAuthRequest { rw.Header().Set("X-Auth-Request-User", session.User) if session.Email != "" { rw.Header().Set("X-Auth-Request-Email", session.Email) + } else { + rw.Header().Del("X-Auth-Request-Email") } - if p.PassAccessToken && session.AccessToken != "" { - rw.Header().Set("X-Auth-Request-Access-Token", session.AccessToken) + + if p.PassAccessToken { + if session.AccessToken != "" { + rw.Header().Set("X-Auth-Request-Access-Token", session.AccessToken) + } else { + rw.Header().Del("X-Auth-Request-Access-Token") + } } } - if p.PassAccessToken && session.AccessToken != "" { - req.Header["X-Forwarded-Access-Token"] = []string{session.AccessToken} + + if p.PassAccessToken { + if session.AccessToken != "" { + req.Header["X-Forwarded-Access-Token"] = []string{session.AccessToken} + } else { + req.Header.Del("X-Forwarded-Access-Token") + } } - if p.PassAuthorization && session.IDToken != "" { - req.Header["Authorization"] = []string{fmt.Sprintf("Bearer %s", session.IDToken)} + + if p.PassAuthorization { + if session.IDToken != "" { + req.Header["Authorization"] = []string{fmt.Sprintf("Bearer %s", session.IDToken)} + } else { + req.Header.Del("Authorization") + } } - if p.SetAuthorization && session.IDToken != "" { - rw.Header().Set("Authorization", fmt.Sprintf("Bearer %s", session.IDToken)) + if p.SetAuthorization { + if session.IDToken != "" { + rw.Header().Set("Authorization", fmt.Sprintf("Bearer %s", session.IDToken)) + } else { + rw.Header().Del("Authorization") + } } + if session.Email == "" { rw.Header().Set("GAP-Auth", session.User) } else { From fa6c4792a1e3f3b44950d6498bac7974b45380f1 Mon Sep 17 00:00:00 2001 From: aledeganopix4d <40891147+aledeganopix4d@users.noreply.github.com> Date: Fri, 16 Aug 2019 15:53:22 +0200 Subject: [PATCH 3/6] Add Bitbucket provider. (#201) Add a new provider for Bitbucket, can be configured from the options specifying team and/or repository that the user must be part/have access to in order to grant login. --- .github/CODEOWNERS | 4 + CHANGELOG.md | 4 + main.go | 2 + options.go | 5 ++ providers/bitbucket.go | 163 ++++++++++++++++++++++++++++++++++ providers/bitbucket_test.go | 170 ++++++++++++++++++++++++++++++++++++ providers/providers.go | 2 + 7 files changed, 350 insertions(+) create mode 100644 providers/bitbucket.go create mode 100644 providers/bitbucket_test.go diff --git a/.github/CODEOWNERS b/.github/CODEOWNERS index a6f7701..c7f2960 100644 --- a/.github/CODEOWNERS +++ b/.github/CODEOWNERS @@ -10,3 +10,7 @@ # or the public devops channel at https://chat.18f.gov/). providers/logingov.go @timothy-spencer providers/logingov_test.go @timothy-spencer + +# Bitbucket provider +providers/bitbucket.go @aledeganopix4d +providers/bitbucket_test.go @aledeganopix4d diff --git a/CHANGELOG.md b/CHANGELOG.md index fec05c6..526ecdf 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -96,6 +96,10 @@ - [#198](https://github.com/pusher/oauth2_proxy/pull/198) Switch from gometalinter to golangci-lint (@steakunderscore) - [#159](https://github.com/pusher/oauth2_proxy/pull/159) Add option to skip the OIDC provider verified email check: `--insecure-oidc-allow-unverified-email` - [#210](https://github.com/pusher/oauth2_proxy/pull/210) Update base image from Alpine 3.9 to 3.10 (@steakunderscore) +- [#201](https://github.com/pusher/oauth2_proxy/pull/201) Add Bitbucket as new OAuth2 provider, accepts email, team and repository permissions to determine authorization (@aledeganopix4d) + - Implement flags to enable Bitbucket authentication: + - `-bitbucket-repository` Restrict authorization to users that can access this repository + - `-bitbucket-team` Restrict authorization to users that are part of this Bitbucket team - [#211](https://github.com/pusher/oauth2_proxy/pull/211) Switch from dep to go modules (@steakunderscore) - [#145](https://github.com/pusher/oauth2_proxy/pull/145) Add support for OIDC UserInfo endpoint email verification (@rtluckie) diff --git a/main.go b/main.go index 872990a..a9f1e4a 100644 --- a/main.go +++ b/main.go @@ -56,6 +56,8 @@ func main() { flagSet.Var(&emailDomains, "email-domain", "authenticate emails with the specified domain (may be given multiple times). Use * to authenticate any email") flagSet.Var(&whitelistDomains, "whitelist-domain", "allowed domains for redirection after authentication. Prefix domain with a . to allow subdomains (eg .example.com)") flagSet.String("azure-tenant", "common", "go to a tenant-specific or common (tenant-independent) endpoint.") + flagSet.String("bitbucket-team", "", "restrict logins to members of this team") + flagSet.String("bitbucket-repository", "", "restrict logins to user with access to this repository") 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") diff --git a/options.go b/options.go index 03f5ff3..706f6d5 100644 --- a/options.go +++ b/options.go @@ -42,6 +42,8 @@ type Options struct { AuthenticatedEmailsFile string `flag:"authenticated-emails-file" cfg:"authenticated_emails_file" env:"OAUTH2_PROXY_AUTHENTICATED_EMAILS_FILE"` AzureTenant string `flag:"azure-tenant" cfg:"azure_tenant" env:"OAUTH2_PROXY_AZURE_TENANT"` + BitbucketTeam string `flag:"bitbucket-team" cfg:"bitbucket_team" env:"OAUTH2_PROXY_BITBUCKET_TEAM"` + BitbucketRepository string `flag:"bitbucket-repository" cfg:"bitbucket_repository" env:"OAUTH2_PROXY_BITBUCKET_REPOSITORY"` EmailDomains []string `flag:"email-domain" cfg:"email_domains" env:"OAUTH2_PROXY_EMAIL_DOMAINS"` WhitelistDomains []string `flag:"whitelist-domain" cfg:"whitelist_domains" env:"OAUTH2_PROXY_WHITELIST_DOMAINS"` GitHubOrg string `flag:"github-org" cfg:"github_org" env:"OAUTH2_PROXY_GITHUB_ORG"` @@ -405,6 +407,9 @@ func parseProviderInfo(o *Options, msgs []string) []string { p.SetGroupRestriction(o.GoogleGroups, o.GoogleAdminEmail, file) } } + case *providers.BitbucketProvider: + p.SetTeam(o.BitbucketTeam) + p.SetRepository(o.BitbucketRepository) case *providers.OIDCProvider: p.AllowUnverifiedEmail = o.InsecureOIDCAllowUnverifiedEmail if o.oidcVerifier == nil { diff --git a/providers/bitbucket.go b/providers/bitbucket.go new file mode 100644 index 0000000..63c1d0f --- /dev/null +++ b/providers/bitbucket.go @@ -0,0 +1,163 @@ +package providers + +import ( + "net/http" + "net/url" + "strings" + + "github.com/pusher/oauth2_proxy/pkg/apis/sessions" + "github.com/pusher/oauth2_proxy/pkg/logger" + "github.com/pusher/oauth2_proxy/pkg/requests" +) + +// BitbucketProvider represents an Bitbucket based Identity Provider +type BitbucketProvider struct { + *ProviderData + Team string + Repository string +} + +// NewBitbucketProvider initiates a new BitbucketProvider +func NewBitbucketProvider(p *ProviderData) *BitbucketProvider { + p.ProviderName = "Bitbucket" + if p.LoginURL == nil || p.LoginURL.String() == "" { + p.LoginURL = &url.URL{ + Scheme: "https", + Host: "bitbucket.org", + Path: "/site/oauth2/authorize", + } + } + if p.RedeemURL == nil || p.RedeemURL.String() == "" { + p.RedeemURL = &url.URL{ + Scheme: "https", + Host: "bitbucket.org", + Path: "/site/oauth2/access_token", + } + } + if p.ValidateURL == nil || p.ValidateURL.String() == "" { + p.ValidateURL = &url.URL{ + Scheme: "https", + Host: "api.bitbucket.org", + Path: "/2.0/user/emails", + } + } + if p.Scope == "" { + p.Scope = "email" + } + return &BitbucketProvider{ProviderData: p} +} + +// SetTeam defines the Bitbucket team the user must be part of +func (p *BitbucketProvider) SetTeam(team string) { + p.Team = team + if !strings.Contains(p.Scope, "team") { + p.Scope += " team" + } +} + +// SetRepository defines the repository the user must have access to +func (p *BitbucketProvider) SetRepository(repository string) { + p.Repository = repository + if !strings.Contains(p.Scope, "repository") { + p.Scope += " repository" + } +} + +// GetEmailAddress returns the email of the authenticated user +func (p *BitbucketProvider) GetEmailAddress(s *sessions.SessionState) (string, error) { + + var emails struct { + Values []struct { + Email string `json:"email"` + Primary bool `json:"is_primary"` + } + } + var teams struct { + Values []struct { + Name string `json:"username"` + } + } + var repositories struct { + Values []struct { + FullName string `json:"full_name"` + } + } + req, err := http.NewRequest("GET", + p.ValidateURL.String()+"?access_token="+s.AccessToken, nil) + if err != nil { + logger.Printf("failed building request %s", err) + return "", err + } + err = requests.RequestJSON(req, &emails) + if err != nil { + logger.Printf("failed making request %s", err) + return "", err + } + + if p.Team != "" { + teamURL := &url.URL{} + *teamURL = *p.ValidateURL + teamURL.Path = "/2.0/teams" + req, err = http.NewRequest("GET", + teamURL.String()+"?role=member&access_token="+s.AccessToken, nil) + if err != nil { + logger.Printf("failed building request %s", err) + return "", err + } + err = requests.RequestJSON(req, &teams) + if err != nil { + logger.Printf("failed requesting teams membership %s", err) + return "", err + } + var found = false + for _, team := range teams.Values { + if p.Team == team.Name { + found = true + break + } + } + if found != true { + logger.Print("team membership test failed, access denied") + return "", nil + } + } + + if p.Repository != "" { + repositoriesURL := &url.URL{} + *repositoriesURL = *p.ValidateURL + repositoriesURL.Path = "/2.0/repositories/" + strings.Split(p.Repository, "/")[0] + req, err = http.NewRequest("GET", + repositoriesURL.String()+"?role=contributor"+ + "&q=full_name="+url.QueryEscape("\""+p.Repository+"\"")+ + "&access_token="+s.AccessToken, + nil) + if err != nil { + logger.Printf("failed building request %s", err) + return "", err + } + err = requests.RequestJSON(req, &repositories) + if err != nil { + logger.Printf("failed checking repository access %s", err) + return "", err + } + var found = false + for _, repository := range repositories.Values { + if p.Repository == repository.FullName { + found = true + break + } + } + if found != true { + logger.Print("repository access test failed, access denied") + return "", nil + } + } + + for _, email := range emails.Values { + if email.Primary { + return email.Email, nil + } + } + + return "", nil +} diff --git a/providers/bitbucket_test.go b/providers/bitbucket_test.go new file mode 100644 index 0000000..585603d --- /dev/null +++ b/providers/bitbucket_test.go @@ -0,0 +1,170 @@ +package providers + +import ( + "log" + "net/http" + "net/http/httptest" + "net/url" + "testing" + + "github.com/stretchr/testify/assert" + + "github.com/pusher/oauth2_proxy/pkg/apis/sessions" +) + +func testBitbucketProvider(hostname, team string, repository string) *BitbucketProvider { + p := NewBitbucketProvider( + &ProviderData{ + ProviderName: "", + LoginURL: &url.URL{}, + RedeemURL: &url.URL{}, + ProfileURL: &url.URL{}, + ValidateURL: &url.URL{}, + Scope: ""}) + + if team != "" { + p.SetTeam(team) + } + + if repository != "" { + p.SetRepository(repository) + } + + if hostname != "" { + updateURL(p.Data().LoginURL, hostname) + updateURL(p.Data().RedeemURL, hostname) + updateURL(p.Data().ProfileURL, hostname) + updateURL(p.Data().ValidateURL, hostname) + } + return p +} + +func testBitbucketBackend(payload string) *httptest.Server { + paths := map[string]bool{ + "/2.0/user/emails": true, + "/2.0/teams": true, + } + + return httptest.NewServer(http.HandlerFunc( + func(w http.ResponseWriter, r *http.Request) { + url := r.URL + if !paths[url.Path] { + log.Printf("%s not in %+v\n", url.Path, paths) + w.WriteHeader(404) + } else if r.URL.Query().Get("access_token") != "imaginary_access_token" { + w.WriteHeader(403) + } else { + w.WriteHeader(200) + w.Write([]byte(payload)) + } + })) +} + +func TestBitbucketProviderDefaults(t *testing.T) { + p := testBitbucketProvider("", "", "") + assert.NotEqual(t, nil, p) + assert.Equal(t, "Bitbucket", p.Data().ProviderName) + assert.Equal(t, "https://bitbucket.org/site/oauth2/authorize", + p.Data().LoginURL.String()) + assert.Equal(t, "https://bitbucket.org/site/oauth2/access_token", + p.Data().RedeemURL.String()) + assert.Equal(t, "https://api.bitbucket.org/2.0/user/emails", + p.Data().ValidateURL.String()) + assert.Equal(t, "email", p.Data().Scope) +} + +func TestBitbucketProviderScopeAdjustForTeam(t *testing.T) { + p := testBitbucketProvider("", "test-team", "") + assert.NotEqual(t, nil, p) + assert.Equal(t, "email team", p.Data().Scope) +} + +func TestBitbucketProviderScopeAdjustForRepository(t *testing.T) { + p := testBitbucketProvider("", "", "rest-repo") + assert.NotEqual(t, nil, p) + assert.Equal(t, "email repository", p.Data().Scope) +} + +func TestBitbucketProviderOverrides(t *testing.T) { + p := NewBitbucketProvider( + &ProviderData{ + LoginURL: &url.URL{ + Scheme: "https", + Host: "example.com", + Path: "/oauth/auth"}, + RedeemURL: &url.URL{ + Scheme: "https", + Host: "example.com", + Path: "/oauth/token"}, + ValidateURL: &url.URL{ + Scheme: "https", + Host: "example.com", + Path: "/api/v3/user"}, + Scope: "profile"}) + assert.NotEqual(t, nil, p) + assert.Equal(t, "Bitbucket", p.Data().ProviderName) + assert.Equal(t, "https://example.com/oauth/auth", + p.Data().LoginURL.String()) + assert.Equal(t, "https://example.com/oauth/token", + p.Data().RedeemURL.String()) + assert.Equal(t, "https://example.com/api/v3/user", + p.Data().ValidateURL.String()) + assert.Equal(t, "profile", p.Data().Scope) +} + +func TestBitbucketProviderGetEmailAddress(t *testing.T) { + b := testBitbucketBackend("{\"values\": [ { \"email\": \"michael.bland@gsa.gov\", \"is_primary\": true } ] }") + defer b.Close() + + bURL, _ := url.Parse(b.URL) + p := testBitbucketProvider(bURL.Host, "", "") + + session := &sessions.SessionState{AccessToken: "imaginary_access_token"} + email, err := p.GetEmailAddress(session) + assert.Equal(t, nil, err) + assert.Equal(t, "michael.bland@gsa.gov", email) +} + +func TestBitbucketProviderGetEmailAddressAndGroup(t *testing.T) { + b := testBitbucketBackend("{\"values\": [ { \"email\": \"michael.bland@gsa.gov\", \"is_primary\": true, \"username\": \"bioinformatics\" } ] }") + defer b.Close() + + bURL, _ := url.Parse(b.URL) + p := testBitbucketProvider(bURL.Host, "bioinformatics", "") + + session := &sessions.SessionState{AccessToken: "imaginary_access_token"} + email, err := p.GetEmailAddress(session) + assert.Equal(t, nil, err) + assert.Equal(t, "michael.bland@gsa.gov", email) +} + +// Note that trying to trigger the "failed building request" case is not +// practical, since the only way it can fail is if the URL fails to parse. +func TestBitbucketProviderGetEmailAddressFailedRequest(t *testing.T) { + b := testBitbucketBackend("unused payload") + defer b.Close() + + bURL, _ := url.Parse(b.URL) + p := testBitbucketProvider(bURL.Host, "", "") + + // We'll trigger a request failure by using an unexpected access + // token. Alternatively, we could allow the parsing of the payload as + // JSON to fail. + session := &sessions.SessionState{AccessToken: "unexpected_access_token"} + email, err := p.GetEmailAddress(session) + assert.NotEqual(t, nil, err) + assert.Equal(t, "", email) +} + +func TestBitbucketProviderGetEmailAddressEmailNotPresentInPayload(t *testing.T) { + b := testBitbucketBackend("{\"foo\": \"bar\"}") + defer b.Close() + + bURL, _ := url.Parse(b.URL) + p := testBitbucketProvider(bURL.Host, "", "") + + session := &sessions.SessionState{AccessToken: "imaginary_access_token"} + email, err := p.GetEmailAddress(session) + assert.Equal(t, "", email) + assert.Equal(t, nil, err) +} diff --git a/providers/providers.go b/providers/providers.go index baf723d..276fab6 100644 --- a/providers/providers.go +++ b/providers/providers.go @@ -36,6 +36,8 @@ func New(provider string, p *ProviderData) Provider { return NewOIDCProvider(p) case "login.gov": return NewLoginGovProvider(p) + case "bitbucket": + return NewBitbucketProvider(p) default: return NewGoogleProvider(p) } From 44ea6920a7cb768b7da422d555438a6e9452e8cb Mon Sep 17 00:00:00 2001 From: Joel Speed Date: Fri, 16 Aug 2019 15:06:53 +0100 Subject: [PATCH 4/6] Update changelog for v4.0.0 release --- CHANGELOG.md | 26 +++++++++++++++++++------- README.md | 4 ++-- 2 files changed, 21 insertions(+), 9 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 526ecdf..44cd0cf 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,12 +1,26 @@ # Vx.x.x (Pre-release) +## Changes since v4.0.0 + +# v4.0.0 + +## Release Highlights +- Documentation is now on a [microsite](https://pusher.github.io/oauth2_proxy/) +- Health check logging can now be disabled for quieter logs +- Authorization Header JWTs can now be verified by the proxy to skip authentication for machine users +- Sessions can now be stored in Redis. This reduces refresh failures and uses smaller cookies (Recommended for those using OIDC refreshing) +- Logging overhaul allows customisable logging formats + +## Important Notes +- This release includes a number of breaking changes that will require users to +reconfigure their proxies. Please read the Breaking Changes below thoroughly. + ## Breaking Changes -- [#231](https://github.com/pusher/oauth2_proxy/pull/231) Rework GitLab provider (@Overv) +- [#231](https://github.com/pusher/oauth2_proxy/pull/231) Rework GitLab provider - This PR changes the configuration options for the GitLab provider to use a self-hosted instance. You now need to specify a `-oidc-issuer-url` rather than explicit `-login-url`, `-redeem-url` and `-validate-url` parameters. - - [#186](https://github.com/pusher/oauth2_proxy/pull/186) Make config consistent - This PR changes configuration options so that all flags have a config counterpart of the same name but with underscores (`_`) in place of hyphens (`-`). @@ -23,8 +37,7 @@ This change affects the following existing environment variables: - The `OAUTH2_SKIP_OIDC_DISCOVERY` environment variable is now `OAUTH2_PROXY_SKIP_OIDC_DISCOVERY`. - The `OAUTH2_OIDC_JWKS_URL` environment variable is now `OAUTH2_PROXY_OIDC_JWKS_URL`. - -- [#146](https://github.com/pusher/oauth2_proxy/pull/146) Use full email address as `User` if the auth response did not contain a `User` field (@gargath) +- [#146](https://github.com/pusher/oauth2_proxy/pull/146) Use full email address as `User` if the auth response did not contain a `User` field - This change modifies the contents of the `X-Forwarded-User` header supplied by the proxy for users where the auth response from the IdP did not contain a username. In that case, this header used to only contain the local part of the user's email address (e.g. `john.doe` for `john.doe@example.com`) but now contains @@ -45,7 +58,7 @@ - [#186](https://github.com/pusher/oauth2_proxy/pull/186) Make config consistent (@JoelSpeed) - [#187](https://github.com/pusher/oauth2_proxy/pull/187) Move root packages to pkg folder (@JoelSpeed) - [#65](https://github.com/pusher/oauth2_proxy/pull/65) Improvements to authenticate requests with a JWT bearer token in the `Authorization` header via - the `-skip-jwt-bearer-token` options. + the `-skip-jwt-bearer-token` options. (@brianv0) - Additional verifiers can be configured via the `-extra-jwt-issuers` flag if the JWT issuers is either an OpenID provider or has a JWKS URL (e.g. `https://example.com/.well-known/jwks.json`). - [#180](https://github.com/pusher/oauth2_proxy/pull/180) Minor refactor of core proxying path (@aeijdenberg). @@ -86,7 +99,6 @@ - Implement two new flags to customize the logging format - `-standard-logging-format` Sets the format for standard logging - `-auth-logging-format` Sets the format for auth logging - - [#111](https://github.com/pusher/oauth2_proxy/pull/111) Add option for telling where to find a login.gov JWT key file (@timothy-spencer) - [#170](https://github.com/pusher/oauth2_proxy/pull/170) Restore binary tarball contents to be compatible with bitlys original tarballs (@zeha) - [#185](https://github.com/pusher/oauth2_proxy/pull/185) Fix an unsupported protocol scheme error during token validation when using the Azure provider (@jonas) @@ -94,7 +106,7 @@ - Google Group membership is additionally checked via email address, allowing users outside a GSuite domain to be authorized. - [#195](https://github.com/pusher/oauth2_proxy/pull/195) Add `-banner` flag for overriding the banner line that is displayed (@steakunderscore) - [#198](https://github.com/pusher/oauth2_proxy/pull/198) Switch from gometalinter to golangci-lint (@steakunderscore) -- [#159](https://github.com/pusher/oauth2_proxy/pull/159) Add option to skip the OIDC provider verified email check: `--insecure-oidc-allow-unverified-email` +- [#159](https://github.com/pusher/oauth2_proxy/pull/159) Add option to skip the OIDC provider verified email check: `--insecure-oidc-allow-unverified-email` (@djfinlay) - [#210](https://github.com/pusher/oauth2_proxy/pull/210) Update base image from Alpine 3.9 to 3.10 (@steakunderscore) - [#201](https://github.com/pusher/oauth2_proxy/pull/201) Add Bitbucket as new OAuth2 provider, accepts email, team and repository permissions to determine authorization (@aledeganopix4d) - Implement flags to enable Bitbucket authentication: diff --git a/README.md b/README.md index 9657e8f..ad88331 100644 --- a/README.md +++ b/README.md @@ -15,7 +15,7 @@ A list of changes can be seen in the [CHANGELOG](CHANGELOG.md). 1. Choose how to deploy: - a. Download [Prebuilt Binary](https://github.com/pusher/oauth2_proxy/releases) (current release is `v3.2.0`) + a. Download [Prebuilt Binary](https://github.com/pusher/oauth2_proxy/releases) (current release is `v4.0.0`) b. Build with `$ go get github.com/pusher/oauth2_proxy` which will put the binary in `$GOROOT/bin` @@ -25,7 +25,7 @@ Prebuilt binaries can be validated by extracting the file and verifying it again ``` sha256sum -c sha256sum.txt 2>&1 | grep OK -oauth2_proxy-3.2.0.linux-amd64: OK +oauth2_proxy-4.0.0.linux-amd64: OK ``` 2. [Select a Provider and Register an OAuth Application with a Provider](https://pusher.github.io/oauth2_proxy/auth-configuration) From b83b7565f33a60421cbf485eb6566698166615fd Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Wed, 21 Aug 2019 10:05:52 +0000 Subject: [PATCH 5/6] Bump nokogiri from 1.10.1 to 1.10.4 in /docs Bumps [nokogiri](https://github.com/sparklemotion/nokogiri) from 1.10.1 to 1.10.4. - [Release notes](https://github.com/sparklemotion/nokogiri/releases) - [Changelog](https://github.com/sparklemotion/nokogiri/blob/master/CHANGELOG.md) - [Commits](https://github.com/sparklemotion/nokogiri/compare/v1.10.1...v1.10.4) Signed-off-by: dependabot[bot] --- docs/Gemfile.lock | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/Gemfile.lock b/docs/Gemfile.lock index 8d96e5d..934baab 100644 --- a/docs/Gemfile.lock +++ b/docs/Gemfile.lock @@ -208,7 +208,7 @@ GEM jekyll-seo-tag (~> 2.1) minitest (5.11.3) multipart-post (2.0.0) - nokogiri (1.10.1) + nokogiri (1.10.4) mini_portile2 (~> 2.4.0) octokit (4.13.0) sawyer (~> 0.8.0, >= 0.5.3) From 82a3d5afdcf9ec2d80449f01d355b9c28437144e Mon Sep 17 00:00:00 2001 From: Nelson Menezes Date: Tue, 27 Aug 2019 18:15:33 +0200 Subject: [PATCH 6/6] Add clarification about plural env vars (#252) --- docs/configuration/configuration.md | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/docs/configuration/configuration.md b/docs/configuration/configuration.md index 05cc299..6fca562 100644 --- a/docs/configuration/configuration.md +++ b/docs/configuration/configuration.md @@ -126,12 +126,14 @@ Multiple upstreams can either be configured by supplying a comma separated list Every command line argument can be specified as an environment variable by prefixing it with `OAUTH2_PROXY_`, capitalising it, and replacing hypens (`-`) -with underscores (`_`). This is particularly useful for storing secrets outside -of a configuration file or the command line. +with underscores (`_`). If the argument can be specified multiple times, the +environment variable should be plural (trailing `S`). -For example, the `--cookie-secret` flag becomes `OAUTH2_PROXY_COOKIE_SECRET` and -the `--set-authorization-header` flag becomes `OAUTH2_PROXY_SET_AUTHORIZATION_HEADER`. +This is particularly useful for storing secrets outside of a configuration file +or the command line. +For example, the `--cookie-secret` flag becomes `OAUTH2_PROXY_COOKIE_SECRET`, +and the `--email-domain` flag becomes `OAUTH2_PROXY_EMAIL_DOMAINS`. ## Logging Configuration