diff --git a/CHANGELOG.md b/CHANGELOG.md index 783c6dc718..e49f3e64e3 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -4,11 +4,31 @@ ## Important Notes +- [#575](https://github.com/oauth2-proxy/oauth2-proxy/pull/575) Sessions from v5.1.1 or earlier will no longer validate since they were not signed with SHA1. + - Sessions from v6.0.0 or later had a graceful conversion to SHA256 that resulted in no reauthentication + - Upgrading from v5.1.1 or earlier will result in a reauthentication +- [#616](https://github.com/oauth2-proxy/oauth2-proxy/pull/616) Ensure you have configured oauth2-proxy to use the `groups` scope. The user may be logged out initially as they may not currently have the `groups` claim however after going back through login process wil be authenticated. + ## Breaking Changes +- [#722](https://github.com/oauth2-proxy/oauth2-proxy/pull/722) When a Redis session store is configured, OAuth2-Proxy will fail to start up unless connection and health checks to Redis pass +- [#800](https://github.com/oauth2-proxy/oauth2-proxy/pull/800) Fix import path for v7. The import path has changed to support the go get installation. + - You can now `go get github.com/oauth2-proxy/oauth2-proxy/v7` to get the latest `v7` version of OAuth2 Proxy + - Import paths for package are now under `v7`, eg `github.com/oauth2-proxy/oauth2-proxy/v7/pkg/` +- [#753](https://github.com/oauth2-proxy/oauth2-proxy/pull/753) A bug in the Azure provider prevented it from properly passing the configured protected `--resource` + via the login url. If this option was used in the past, behavior will change with this release as it will + affect the tokens returned by Azure. In the past, the tokens were always for `https://graph.microsoft.com` (the default) + and will now be for the configured resource (if it exists, otherwise it will run into errors) + ## Changes since v6.1.1 +- [#753](https://github.com/oauth2-proxy/oauth2-proxy/pull/753) Pass resource parameter in login url (@codablock) +- [#575](https://github.com/oauth2-proxy/oauth2-proxy/pull/575) Stop accepting legacy SHA1 signed cookies (@NickMeves) +- [#722](https://github.com/oauth2-proxy/oauth2-proxy/pull/722) Validate Redis configuration options at startup (@NickMeves) +- [#791](https://github.com/oauth2-proxy/oauth2-proxy/pull/791) Remove GetPreferredUsername method from provider interface (@NickMeves) - [#764](https://github.com/oauth2-proxy/oauth2-proxy/pull/764) Document bcrypt encryption for htpasswd (and hide SHA) (@lentzi90) +- [#616](https://github.com/oauth2-proxy/oauth2-proxy/pull/616) Add support to ensure user belongs in required groups when using the OIDC provider (@stefansedich) +- [#800](https://github.com/oauth2-proxy/oauth2-proxy/pull/800) Fix import path for v7 (@johejo) # v6.1.1 diff --git a/docs/1_installation.md b/docs/1_installation.md index 79888db2d4..f2415629e5 100644 --- a/docs/1_installation.md +++ b/docs/1_installation.md @@ -11,7 +11,7 @@ nav_order: 1 a. Download [Prebuilt Binary](https://github.com/oauth2-proxy/oauth2-proxy/releases) (current release is `v6.1.1`) - b. Build with `$ go get github.com/oauth2-proxy/oauth2-proxy` which will put the binary in `$GOROOT/bin` + b. Build with `$ go get github.com/oauth2-proxy/oauth2-proxy/v7` which will put the binary in `$GOPATH/bin` c. Using the prebuilt docker image [quay.io/oauth2-proxy/oauth2-proxy](https://quay.io/oauth2-proxy/oauth2-proxy) (AMD64, ARMv6 and ARM64 tags available) diff --git a/docs/2_auth.md b/docs/2_auth.md index 974dfbc946..d823028993 100644 --- a/docs/2_auth.md +++ b/docs/2_auth.md @@ -142,9 +142,9 @@ Make sure you set the following to the appropriate url: -provider=keycloak -client-id= -client-secret= - -login-url="http(s):///realms//protocol/openid-connect/auth" - -redeem-url="http(s):///realms//protocol/openid-connect/token" - -validate-url="http(s):///realms//protocol/openid-connect/userinfo" + -login-url="http(s):///auth/realms//protocol/openid-connect/auth" + -redeem-url="http(s):///auth/realms//protocol/openid-connect/token" + -validate-url="http(s):///auth/realms//protocol/openid-connect/userinfo" -keycloak-group= The group management in keycloak is using a tree. If you create a group named admin in keycloak you should define the 'keycloak-group' value to /admin. diff --git a/docs/configuration/configuration.md b/docs/configuration/configuration.md index 10e0afcc75..0370cdf4ea 100644 --- a/docs/configuration/configuration.md +++ b/docs/configuration/configuration.md @@ -78,12 +78,13 @@ An example [oauth2-proxy.cfg]({{ site.gitweb }}/contrib/oauth2-proxy.cfg.example | `--insecure-oidc-skip-issuer-verification` | bool | allow the OIDC issuer URL to differ from the expected (currently required for Azure multi-tenant compatibility) | false | | `--oidc-issuer-url` | string | the OpenID Connect issuer URL, e.g. `"https://accounts.google.com"` | | | `--oidc-jwks-url` | string | OIDC JWKS URI for token verification; required if OIDC discovery is disabled | | +| `--oidc-groups-claim` | string | which claim contains the user groups | `"groups"` | | `--pass-access-token` | bool | pass OAuth access_token to upstream via X-Forwarded-Access-Token header | false | | `--pass-authorization-header` | bool | pass OIDC IDToken to upstream via Authorization Bearer header | false | | `--pass-basic-auth` | bool | pass HTTP Basic Auth, X-Forwarded-User, X-Forwarded-Email and X-Forwarded-Preferred-Username information to upstream | true | | `--prefer-email-to-user` | bool | Prefer to use the Email address as the Username when passing information to upstream. Will only use Username if Email is unavailable, e.g. htaccess authentication. Used in conjunction with `--pass-basic-auth` and `--pass-user-headers` | false | | `--pass-host-header` | bool | pass the request Host Header to upstream | true | -| `--pass-user-headers` | bool | pass X-Forwarded-User, X-Forwarded-Email and X-Forwarded-Preferred-Username information to upstream | true | +| `--pass-user-headers` | bool | pass X-Forwarded-User, X-Forwarded-Groups, X-Forwarded-Email and X-Forwarded-Preferred-Username information to upstream | true | | `--profile-url` | string | Profile access endpoint | | | `--prompt` | string | [OIDC prompt](https://openid.net/specs/openid-connect-core-1_0.html#AuthRequest); if present, `approval-prompt` is ignored | `""` | | `--provider` | string | OAuth provider | google | @@ -112,7 +113,7 @@ An example [oauth2-proxy.cfg]({{ site.gitweb }}/contrib/oauth2-proxy.cfg.example | `--scope` | string | OAuth scope specification | | | `--session-cookie-minimal` | bool | strip OAuth tokens from cookie session stores if they aren't needed (cookie session store only) | false | | `--session-store-type` | string | [Session data storage backend](configuration/sessions); redis or cookie | cookie | -| `--set-xauthrequest` | bool | set X-Auth-Request-User, X-Auth-Request-Email and X-Auth-Request-Preferred-Username response headers (useful in Nginx auth_request mode) | false | +| `--set-xauthrequest` | bool | set X-Auth-Request-User, X-Auth-Request-Groups, X-Auth-Request-Email and X-Auth-Request-Preferred-Username response headers (useful in Nginx auth_request mode) | false | | `--set-authorization-header` | bool | set Authorization Bearer response header (useful in Nginx auth_request mode) | false | | `--set-basic-auth` | bool | set HTTP Basic Auth information in response (useful in Nginx auth_request mode) | false | | `--signature-key` | string | GAP-Signature request signature key (algorithm:secretkey) | | @@ -131,6 +132,7 @@ An example [oauth2-proxy.cfg]({{ site.gitweb }}/contrib/oauth2-proxy.cfg.example | `--tls-key-file` | string | path to private key file | | | `--upstream` | string \| list | the http url(s) of the upstream endpoint, file:// paths for static files or `static://` for static response. Routing is based on the path | | | `--user-id-claim` | string | which claim contains the user ID | \["email"\] | +| `--allowed-group` | string \| list | restrict logins to members of this group (may be given multiple times) | | | `--validate-url` | string | Access token validation endpoint | | | `--version` | n/a | print version string | | | `--whitelist-domain` | string \| list | allowed domains for redirection after authentication. Prefix domain with a `.` to allow subdomains (e.g. `.example.com`) \[[2](#footnote2)\] | | diff --git a/go.mod b/go.mod index b02815a688..7e4d28bf62 100644 --- a/go.mod +++ b/go.mod @@ -1,4 +1,4 @@ -module github.com/oauth2-proxy/oauth2-proxy +module github.com/oauth2-proxy/oauth2-proxy/v7 go 1.14 diff --git a/go.sum b/go.sum index 444ed563be..4f4922cfd6 100644 --- a/go.sum +++ b/go.sum @@ -150,15 +150,11 @@ github.com/nxadm/tail v1.4.4/go.mod h1:kenIhsEOeOJmVchQTgglprH7qJGnHDVpk1VPCcaMI github.com/oklog/ulid v1.3.1/go.mod h1:CirwcVhetQ6Lv90oh/F+FBtV6XMibvdAFo93nm5qn4U= github.com/onsi/ginkgo v1.6.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE= github.com/onsi/ginkgo v1.10.1/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE= -github.com/onsi/ginkgo v1.12.0 h1:Iw5WCbBcaAAd0fpRb1c9r5YCylv4XDoCSigm1zLevwU= -github.com/onsi/ginkgo v1.12.0/go.mod h1:oUhWkIvk5aDxtKvDDuw8gItl8pKl42LzjC9KZE0HfGg= github.com/onsi/ginkgo v1.12.1/go.mod h1:zj2OWP4+oCPe1qIXoGWkgMRwljMUYCdkwsT2108oapk= github.com/onsi/ginkgo v1.14.0 h1:2mOpI4JVVPBN+WQRa0WKH2eXR+Ey+uK4n7Zj0aYpIQA= github.com/onsi/ginkgo v1.14.0/go.mod h1:iSB4RoI2tjJc9BBv4NKIKWKya62Rps+oPG/Lv9klQyY= github.com/onsi/gomega v1.7.0/go.mod h1:ex+gbHU/CVuBBDIJjb2X0qEXbFg53c61hWP/1CpauHY= github.com/onsi/gomega v1.7.1/go.mod h1:XdKZgCCFLUoM/7CFJVPcG8C1xQ1AJ0vpAezJrB7JYyY= -github.com/onsi/gomega v1.9.0 h1:R1uwffexN6Pr340GtYRIdZmAiN4J+iw6WG4wog1DUXg= -github.com/onsi/gomega v1.9.0/go.mod h1:Ho0h+IUsWyvy1OpqCwxlQ/21gkhVunqlU8fDGcoTdcA= github.com/onsi/gomega v1.10.1 h1:o0+MgICZLuZ7xjH7Vx6zS/zcu93/BEp1VwkIW1mEXCE= github.com/onsi/gomega v1.10.1/go.mod h1:iN09h71vgCQne3DLsj+A5owkum+a2tYe+TOCB1ybHNo= github.com/pelletier/go-toml v1.2.0 h1:T5zMGML61Wp+FlcbWjRDT7yAxhJNAiPPLOFECq181zc= @@ -295,8 +291,6 @@ golang.org/x/tools v0.0.0-20190312170243-e65039ee4138/go.mod h1:LCzVGOaR6xXOjkQ3 golang.org/x/tools v0.0.0-20190328211700-ab21143f2384/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= golang.org/x/tools v0.0.0-20190524140312-2c0ae7006135 h1:5Beo0mZN8dRzgrMMkDp0jc8YXQKx9DiJ2k1dkvGsn5A= golang.org/x/tools v0.0.0-20190524140312-2c0ae7006135/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q= -golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7 h1:9zdDQZ7Thm29KFXgAX/+yaf3eVbP7djjWp/dXAppNCc= -golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543 h1:E7g+9GITq07hpfrRu66IVDexMakfv52eLZ2CXBWiKr4= golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= google.golang.org/api v0.4.0/go.mod h1:8k5glujaEP+g9n7WNsDg8QP6cUVNI86fCNMcbazEtwE= diff --git a/http.go b/http.go index fd0d6b76b8..48cac1333b 100644 --- a/http.go +++ b/http.go @@ -9,8 +9,8 @@ import ( "strings" "time" - "github.com/oauth2-proxy/oauth2-proxy/pkg/apis/options" - "github.com/oauth2-proxy/oauth2-proxy/pkg/logger" + "github.com/oauth2-proxy/oauth2-proxy/v7/pkg/apis/options" + "github.com/oauth2-proxy/oauth2-proxy/v7/pkg/logger" ) // Server represents an HTTP server diff --git a/http_test.go b/http_test.go index ba516b971d..f4e128430f 100644 --- a/http_test.go +++ b/http_test.go @@ -6,7 +6,7 @@ import ( "testing" "time" - "github.com/oauth2-proxy/oauth2-proxy/pkg/apis/options" + "github.com/oauth2-proxy/oauth2-proxy/v7/pkg/apis/options" "github.com/stretchr/testify/assert" ) diff --git a/logging_handler.go b/logging_handler.go index 1c8574135c..6da38c0ace 100644 --- a/logging_handler.go +++ b/logging_handler.go @@ -10,7 +10,7 @@ import ( "net/http" "time" - "github.com/oauth2-proxy/oauth2-proxy/pkg/logger" + "github.com/oauth2-proxy/oauth2-proxy/v7/pkg/logger" ) // responseLogger is wrapper of http.ResponseWriter that keeps track of its HTTP status diff --git a/logging_handler_test.go b/logging_handler_test.go index 5582af83c3..1938c54bb8 100644 --- a/logging_handler_test.go +++ b/logging_handler_test.go @@ -6,7 +6,7 @@ import ( "net/http/httptest" "testing" - "github.com/oauth2-proxy/oauth2-proxy/pkg/logger" + "github.com/oauth2-proxy/oauth2-proxy/v7/pkg/logger" "github.com/stretchr/testify/assert" ) diff --git a/main.go b/main.go index 527d55ad38..8cd3ee5bb3 100644 --- a/main.go +++ b/main.go @@ -11,10 +11,10 @@ import ( "time" "github.com/justinas/alice" - "github.com/oauth2-proxy/oauth2-proxy/pkg/apis/options" - "github.com/oauth2-proxy/oauth2-proxy/pkg/logger" - "github.com/oauth2-proxy/oauth2-proxy/pkg/middleware" - "github.com/oauth2-proxy/oauth2-proxy/pkg/validation" + "github.com/oauth2-proxy/oauth2-proxy/v7/pkg/apis/options" + "github.com/oauth2-proxy/oauth2-proxy/v7/pkg/logger" + "github.com/oauth2-proxy/oauth2-proxy/v7/pkg/middleware" + "github.com/oauth2-proxy/oauth2-proxy/v7/pkg/validation" ) func main() { diff --git a/oauthproxy.go b/oauthproxy.go index f4d3c496e5..092dcc938c 100644 --- a/oauthproxy.go +++ b/oauthproxy.go @@ -16,20 +16,20 @@ import ( "github.com/coreos/go-oidc" "github.com/justinas/alice" - ipapi "github.com/oauth2-proxy/oauth2-proxy/pkg/apis/ip" - middlewareapi "github.com/oauth2-proxy/oauth2-proxy/pkg/apis/middleware" - "github.com/oauth2-proxy/oauth2-proxy/pkg/apis/options" - sessionsapi "github.com/oauth2-proxy/oauth2-proxy/pkg/apis/sessions" - "github.com/oauth2-proxy/oauth2-proxy/pkg/authentication/basic" - "github.com/oauth2-proxy/oauth2-proxy/pkg/cookies" - "github.com/oauth2-proxy/oauth2-proxy/pkg/encryption" - "github.com/oauth2-proxy/oauth2-proxy/pkg/ip" - "github.com/oauth2-proxy/oauth2-proxy/pkg/logger" - "github.com/oauth2-proxy/oauth2-proxy/pkg/middleware" - "github.com/oauth2-proxy/oauth2-proxy/pkg/sessions" - "github.com/oauth2-proxy/oauth2-proxy/pkg/upstream" - "github.com/oauth2-proxy/oauth2-proxy/pkg/util" - "github.com/oauth2-proxy/oauth2-proxy/providers" + ipapi "github.com/oauth2-proxy/oauth2-proxy/v7/pkg/apis/ip" + middlewareapi "github.com/oauth2-proxy/oauth2-proxy/v7/pkg/apis/middleware" + "github.com/oauth2-proxy/oauth2-proxy/v7/pkg/apis/options" + sessionsapi "github.com/oauth2-proxy/oauth2-proxy/v7/pkg/apis/sessions" + "github.com/oauth2-proxy/oauth2-proxy/v7/pkg/authentication/basic" + "github.com/oauth2-proxy/oauth2-proxy/v7/pkg/cookies" + "github.com/oauth2-proxy/oauth2-proxy/v7/pkg/encryption" + "github.com/oauth2-proxy/oauth2-proxy/v7/pkg/ip" + "github.com/oauth2-proxy/oauth2-proxy/v7/pkg/logger" + "github.com/oauth2-proxy/oauth2-proxy/v7/pkg/middleware" + "github.com/oauth2-proxy/oauth2-proxy/v7/pkg/sessions" + "github.com/oauth2-proxy/oauth2-proxy/v7/pkg/upstream" + "github.com/oauth2-proxy/oauth2-proxy/v7/pkg/util" + "github.com/oauth2-proxy/oauth2-proxy/v7/providers" ) const ( @@ -102,6 +102,7 @@ type OAuthProxy struct { trustedIPs *ip.NetSet Banner string Footer string + AllowedGroups []string sessionChain alice.Chain } @@ -215,6 +216,7 @@ func NewOAuthProxy(opts *options.Options, validator func(string) bool) (*OAuthPr Banner: opts.Banner, Footer: opts.Footer, SignInMessage: buildSignInMessage(opts), + AllowedGroups: opts.AllowedGroups, basicAuthValidator: basicAuthValidator, displayHtpasswdForm: basicAuthValidator != nil, @@ -294,34 +296,31 @@ func (p *OAuthProxy) GetRedirectURI(host string) string { return u.String() } -func (p *OAuthProxy) redeemCode(ctx context.Context, host, code string) (s *sessionsapi.SessionState, err error) { +func (p *OAuthProxy) redeemCode(ctx context.Context, host, code string) (*sessionsapi.SessionState, error) { if code == "" { return nil, errors.New("missing code") } redirectURI := p.GetRedirectURI(host) - s, err = p.provider.Redeem(ctx, redirectURI, code) + s, err := p.provider.Redeem(ctx, redirectURI, code) if err != nil { - return + return nil, err } if s.Email == "" { s.Email, err = p.provider.GetEmailAddress(ctx, s) - } - - if s.PreferredUsername == "" { - s.PreferredUsername, err = p.provider.GetPreferredUsername(ctx, s) - if err != nil && err.Error() == "not implemented" { - err = nil + if err != nil && err.Error() != "not implemented" { + return nil, err } } if s.User == "" { s.User, err = p.provider.GetUserName(ctx, s) - if err != nil && err.Error() == "not implemented" { - err = nil + if err != nil && err.Error() != "not implemented" { + return nil, err } } - return + + return s, nil } // MakeCSRFCookie creates a cookie for CSRF @@ -888,7 +887,10 @@ func (p *OAuthProxy) getAuthenticatedSession(rw http.ResponseWriter, req *http.R return nil, ErrNeedsLogin } - if session != nil && session.Email != "" && !p.Validator(session.Email) { + invalidEmail := session != nil && session.Email != "" && !p.Validator(session.Email) + invalidGroups := session != nil && !p.validateGroups(session.Groups) + + if invalidEmail || invalidGroups { logger.Printf(session.Email, req, logger.AuthFailure, "Invalid authentication via session: removing session %s", session) // Invalid session, clear it err := p.ClearSessionCookie(rw, req) @@ -942,6 +944,14 @@ func (p *OAuthProxy) addHeadersForProxying(rw http.ResponseWriter, req *http.Req } else { req.Header.Del("X-Forwarded-Preferred-Username") } + + if len(session.Groups) > 0 { + for _, group := range session.Groups { + req.Header.Add("X-Forwarded-Groups", group) + } + } else { + req.Header.Del("X-Forwarded-Groups") + } } if p.SetXAuthRequest { @@ -964,6 +974,14 @@ func (p *OAuthProxy) addHeadersForProxying(rw http.ResponseWriter, req *http.Req rw.Header().Del("X-Auth-Request-Access-Token") } } + + if len(session.Groups) > 0 { + for _, group := range session.Groups { + rw.Header().Add("X-Auth-Request-Groups", group) + } + } else { + rw.Header().Del("X-Auth-Request-Groups") + } } if p.PassAccessToken { @@ -1012,6 +1030,7 @@ func (p *OAuthProxy) addHeadersForProxying(rw http.ResponseWriter, req *http.Req func (p *OAuthProxy) stripAuthHeaders(req *http.Request) { if p.PassBasicAuth { req.Header.Del("X-Forwarded-User") + req.Header.Del("X-Forwarded-Groups") req.Header.Del("X-Forwarded-Email") req.Header.Del("X-Forwarded-Preferred-Username") req.Header.Del("Authorization") @@ -1019,6 +1038,7 @@ func (p *OAuthProxy) stripAuthHeaders(req *http.Request) { if p.PassUserHeaders { req.Header.Del("X-Forwarded-User") + req.Header.Del("X-Forwarded-Groups") req.Header.Del("X-Forwarded-Email") req.Header.Del("X-Forwarded-Preferred-Username") } @@ -1049,3 +1069,23 @@ func (p *OAuthProxy) ErrorJSON(rw http.ResponseWriter, code int) { rw.Header().Set("Content-Type", applicationJSON) rw.WriteHeader(code) } + +func (p *OAuthProxy) validateGroups(groups []string) bool { + if len(p.AllowedGroups) == 0 { + return true + } + + allowedGroups := map[string]struct{}{} + + for _, group := range p.AllowedGroups { + allowedGroups[group] = struct{}{} + } + + for _, group := range groups { + if _, ok := allowedGroups[group]; ok { + return true + } + } + + return false +} diff --git a/oauthproxy_test.go b/oauthproxy_test.go index 8342527415..33f131cda1 100644 --- a/oauthproxy_test.go +++ b/oauthproxy_test.go @@ -19,13 +19,13 @@ import ( "github.com/coreos/go-oidc" "github.com/mbland/hmacauth" - "github.com/oauth2-proxy/oauth2-proxy/pkg/apis/options" - "github.com/oauth2-proxy/oauth2-proxy/pkg/apis/sessions" - "github.com/oauth2-proxy/oauth2-proxy/pkg/logger" - sessionscookie "github.com/oauth2-proxy/oauth2-proxy/pkg/sessions/cookie" - "github.com/oauth2-proxy/oauth2-proxy/pkg/upstream" - "github.com/oauth2-proxy/oauth2-proxy/pkg/validation" - "github.com/oauth2-proxy/oauth2-proxy/providers" + "github.com/oauth2-proxy/oauth2-proxy/v7/pkg/apis/options" + "github.com/oauth2-proxy/oauth2-proxy/v7/pkg/apis/sessions" + "github.com/oauth2-proxy/oauth2-proxy/v7/pkg/logger" + sessionscookie "github.com/oauth2-proxy/oauth2-proxy/v7/pkg/sessions/cookie" + "github.com/oauth2-proxy/oauth2-proxy/v7/pkg/upstream" + "github.com/oauth2-proxy/oauth2-proxy/v7/pkg/validation" + "github.com/oauth2-proxy/oauth2-proxy/v7/providers" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" ) @@ -592,6 +592,37 @@ func TestPassUserHeadersWithEmail(t *testing.T) { } } +func TestPassGroupsHeadersWithGroups(t *testing.T) { + opts := baseTestOptions() + err := validation.Validate(opts) + assert.NoError(t, err) + + const emailAddress = "john.doe@example.com" + const userName = "9fcab5c9b889a557" + + groups := []string{"a", "b"} + created := time.Now() + session := &sessions.SessionState{ + User: userName, + Groups: groups, + Email: emailAddress, + AccessToken: "oauth_token", + CreatedAt: &created, + } + { + rw := httptest.NewRecorder() + req, _ := http.NewRequest("GET", opts.ProxyPrefix+"/testCase0", nil) + proxy, err := NewOAuthProxy(opts, func(email string) bool { + return email == emailAddress + }) + if err != nil { + t.Fatal(err) + } + proxy.addHeadersForProxying(rw, req, session) + assert.Equal(t, groups, req.Header["X-Forwarded-Groups"]) + } +} + func TestStripAuthHeaders(t *testing.T) { testCases := map[string]struct { SkipAuthStripHeaders bool @@ -609,6 +640,7 @@ func TestStripAuthHeaders(t *testing.T) { PassAuthorization: false, StrippedHeaders: map[string]bool{ "X-Forwarded-User": true, + "X-Forwared-Groups": true, "X-Forwarded-Email": true, "X-Forwarded-Preferred-Username": true, "X-Forwarded-Access-Token": false, @@ -623,6 +655,7 @@ func TestStripAuthHeaders(t *testing.T) { PassAuthorization: false, StrippedHeaders: map[string]bool{ "X-Forwarded-User": true, + "X-Forwared-Groups": true, "X-Forwarded-Email": true, "X-Forwarded-Preferred-Username": true, "X-Forwarded-Access-Token": true, @@ -637,6 +670,7 @@ func TestStripAuthHeaders(t *testing.T) { PassAuthorization: false, StrippedHeaders: map[string]bool{ "X-Forwarded-User": true, + "X-Forwared-Groups": true, "X-Forwarded-Email": true, "X-Forwarded-Preferred-Username": true, "X-Forwarded-Access-Token": true, @@ -651,6 +685,7 @@ func TestStripAuthHeaders(t *testing.T) { PassAuthorization: true, StrippedHeaders: map[string]bool{ "X-Forwarded-User": false, + "X-Forwared-Groups": false, "X-Forwarded-Email": false, "X-Forwarded-Preferred-Username": false, "X-Forwarded-Access-Token": false, @@ -665,6 +700,7 @@ func TestStripAuthHeaders(t *testing.T) { PassAuthorization: false, StrippedHeaders: map[string]bool{ "X-Forwarded-User": false, + "X-Forwared-Groups": false, "X-Forwarded-Email": false, "X-Forwarded-Preferred-Username": false, "X-Forwarded-Access-Token": false, @@ -679,6 +715,7 @@ func TestStripAuthHeaders(t *testing.T) { PassAuthorization: false, StrippedHeaders: map[string]bool{ "X-Forwarded-User": false, + "X-Forwared-Groups": false, "X-Forwarded-Email": false, "X-Forwarded-Preferred-Username": false, "X-Forwarded-Access-Token": false, @@ -690,6 +727,7 @@ func TestStripAuthHeaders(t *testing.T) { initialHeaders := map[string]string{ "X-Forwarded-User": "9fcab5c9b889a557", "X-Forwarded-Email": "john.doe@example.com", + "X-Forwarded-Groups": "a,b,c", "X-Forwarded-Preferred-Username": "john.doe", "X-Forwarded-Access-Token": "AccessToken", "Authorization": "bearer IDToken", @@ -1333,6 +1371,7 @@ func TestAuthOnlyEndpointSetXAuthRequestHeaders(t *testing.T) { pcTest.opts = baseTestOptions() pcTest.opts.SetXAuthRequest = true + pcTest.opts.AllowedGroups = []string{"oauth_groups"} err := validation.Validate(pcTest.opts) assert.NoError(t, err) @@ -1354,13 +1393,14 @@ func TestAuthOnlyEndpointSetXAuthRequestHeaders(t *testing.T) { created := time.Now() startSession := &sessions.SessionState{ - User: "oauth_user", Email: "oauth_user@example.com", AccessToken: "oauth_token", CreatedAt: &created} + User: "oauth_user", Groups: []string{"oauth_groups"}, Email: "oauth_user@example.com", AccessToken: "oauth_token", CreatedAt: &created} err = pcTest.SaveSession(startSession) assert.NoError(t, err) pcTest.proxy.ServeHTTP(pcTest.rw, pcTest.req) assert.Equal(t, http.StatusAccepted, pcTest.rw.Code) assert.Equal(t, "oauth_user", pcTest.rw.Header().Get("X-Auth-Request-User")) + assert.Equal(t, startSession.Groups, pcTest.rw.Header().Values("X-Auth-Request-Groups")) assert.Equal(t, "oauth_user@example.com", pcTest.rw.Header().Get("X-Auth-Request-Email")) } @@ -2199,3 +2239,108 @@ func TestTrustedIPs(t *testing.T) { }) } } + +func TestProxyAllowedGroups(t *testing.T) { + tests := []struct { + name string + allowedGroups []string + groups []string + expectUnauthorized bool + }{ + {"NoAllowedGroups", []string{}, []string{}, false}, + {"NoAllowedGroupsUserHasGroups", []string{}, []string{"a", "b"}, false}, + {"UserInAllowedGroup", []string{"a"}, []string{"a", "b"}, false}, + {"UserNotInAllowedGroup", []string{"a"}, []string{"c"}, true}, + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + emailAddress := "test" + created := time.Now() + + session := &sessions.SessionState{ + Groups: tt.groups, + Email: emailAddress, + AccessToken: "oauth_token", + CreatedAt: &created, + } + + upstream := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + w.WriteHeader(200) + })) + t.Cleanup(upstream.Close) + + test, err := NewProcessCookieTestWithOptionsModifiers(func(opts *options.Options) { + opts.AllowedGroups = tt.allowedGroups + opts.UpstreamServers = options.Upstreams{ + { + ID: upstream.URL, + Path: "/", + URI: upstream.URL, + }, + } + }) + if err != nil { + t.Fatal(err) + } + + test.req, _ = http.NewRequest("GET", "/", nil) + + test.req.Header.Add("accept", applicationJSON) + test.SaveSession(session) + test.proxy.ServeHTTP(test.rw, test.req) + + if tt.expectUnauthorized { + assert.Equal(t, http.StatusUnauthorized, test.rw.Code) + } else { + assert.Equal(t, http.StatusOK, test.rw.Code) + } + }) + } +} + +func TestAuthOnlyAllowedGroups(t *testing.T) { + tests := []struct { + name string + allowedGroups []string + groups []string + expectUnauthorized bool + }{ + {"NoAllowedGroups", []string{}, []string{}, false}, + {"NoAllowedGroupsUserHasGroups", []string{}, []string{"a", "b"}, false}, + {"UserInAllowedGroup", []string{"a"}, []string{"a", "b"}, false}, + {"UserNotInAllowedGroup", []string{"a"}, []string{"c"}, true}, + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + emailAddress := "test" + created := time.Now() + + session := &sessions.SessionState{ + Groups: tt.groups, + Email: emailAddress, + AccessToken: "oauth_token", + CreatedAt: &created, + } + + test, err := NewAuthOnlyEndpointTest(func(opts *options.Options) { + opts.AllowedGroups = tt.allowedGroups + }) + if err != nil { + t.Fatal(err) + } + + err = test.SaveSession(session) + assert.NoError(t, err) + + test.proxy.ServeHTTP(test.rw, test.req) + + if tt.expectUnauthorized { + assert.Equal(t, http.StatusUnauthorized, test.rw.Code) + } else { + assert.Equal(t, http.StatusAccepted, test.rw.Code) + } + }) + } +} diff --git a/pkg/apis/middleware/scope.go b/pkg/apis/middleware/scope.go index c8153d1a93..37f6f336d6 100644 --- a/pkg/apis/middleware/scope.go +++ b/pkg/apis/middleware/scope.go @@ -1,7 +1,7 @@ package middleware import ( - "github.com/oauth2-proxy/oauth2-proxy/pkg/apis/sessions" + "github.com/oauth2-proxy/oauth2-proxy/v7/pkg/apis/sessions" ) // RequestScope contains information regarding the request that is being made. diff --git a/pkg/apis/middleware/session.go b/pkg/apis/middleware/session.go index 344ba31ef6..95a76fba55 100644 --- a/pkg/apis/middleware/session.go +++ b/pkg/apis/middleware/session.go @@ -4,7 +4,7 @@ import ( "context" "github.com/coreos/go-oidc" - sessionsapi "github.com/oauth2-proxy/oauth2-proxy/pkg/apis/sessions" + sessionsapi "github.com/oauth2-proxy/oauth2-proxy/v7/pkg/apis/sessions" ) // TokenToSessionFunc takes a rawIDToken and an idToken and converts it into a diff --git a/pkg/apis/options/legacy_options.go b/pkg/apis/options/legacy_options.go index a2256dafaa..2fe55ddd20 100644 --- a/pkg/apis/options/legacy_options.go +++ b/pkg/apis/options/legacy_options.go @@ -7,7 +7,7 @@ import ( "strings" "time" - "github.com/oauth2-proxy/oauth2-proxy/pkg/logger" + "github.com/oauth2-proxy/oauth2-proxy/v7/pkg/logger" "github.com/spf13/pflag" ) diff --git a/pkg/apis/options/logging.go b/pkg/apis/options/logging.go index b133825e15..1c6f941533 100644 --- a/pkg/apis/options/logging.go +++ b/pkg/apis/options/logging.go @@ -1,7 +1,7 @@ package options import ( - "github.com/oauth2-proxy/oauth2-proxy/pkg/logger" + "github.com/oauth2-proxy/oauth2-proxy/v7/pkg/logger" "github.com/spf13/pflag" ) diff --git a/pkg/apis/options/options.go b/pkg/apis/options/options.go index b723b60b76..bcb600e92e 100644 --- a/pkg/apis/options/options.go +++ b/pkg/apis/options/options.go @@ -6,8 +6,8 @@ import ( "regexp" oidc "github.com/coreos/go-oidc" - ipapi "github.com/oauth2-proxy/oauth2-proxy/pkg/apis/ip" - "github.com/oauth2-proxy/oauth2-proxy/providers" + ipapi "github.com/oauth2-proxy/oauth2-proxy/v7/pkg/apis/ip" + "github.com/oauth2-proxy/oauth2-proxy/v7/providers" "github.com/spf13/pflag" ) @@ -93,6 +93,7 @@ type Options struct { InsecureOIDCSkipIssuerVerification bool `flag:"insecure-oidc-skip-issuer-verification" cfg:"insecure_oidc_skip_issuer_verification"` SkipOIDCDiscovery bool `flag:"skip-oidc-discovery" cfg:"skip_oidc_discovery"` OIDCJwksURL string `flag:"oidc-jwks-url" cfg:"oidc_jwks_url"` + OIDCGroupsClaim string `flag:"oidc-groups-claim" cfg:"oidc_groups_claim"` LoginURL string `flag:"login-url" cfg:"login_url"` RedeemURL string `flag:"redeem-url" cfg:"redeem_url"` ProfileURL string `flag:"profile-url" cfg:"profile_url"` @@ -102,6 +103,7 @@ type Options struct { Prompt string `flag:"prompt" cfg:"prompt"` ApprovalPrompt string `flag:"approval-prompt" cfg:"approval_prompt"` // Deprecated by OIDC 1.0 UserIDClaim string `flag:"user-id-claim" cfg:"user_id_claim"` + AllowedGroups []string `flag:"allowed-group" cfg:"allowed_groups"` SignatureKey string `flag:"signature-key" cfg:"signature_key"` AcrValues string `flag:"acr-values" cfg:"acr_values"` @@ -167,6 +169,7 @@ func NewOptions() *Options { InsecureOIDCAllowUnverifiedEmail: false, SkipOIDCDiscovery: false, Logging: loggingDefaults(), + OIDCGroupsClaim: "groups", } } @@ -248,6 +251,7 @@ func NewFlagSet() *pflag.FlagSet { flagSet.Bool("insecure-oidc-skip-issuer-verification", false, "Do not verify if issuer matches OIDC discovery URL") flagSet.Bool("skip-oidc-discovery", false, "Skip OIDC discovery and use manually supplied Endpoints") flagSet.String("oidc-jwks-url", "", "OpenID Connect JWKS URL (ie: https://www.googleapis.com/oauth2/v3/certs)") + flagSet.String("oidc-groups-claim", "groups", "which claim contains the user groups") flagSet.String("login-url", "", "Authentication endpoint") flagSet.String("redeem-url", "", "Token redemption endpoint") flagSet.String("profile-url", "", "Profile access endpoint") @@ -265,6 +269,7 @@ func NewFlagSet() *pflag.FlagSet { flagSet.Bool("gcp-healthchecks", false, "Enable GCP/GKE healthcheck endpoints") flagSet.String("user-id-claim", "email", "which claim contains the user ID") + flagSet.StringSlice("allowed-group", []string{}, "restrict logins to members of this group (may be given multiple times)") flagSet.AddFlagSet(cookieFlagSet()) flagSet.AddFlagSet(loggingFlagSet()) diff --git a/pkg/apis/options/options_suite_test.go b/pkg/apis/options/options_suite_test.go index a25cbe42c8..6e1a05f3c0 100644 --- a/pkg/apis/options/options_suite_test.go +++ b/pkg/apis/options/options_suite_test.go @@ -3,7 +3,7 @@ package options import ( "testing" - "github.com/oauth2-proxy/oauth2-proxy/pkg/logger" + "github.com/oauth2-proxy/oauth2-proxy/v7/pkg/logger" . "github.com/onsi/ginkgo" . "github.com/onsi/gomega" ) diff --git a/pkg/apis/sessions/legacy_v5_tester.go b/pkg/apis/sessions/legacy_v5_tester.go index 25bc0b0c62..8fab4eae6a 100644 --- a/pkg/apis/sessions/legacy_v5_tester.go +++ b/pkg/apis/sessions/legacy_v5_tester.go @@ -5,7 +5,7 @@ import ( "testing" "time" - "github.com/oauth2-proxy/oauth2-proxy/pkg/encryption" + "github.com/oauth2-proxy/oauth2-proxy/v7/pkg/encryption" "github.com/stretchr/testify/assert" ) diff --git a/pkg/apis/sessions/session_state.go b/pkg/apis/sessions/session_state.go index e69c4db47d..c3db8994b1 100644 --- a/pkg/apis/sessions/session_state.go +++ b/pkg/apis/sessions/session_state.go @@ -7,10 +7,11 @@ import ( "fmt" "io" "io/ioutil" + "reflect" "time" "unicode/utf8" - "github.com/oauth2-proxy/oauth2-proxy/pkg/encryption" + "github.com/oauth2-proxy/oauth2-proxy/v7/pkg/encryption" "github.com/pierrec/lz4" "github.com/vmihailenco/msgpack/v4" ) @@ -24,6 +25,7 @@ type SessionState struct { RefreshToken string `json:",omitempty" msgpack:"rt,omitempty"` Email string `json:",omitempty" msgpack:"e,omitempty"` User string `json:",omitempty" msgpack:"u,omitempty"` + Groups []string `json:",omitempty" msgpack:"g,omitempty"` PreferredUsername string `json:",omitempty" msgpack:"pu,omitempty"` } @@ -61,6 +63,9 @@ func (s *SessionState) String() string { if s.RefreshToken != "" { o += " refresh_token:true" } + if len(s.Groups) > 0 { + o += fmt.Sprintf(" groups:%v", s.Groups) + } return o + "}" } @@ -233,7 +238,7 @@ func (s *SessionState) validate() error { } empty := new(SessionState) - if *s == *empty { + if reflect.DeepEqual(*s, *empty) { return errors.New("invalid empty session unmarshalled") } diff --git a/pkg/apis/sessions/session_state_test.go b/pkg/apis/sessions/session_state_test.go index 08216b2697..4a91bdecc1 100644 --- a/pkg/apis/sessions/session_state_test.go +++ b/pkg/apis/sessions/session_state_test.go @@ -8,7 +8,7 @@ import ( "testing" "time" - "github.com/oauth2-proxy/oauth2-proxy/pkg/encryption" + "github.com/oauth2-proxy/oauth2-proxy/v7/pkg/encryption" . "github.com/onsi/gomega" "github.com/stretchr/testify/assert" ) @@ -186,6 +186,17 @@ func TestEncodeAndDecodeSessionState(t *testing.T) { IDToken: "IDToken.12349871293847fdsaihf9238h4f91h8fr.1349f831y98fd7", ExpiresOn: &expires, }, + "With groups": { + Email: "username@example.com", + User: "username", + PreferredUsername: "preferred.username", + AccessToken: "AccessToken.12349871293847fdsaihf9238h4f91h8fr.1349f831y98fd7", + IDToken: "IDToken.12349871293847fdsaihf9238h4f91h8fr.1349f831y98fd7", + CreatedAt: &created, + ExpiresOn: &expires, + RefreshToken: "RefreshToken.12349871293847fdsaihf9238h4f91h8fr.1349f831y98fd7", + Groups: []string{"group-a", "group-b"}, + }, } for _, secretSize := range []int{16, 24, 32} { diff --git a/pkg/authentication/basic/basic_suite_test.go b/pkg/authentication/basic/basic_suite_test.go index 4d5fa806b0..6ba23fc322 100644 --- a/pkg/authentication/basic/basic_suite_test.go +++ b/pkg/authentication/basic/basic_suite_test.go @@ -3,7 +3,7 @@ package basic import ( "testing" - "github.com/oauth2-proxy/oauth2-proxy/pkg/logger" + "github.com/oauth2-proxy/oauth2-proxy/v7/pkg/logger" . "github.com/onsi/ginkgo" . "github.com/onsi/gomega" ) diff --git a/pkg/authentication/basic/htpasswd.go b/pkg/authentication/basic/htpasswd.go index a0ec9c2295..47a1dd3f14 100644 --- a/pkg/authentication/basic/htpasswd.go +++ b/pkg/authentication/basic/htpasswd.go @@ -9,7 +9,7 @@ import ( "io" "os" - "github.com/oauth2-proxy/oauth2-proxy/pkg/logger" + "github.com/oauth2-proxy/oauth2-proxy/v7/pkg/logger" "golang.org/x/crypto/bcrypt" ) diff --git a/pkg/cookies/cookies.go b/pkg/cookies/cookies.go index 0d4996399d..9b6dc03d4d 100644 --- a/pkg/cookies/cookies.go +++ b/pkg/cookies/cookies.go @@ -7,9 +7,9 @@ import ( "strings" "time" - "github.com/oauth2-proxy/oauth2-proxy/pkg/apis/options" - "github.com/oauth2-proxy/oauth2-proxy/pkg/logger" - "github.com/oauth2-proxy/oauth2-proxy/pkg/util" + "github.com/oauth2-proxy/oauth2-proxy/v7/pkg/apis/options" + "github.com/oauth2-proxy/oauth2-proxy/v7/pkg/logger" + "github.com/oauth2-proxy/oauth2-proxy/v7/pkg/util" ) // MakeCookie constructs a cookie from the given parameters, diff --git a/pkg/encryption/utils.go b/pkg/encryption/utils.go index 269a89c6af..c9d19249d2 100644 --- a/pkg/encryption/utils.go +++ b/pkg/encryption/utils.go @@ -2,8 +2,6 @@ package encryption import ( "crypto/hmac" - // TODO (@NickMeves): Remove SHA1 signed cookie support in V7 - "crypto/sha1" // #nosec G505 "crypto/sha256" "encoding/base64" "fmt" @@ -95,16 +93,7 @@ func checkSignature(signature string, args ...string) bool { if err != nil { return false } - if checkHmac(signature, checkSig) { - return true - } - - // TODO (@NickMeves): Remove SHA1 signed cookie support in V7 - legacySig, err := cookieSignature(sha1.New, args...) - if err != nil { - return false - } - return checkHmac(signature, legacySig) + return checkHmac(signature, checkSig) } func checkHmac(input, expected string) bool { diff --git a/pkg/encryption/utils_test.go b/pkg/encryption/utils_test.go index 162c64ce80..2500d4ab15 100644 --- a/pkg/encryption/utils_test.go +++ b/pkg/encryption/utils_test.go @@ -94,8 +94,8 @@ func TestSignAndValidate(t *testing.T) { assert.NoError(t, err) assert.True(t, checkSignature(sha256sig, seed, key, value, epoch)) - // This should be switched to False after fully deprecating SHA1 - assert.True(t, checkSignature(sha1sig, seed, key, value, epoch)) + // We don't validate legacy SHA1 signatures anymore + assert.False(t, checkSignature(sha1sig, seed, key, value, epoch)) assert.False(t, checkSignature(sha256sig, seed, key, "tampered", epoch)) assert.False(t, checkSignature(sha1sig, seed, key, "tampered", epoch)) diff --git a/pkg/ip/realclientip.go b/pkg/ip/realclientip.go index b82a3c6ebd..dd99ed5578 100644 --- a/pkg/ip/realclientip.go +++ b/pkg/ip/realclientip.go @@ -6,7 +6,7 @@ import ( "net/http" "strings" - ipapi "github.com/oauth2-proxy/oauth2-proxy/pkg/apis/ip" + ipapi "github.com/oauth2-proxy/oauth2-proxy/v7/pkg/apis/ip" ) func GetRealClientIPParser(headerKey string) (ipapi.RealClientIPParser, error) { diff --git a/pkg/ip/realclientip_test.go b/pkg/ip/realclientip_test.go index b24b733b02..810ce7e0e7 100644 --- a/pkg/ip/realclientip_test.go +++ b/pkg/ip/realclientip_test.go @@ -6,7 +6,7 @@ import ( "reflect" "testing" - ipapi "github.com/oauth2-proxy/oauth2-proxy/pkg/apis/ip" + ipapi "github.com/oauth2-proxy/oauth2-proxy/v7/pkg/apis/ip" "github.com/stretchr/testify/assert" ) diff --git a/pkg/logger/logger.go b/pkg/logger/logger.go index 901d0a0df7..3d1dced489 100644 --- a/pkg/logger/logger.go +++ b/pkg/logger/logger.go @@ -12,7 +12,7 @@ import ( "text/template" "time" - "github.com/oauth2-proxy/oauth2-proxy/pkg/util" + "github.com/oauth2-proxy/oauth2-proxy/v7/pkg/util" ) // AuthStatus defines the different types of auth logging that occur diff --git a/pkg/middleware/basic_session.go b/pkg/middleware/basic_session.go index 0226415509..5a7b77f912 100644 --- a/pkg/middleware/basic_session.go +++ b/pkg/middleware/basic_session.go @@ -5,9 +5,9 @@ import ( "net/http" "github.com/justinas/alice" - sessionsapi "github.com/oauth2-proxy/oauth2-proxy/pkg/apis/sessions" - "github.com/oauth2-proxy/oauth2-proxy/pkg/authentication/basic" - "github.com/oauth2-proxy/oauth2-proxy/pkg/logger" + sessionsapi "github.com/oauth2-proxy/oauth2-proxy/v7/pkg/apis/sessions" + "github.com/oauth2-proxy/oauth2-proxy/v7/pkg/authentication/basic" + "github.com/oauth2-proxy/oauth2-proxy/v7/pkg/logger" ) func NewBasicAuthSessionLoader(validator basic.Validator) alice.Constructor { diff --git a/pkg/middleware/basic_session_test.go b/pkg/middleware/basic_session_test.go index 109b09a5d0..35e4f8047a 100644 --- a/pkg/middleware/basic_session_test.go +++ b/pkg/middleware/basic_session_test.go @@ -6,8 +6,8 @@ import ( "net/http" "net/http/httptest" - middlewareapi "github.com/oauth2-proxy/oauth2-proxy/pkg/apis/middleware" - sessionsapi "github.com/oauth2-proxy/oauth2-proxy/pkg/apis/sessions" + middlewareapi "github.com/oauth2-proxy/oauth2-proxy/v7/pkg/apis/middleware" + sessionsapi "github.com/oauth2-proxy/oauth2-proxy/v7/pkg/apis/sessions" . "github.com/onsi/ginkgo" . "github.com/onsi/ginkgo/extensions/table" . "github.com/onsi/gomega" diff --git a/pkg/middleware/jwt_session.go b/pkg/middleware/jwt_session.go index 3f17296d06..024a45acc2 100644 --- a/pkg/middleware/jwt_session.go +++ b/pkg/middleware/jwt_session.go @@ -8,9 +8,9 @@ import ( "github.com/coreos/go-oidc" "github.com/justinas/alice" - middlewareapi "github.com/oauth2-proxy/oauth2-proxy/pkg/apis/middleware" - sessionsapi "github.com/oauth2-proxy/oauth2-proxy/pkg/apis/sessions" - "github.com/oauth2-proxy/oauth2-proxy/pkg/logger" + middlewareapi "github.com/oauth2-proxy/oauth2-proxy/v7/pkg/apis/middleware" + sessionsapi "github.com/oauth2-proxy/oauth2-proxy/v7/pkg/apis/sessions" + "github.com/oauth2-proxy/oauth2-proxy/v7/pkg/logger" ) const jwtRegexFormat = `^eyJ[a-zA-Z0-9_-]*\.eyJ[a-zA-Z0-9_-]*\.[a-zA-Z0-9_-]+$` diff --git a/pkg/middleware/jwt_session_test.go b/pkg/middleware/jwt_session_test.go index 5148ad2862..b950373186 100644 --- a/pkg/middleware/jwt_session_test.go +++ b/pkg/middleware/jwt_session_test.go @@ -15,8 +15,8 @@ import ( "github.com/coreos/go-oidc" "github.com/dgrijalva/jwt-go" - middlewareapi "github.com/oauth2-proxy/oauth2-proxy/pkg/apis/middleware" - sessionsapi "github.com/oauth2-proxy/oauth2-proxy/pkg/apis/sessions" + middlewareapi "github.com/oauth2-proxy/oauth2-proxy/v7/pkg/apis/middleware" + sessionsapi "github.com/oauth2-proxy/oauth2-proxy/v7/pkg/apis/sessions" . "github.com/onsi/ginkgo" . "github.com/onsi/ginkgo/extensions/table" . "github.com/onsi/gomega" diff --git a/pkg/middleware/middleware_suite_test.go b/pkg/middleware/middleware_suite_test.go index 204a979819..1a0d2f141d 100644 --- a/pkg/middleware/middleware_suite_test.go +++ b/pkg/middleware/middleware_suite_test.go @@ -4,7 +4,7 @@ import ( "net/http" "testing" - "github.com/oauth2-proxy/oauth2-proxy/pkg/logger" + "github.com/oauth2-proxy/oauth2-proxy/v7/pkg/logger" . "github.com/onsi/ginkgo" . "github.com/onsi/gomega" ) diff --git a/pkg/middleware/redirect_to_https.go b/pkg/middleware/redirect_to_https.go index 691a0565ff..18b4b967b6 100644 --- a/pkg/middleware/redirect_to_https.go +++ b/pkg/middleware/redirect_to_https.go @@ -7,7 +7,7 @@ import ( "strings" "github.com/justinas/alice" - "github.com/oauth2-proxy/oauth2-proxy/pkg/util" + "github.com/oauth2-proxy/oauth2-proxy/v7/pkg/util" ) const httpsScheme = "https" diff --git a/pkg/middleware/scope.go b/pkg/middleware/scope.go index d5925ad4ed..88719310fc 100644 --- a/pkg/middleware/scope.go +++ b/pkg/middleware/scope.go @@ -5,7 +5,7 @@ import ( "net/http" "github.com/justinas/alice" - middlewareapi "github.com/oauth2-proxy/oauth2-proxy/pkg/apis/middleware" + middlewareapi "github.com/oauth2-proxy/oauth2-proxy/v7/pkg/apis/middleware" ) type scopeKey string diff --git a/pkg/middleware/scope_test.go b/pkg/middleware/scope_test.go index 5a998bb030..e9533a8dcd 100644 --- a/pkg/middleware/scope_test.go +++ b/pkg/middleware/scope_test.go @@ -5,7 +5,7 @@ import ( "net/http" "net/http/httptest" - middlewareapi "github.com/oauth2-proxy/oauth2-proxy/pkg/apis/middleware" + middlewareapi "github.com/oauth2-proxy/oauth2-proxy/v7/pkg/apis/middleware" . "github.com/onsi/ginkgo" . "github.com/onsi/gomega" ) diff --git a/pkg/middleware/stored_session.go b/pkg/middleware/stored_session.go index da6de96dc8..6d86e61375 100644 --- a/pkg/middleware/stored_session.go +++ b/pkg/middleware/stored_session.go @@ -8,8 +8,8 @@ import ( "time" "github.com/justinas/alice" - sessionsapi "github.com/oauth2-proxy/oauth2-proxy/pkg/apis/sessions" - "github.com/oauth2-proxy/oauth2-proxy/pkg/logger" + sessionsapi "github.com/oauth2-proxy/oauth2-proxy/v7/pkg/apis/sessions" + "github.com/oauth2-proxy/oauth2-proxy/v7/pkg/logger" ) // StoredSessionLoaderOptions cotnains all of the requirements to construct diff --git a/pkg/middleware/stored_session_test.go b/pkg/middleware/stored_session_test.go index 1721b3097a..89eadc5d85 100644 --- a/pkg/middleware/stored_session_test.go +++ b/pkg/middleware/stored_session_test.go @@ -8,8 +8,8 @@ import ( "net/http/httptest" "time" - middlewareapi "github.com/oauth2-proxy/oauth2-proxy/pkg/apis/middleware" - sessionsapi "github.com/oauth2-proxy/oauth2-proxy/pkg/apis/sessions" + middlewareapi "github.com/oauth2-proxy/oauth2-proxy/v7/pkg/apis/middleware" + sessionsapi "github.com/oauth2-proxy/oauth2-proxy/v7/pkg/apis/sessions" . "github.com/onsi/ginkgo" . "github.com/onsi/ginkgo/extensions/table" . "github.com/onsi/gomega" diff --git a/pkg/requests/requests_suite_test.go b/pkg/requests/requests_suite_test.go index 83da733ac9..54383ffac6 100644 --- a/pkg/requests/requests_suite_test.go +++ b/pkg/requests/requests_suite_test.go @@ -9,7 +9,7 @@ import ( "net/http/httptest" "testing" - "github.com/oauth2-proxy/oauth2-proxy/pkg/logger" + "github.com/oauth2-proxy/oauth2-proxy/v7/pkg/logger" . "github.com/onsi/ginkgo" . "github.com/onsi/gomega" ) diff --git a/pkg/sessions/cookie/session_store.go b/pkg/sessions/cookie/session_store.go index 084193344a..461e08ea66 100644 --- a/pkg/sessions/cookie/session_store.go +++ b/pkg/sessions/cookie/session_store.go @@ -8,11 +8,11 @@ import ( "strings" "time" - "github.com/oauth2-proxy/oauth2-proxy/pkg/apis/options" - "github.com/oauth2-proxy/oauth2-proxy/pkg/apis/sessions" - pkgcookies "github.com/oauth2-proxy/oauth2-proxy/pkg/cookies" - "github.com/oauth2-proxy/oauth2-proxy/pkg/encryption" - "github.com/oauth2-proxy/oauth2-proxy/pkg/logger" + "github.com/oauth2-proxy/oauth2-proxy/v7/pkg/apis/options" + "github.com/oauth2-proxy/oauth2-proxy/v7/pkg/apis/sessions" + pkgcookies "github.com/oauth2-proxy/oauth2-proxy/v7/pkg/cookies" + "github.com/oauth2-proxy/oauth2-proxy/v7/pkg/encryption" + "github.com/oauth2-proxy/oauth2-proxy/v7/pkg/logger" ) const ( diff --git a/pkg/sessions/cookie/session_store_test.go b/pkg/sessions/cookie/session_store_test.go index a2670c2c7d..5ef9eff1a1 100644 --- a/pkg/sessions/cookie/session_store_test.go +++ b/pkg/sessions/cookie/session_store_test.go @@ -8,10 +8,10 @@ import ( "testing" "time" - "github.com/oauth2-proxy/oauth2-proxy/pkg/apis/options" - sessionsapi "github.com/oauth2-proxy/oauth2-proxy/pkg/apis/sessions" - "github.com/oauth2-proxy/oauth2-proxy/pkg/logger" - "github.com/oauth2-proxy/oauth2-proxy/pkg/sessions/tests" + "github.com/oauth2-proxy/oauth2-proxy/v7/pkg/apis/options" + sessionsapi "github.com/oauth2-proxy/oauth2-proxy/v7/pkg/apis/sessions" + "github.com/oauth2-proxy/oauth2-proxy/v7/pkg/logger" + "github.com/oauth2-proxy/oauth2-proxy/v7/pkg/sessions/tests" . "github.com/onsi/ginkgo" . "github.com/onsi/gomega" "github.com/stretchr/testify/assert" diff --git a/pkg/sessions/persistence/manager.go b/pkg/sessions/persistence/manager.go index 4697ad21ad..fc621a81ad 100644 --- a/pkg/sessions/persistence/manager.go +++ b/pkg/sessions/persistence/manager.go @@ -5,8 +5,8 @@ import ( "net/http" "time" - "github.com/oauth2-proxy/oauth2-proxy/pkg/apis/options" - "github.com/oauth2-proxy/oauth2-proxy/pkg/apis/sessions" + "github.com/oauth2-proxy/oauth2-proxy/v7/pkg/apis/options" + "github.com/oauth2-proxy/oauth2-proxy/v7/pkg/apis/sessions" ) // Manager wraps a Store and handles the implementation details of the diff --git a/pkg/sessions/persistence/manager_test.go b/pkg/sessions/persistence/manager_test.go index c41f246bda..791595bd6c 100644 --- a/pkg/sessions/persistence/manager_test.go +++ b/pkg/sessions/persistence/manager_test.go @@ -3,9 +3,9 @@ package persistence import ( "time" - "github.com/oauth2-proxy/oauth2-proxy/pkg/apis/options" - sessionsapi "github.com/oauth2-proxy/oauth2-proxy/pkg/apis/sessions" - "github.com/oauth2-proxy/oauth2-proxy/pkg/sessions/tests" + "github.com/oauth2-proxy/oauth2-proxy/v7/pkg/apis/options" + sessionsapi "github.com/oauth2-proxy/oauth2-proxy/v7/pkg/apis/sessions" + "github.com/oauth2-proxy/oauth2-proxy/v7/pkg/sessions/tests" . "github.com/onsi/ginkgo" ) diff --git a/pkg/sessions/persistence/persistence_suite_test.go b/pkg/sessions/persistence/persistence_suite_test.go index 34b443d613..57ccc40c43 100644 --- a/pkg/sessions/persistence/persistence_suite_test.go +++ b/pkg/sessions/persistence/persistence_suite_test.go @@ -3,7 +3,7 @@ package persistence import ( "testing" - "github.com/oauth2-proxy/oauth2-proxy/pkg/logger" + "github.com/oauth2-proxy/oauth2-proxy/v7/pkg/logger" . "github.com/onsi/ginkgo" . "github.com/onsi/gomega" ) diff --git a/pkg/sessions/persistence/ticket.go b/pkg/sessions/persistence/ticket.go index abb786145d..eb3aafc498 100644 --- a/pkg/sessions/persistence/ticket.go +++ b/pkg/sessions/persistence/ticket.go @@ -13,10 +13,10 @@ import ( "strings" "time" - "github.com/oauth2-proxy/oauth2-proxy/pkg/apis/options" - "github.com/oauth2-proxy/oauth2-proxy/pkg/apis/sessions" - "github.com/oauth2-proxy/oauth2-proxy/pkg/cookies" - "github.com/oauth2-proxy/oauth2-proxy/pkg/encryption" + "github.com/oauth2-proxy/oauth2-proxy/v7/pkg/apis/options" + "github.com/oauth2-proxy/oauth2-proxy/v7/pkg/apis/sessions" + "github.com/oauth2-proxy/oauth2-proxy/v7/pkg/cookies" + "github.com/oauth2-proxy/oauth2-proxy/v7/pkg/encryption" ) // saveFunc performs a persistent store's save functionality using diff --git a/pkg/sessions/persistence/ticket_test.go b/pkg/sessions/persistence/ticket_test.go index 2e9b8bfcc1..0a121bb071 100644 --- a/pkg/sessions/persistence/ticket_test.go +++ b/pkg/sessions/persistence/ticket_test.go @@ -11,8 +11,8 @@ import ( "testing" "time" - "github.com/oauth2-proxy/oauth2-proxy/pkg/apis/options" - "github.com/oauth2-proxy/oauth2-proxy/pkg/apis/sessions" + "github.com/oauth2-proxy/oauth2-proxy/v7/pkg/apis/options" + "github.com/oauth2-proxy/oauth2-proxy/v7/pkg/apis/sessions" . "github.com/onsi/ginkgo" . "github.com/onsi/ginkgo/extensions/table" . "github.com/onsi/gomega" diff --git a/pkg/sessions/redis/redis_store.go b/pkg/sessions/redis/redis_store.go index 10d99347b8..ebd9ad1987 100644 --- a/pkg/sessions/redis/redis_store.go +++ b/pkg/sessions/redis/redis_store.go @@ -8,10 +8,10 @@ import ( "time" "github.com/go-redis/redis/v7" - "github.com/oauth2-proxy/oauth2-proxy/pkg/apis/options" - "github.com/oauth2-proxy/oauth2-proxy/pkg/apis/sessions" - "github.com/oauth2-proxy/oauth2-proxy/pkg/logger" - "github.com/oauth2-proxy/oauth2-proxy/pkg/sessions/persistence" + "github.com/oauth2-proxy/oauth2-proxy/v7/pkg/apis/options" + "github.com/oauth2-proxy/oauth2-proxy/v7/pkg/apis/sessions" + "github.com/oauth2-proxy/oauth2-proxy/v7/pkg/logger" + "github.com/oauth2-proxy/oauth2-proxy/v7/pkg/sessions/persistence" ) // SessionStore is an implementation of the persistence.Store @@ -23,7 +23,7 @@ type SessionStore struct { // NewRedisSessionStore initialises a new instance of the SessionStore and wraps // it in a persistence.Manager func NewRedisSessionStore(opts *options.SessionOptions, cookieOpts *options.Cookie) (sessions.SessionStore, error) { - client, err := newRedisClient(opts.Redis) + client, err := NewRedisClient(opts.Redis) if err != nil { return nil, fmt.Errorf("error constructing redis client: %v", err) } @@ -64,9 +64,9 @@ func (store *SessionStore) Clear(ctx context.Context, key string) error { return nil } -// newRedisClient makes a redis.Client (either standalone, sentinel aware, or +// NewRedisClient makes a redis.Client (either standalone, sentinel aware, or // redis cluster) -func newRedisClient(opts options.RedisStoreOptions) (Client, error) { +func NewRedisClient(opts options.RedisStoreOptions) (Client, error) { if opts.UseSentinel && opts.UseCluster { return nil, fmt.Errorf("options redis-use-sentinel and redis-use-cluster are mutually exclusive") } diff --git a/pkg/sessions/redis/redis_store_test.go b/pkg/sessions/redis/redis_store_test.go index 12daaab65a..d34d007cc8 100644 --- a/pkg/sessions/redis/redis_store_test.go +++ b/pkg/sessions/redis/redis_store_test.go @@ -9,11 +9,11 @@ import ( "github.com/Bose/minisentinel" "github.com/alicebob/miniredis/v2" "github.com/go-redis/redis/v7" - "github.com/oauth2-proxy/oauth2-proxy/pkg/apis/options" - sessionsapi "github.com/oauth2-proxy/oauth2-proxy/pkg/apis/sessions" - "github.com/oauth2-proxy/oauth2-proxy/pkg/logger" - "github.com/oauth2-proxy/oauth2-proxy/pkg/sessions/persistence" - "github.com/oauth2-proxy/oauth2-proxy/pkg/sessions/tests" + "github.com/oauth2-proxy/oauth2-proxy/v7/pkg/apis/options" + sessionsapi "github.com/oauth2-proxy/oauth2-proxy/v7/pkg/apis/sessions" + "github.com/oauth2-proxy/oauth2-proxy/v7/pkg/logger" + "github.com/oauth2-proxy/oauth2-proxy/v7/pkg/sessions/persistence" + "github.com/oauth2-proxy/oauth2-proxy/v7/pkg/sessions/tests" . "github.com/onsi/ginkgo" . "github.com/onsi/gomega" ) diff --git a/pkg/sessions/session_store.go b/pkg/sessions/session_store.go index ba102af84b..3d4b8d9771 100644 --- a/pkg/sessions/session_store.go +++ b/pkg/sessions/session_store.go @@ -3,10 +3,10 @@ package sessions import ( "fmt" - "github.com/oauth2-proxy/oauth2-proxy/pkg/apis/options" - "github.com/oauth2-proxy/oauth2-proxy/pkg/apis/sessions" - "github.com/oauth2-proxy/oauth2-proxy/pkg/sessions/cookie" - "github.com/oauth2-proxy/oauth2-proxy/pkg/sessions/redis" + "github.com/oauth2-proxy/oauth2-proxy/v7/pkg/apis/options" + "github.com/oauth2-proxy/oauth2-proxy/v7/pkg/apis/sessions" + "github.com/oauth2-proxy/oauth2-proxy/v7/pkg/sessions/cookie" + "github.com/oauth2-proxy/oauth2-proxy/v7/pkg/sessions/redis" ) // NewSessionStore creates a SessionStore from the provided configuration diff --git a/pkg/sessions/session_store_test.go b/pkg/sessions/session_store_test.go index 2f8d599203..63dc56e5a4 100644 --- a/pkg/sessions/session_store_test.go +++ b/pkg/sessions/session_store_test.go @@ -6,12 +6,12 @@ import ( "testing" "time" - "github.com/oauth2-proxy/oauth2-proxy/pkg/apis/options" - "github.com/oauth2-proxy/oauth2-proxy/pkg/logger" - "github.com/oauth2-proxy/oauth2-proxy/pkg/sessions" - sessionscookie "github.com/oauth2-proxy/oauth2-proxy/pkg/sessions/cookie" - "github.com/oauth2-proxy/oauth2-proxy/pkg/sessions/persistence" - "github.com/oauth2-proxy/oauth2-proxy/pkg/sessions/redis" + "github.com/oauth2-proxy/oauth2-proxy/v7/pkg/apis/options" + "github.com/oauth2-proxy/oauth2-proxy/v7/pkg/logger" + "github.com/oauth2-proxy/oauth2-proxy/v7/pkg/sessions" + sessionscookie "github.com/oauth2-proxy/oauth2-proxy/v7/pkg/sessions/cookie" + "github.com/oauth2-proxy/oauth2-proxy/v7/pkg/sessions/persistence" + "github.com/oauth2-proxy/oauth2-proxy/v7/pkg/sessions/redis" . "github.com/onsi/ginkgo" . "github.com/onsi/gomega" ) diff --git a/pkg/sessions/tests/session_store_tests.go b/pkg/sessions/tests/session_store_tests.go index f23028f32a..df90c7f36f 100644 --- a/pkg/sessions/tests/session_store_tests.go +++ b/pkg/sessions/tests/session_store_tests.go @@ -8,10 +8,10 @@ import ( "strings" "time" - "github.com/oauth2-proxy/oauth2-proxy/pkg/apis/options" - sessionsapi "github.com/oauth2-proxy/oauth2-proxy/pkg/apis/sessions" - cookiesapi "github.com/oauth2-proxy/oauth2-proxy/pkg/cookies" - "github.com/oauth2-proxy/oauth2-proxy/pkg/encryption" + "github.com/oauth2-proxy/oauth2-proxy/v7/pkg/apis/options" + sessionsapi "github.com/oauth2-proxy/oauth2-proxy/v7/pkg/apis/sessions" + cookiesapi "github.com/oauth2-proxy/oauth2-proxy/v7/pkg/cookies" + "github.com/oauth2-proxy/oauth2-proxy/v7/pkg/encryption" . "github.com/onsi/ginkgo" . "github.com/onsi/gomega" ) diff --git a/pkg/upstream/http.go b/pkg/upstream/http.go index 833b139992..88c0afcdc0 100644 --- a/pkg/upstream/http.go +++ b/pkg/upstream/http.go @@ -9,7 +9,7 @@ import ( "time" "github.com/mbland/hmacauth" - "github.com/oauth2-proxy/oauth2-proxy/pkg/apis/options" + "github.com/oauth2-proxy/oauth2-proxy/v7/pkg/apis/options" "github.com/yhat/wsutil" ) diff --git a/pkg/upstream/http_test.go b/pkg/upstream/http_test.go index c49c0c35e2..8bfe90875b 100644 --- a/pkg/upstream/http_test.go +++ b/pkg/upstream/http_test.go @@ -13,7 +13,7 @@ import ( "strings" "time" - "github.com/oauth2-proxy/oauth2-proxy/pkg/apis/options" + "github.com/oauth2-proxy/oauth2-proxy/v7/pkg/apis/options" . "github.com/onsi/ginkgo" . "github.com/onsi/ginkgo/extensions/table" . "github.com/onsi/gomega" diff --git a/pkg/upstream/proxy.go b/pkg/upstream/proxy.go index 9197944b33..80d4b4d517 100644 --- a/pkg/upstream/proxy.go +++ b/pkg/upstream/proxy.go @@ -6,8 +6,8 @@ import ( "net/http" "net/url" - "github.com/oauth2-proxy/oauth2-proxy/pkg/apis/options" - "github.com/oauth2-proxy/oauth2-proxy/pkg/logger" + "github.com/oauth2-proxy/oauth2-proxy/v7/pkg/apis/options" + "github.com/oauth2-proxy/oauth2-proxy/v7/pkg/logger" ) // ProxyErrorHandler is a function that will be used to render error pages when diff --git a/pkg/upstream/proxy_test.go b/pkg/upstream/proxy_test.go index 945fb665bf..31b7bfa611 100644 --- a/pkg/upstream/proxy_test.go +++ b/pkg/upstream/proxy_test.go @@ -8,7 +8,7 @@ import ( "net/http" "net/http/httptest" - "github.com/oauth2-proxy/oauth2-proxy/pkg/apis/options" + "github.com/oauth2-proxy/oauth2-proxy/v7/pkg/apis/options" . "github.com/onsi/ginkgo" . "github.com/onsi/ginkgo/extensions/table" . "github.com/onsi/gomega" diff --git a/pkg/upstream/upstream_suite_test.go b/pkg/upstream/upstream_suite_test.go index 7d8c2ba47c..581a4cf2dd 100644 --- a/pkg/upstream/upstream_suite_test.go +++ b/pkg/upstream/upstream_suite_test.go @@ -11,7 +11,7 @@ import ( "path" "testing" - "github.com/oauth2-proxy/oauth2-proxy/pkg/logger" + "github.com/oauth2-proxy/oauth2-proxy/v7/pkg/logger" . "github.com/onsi/ginkgo" . "github.com/onsi/gomega" "golang.org/x/net/websocket" diff --git a/pkg/validation/cookie.go b/pkg/validation/cookie.go index 2d5a557af5..2984ac2e0c 100644 --- a/pkg/validation/cookie.go +++ b/pkg/validation/cookie.go @@ -5,8 +5,8 @@ import ( "net/http" "sort" - "github.com/oauth2-proxy/oauth2-proxy/pkg/apis/options" - "github.com/oauth2-proxy/oauth2-proxy/pkg/encryption" + "github.com/oauth2-proxy/oauth2-proxy/v7/pkg/apis/options" + "github.com/oauth2-proxy/oauth2-proxy/v7/pkg/encryption" ) func validateCookie(o options.Cookie) []string { diff --git a/pkg/validation/cookie_test.go b/pkg/validation/cookie_test.go index ac2e7951a6..b756daa8fa 100644 --- a/pkg/validation/cookie_test.go +++ b/pkg/validation/cookie_test.go @@ -5,7 +5,7 @@ import ( "testing" "time" - "github.com/oauth2-proxy/oauth2-proxy/pkg/apis/options" + "github.com/oauth2-proxy/oauth2-proxy/v7/pkg/apis/options" . "github.com/onsi/gomega" ) diff --git a/pkg/validation/logging.go b/pkg/validation/logging.go index 2ad7aba324..c291405c3b 100644 --- a/pkg/validation/logging.go +++ b/pkg/validation/logging.go @@ -3,8 +3,8 @@ package validation import ( "os" - "github.com/oauth2-proxy/oauth2-proxy/pkg/apis/options" - "github.com/oauth2-proxy/oauth2-proxy/pkg/logger" + "github.com/oauth2-proxy/oauth2-proxy/v7/pkg/apis/options" + "github.com/oauth2-proxy/oauth2-proxy/v7/pkg/logger" "gopkg.in/natefinch/lumberjack.v2" ) diff --git a/pkg/validation/options.go b/pkg/validation/options.go index f9325cf0f5..12631eb977 100644 --- a/pkg/validation/options.go +++ b/pkg/validation/options.go @@ -15,12 +15,12 @@ import ( "github.com/coreos/go-oidc" "github.com/dgrijalva/jwt-go" "github.com/mbland/hmacauth" - "github.com/oauth2-proxy/oauth2-proxy/pkg/apis/options" - "github.com/oauth2-proxy/oauth2-proxy/pkg/ip" - "github.com/oauth2-proxy/oauth2-proxy/pkg/logger" - "github.com/oauth2-proxy/oauth2-proxy/pkg/requests" - "github.com/oauth2-proxy/oauth2-proxy/pkg/util" - "github.com/oauth2-proxy/oauth2-proxy/providers" + "github.com/oauth2-proxy/oauth2-proxy/v7/pkg/apis/options" + "github.com/oauth2-proxy/oauth2-proxy/v7/pkg/ip" + "github.com/oauth2-proxy/oauth2-proxy/v7/pkg/logger" + "github.com/oauth2-proxy/oauth2-proxy/v7/pkg/requests" + "github.com/oauth2-proxy/oauth2-proxy/v7/pkg/util" + "github.com/oauth2-proxy/oauth2-proxy/v7/providers" ) // Validate checks that required options are set and validates those that they @@ -28,6 +28,7 @@ import ( func Validate(o *options.Options) error { msgs := validateCookie(o.Cookie) msgs = append(msgs, validateSessionCookieMinimal(o)...) + msgs = append(msgs, validateRedisSessionStore(o)...) if o.SSLInsecureSkipVerify { // InsecureSkipVerify is a configurable option we allow @@ -152,6 +153,10 @@ func Validate(o *options.Options) error { } if o.Scope == "" { o.Scope = "openid email profile" + + if len(o.AllowedGroups) > 0 { + o.Scope += " groups" + } } } @@ -279,6 +284,7 @@ func parseProviderInfo(o *options.Options, msgs []string) []string { case *providers.OIDCProvider: p.AllowUnverifiedEmail = o.InsecureOIDCAllowUnverifiedEmail p.UserIDClaim = o.UserIDClaim + p.GroupsClaim = o.OIDCGroupsClaim if o.GetOIDCVerifier() == nil { msgs = append(msgs, "oidc provider requires an oidc issuer URL") } else { diff --git a/pkg/validation/options_test.go b/pkg/validation/options_test.go index 8c9a892f08..1f418f82c8 100644 --- a/pkg/validation/options_test.go +++ b/pkg/validation/options_test.go @@ -10,7 +10,7 @@ import ( "testing" "time" - "github.com/oauth2-proxy/oauth2-proxy/pkg/apis/options" + "github.com/oauth2-proxy/oauth2-proxy/v7/pkg/apis/options" "github.com/stretchr/testify/assert" ) diff --git a/pkg/validation/sessions.go b/pkg/validation/sessions.go index db4ba9fddc..8cacd48bf2 100644 --- a/pkg/validation/sessions.go +++ b/pkg/validation/sessions.go @@ -1,9 +1,13 @@ package validation import ( + "context" + "fmt" "time" - "github.com/oauth2-proxy/oauth2-proxy/pkg/apis/options" + "github.com/oauth2-proxy/oauth2-proxy/v7/pkg/apis/options" + "github.com/oauth2-proxy/oauth2-proxy/v7/pkg/encryption" + "github.com/oauth2-proxy/oauth2-proxy/v7/pkg/sessions/redis" ) func validateSessionCookieMinimal(o *options.Options) []string { @@ -30,3 +34,50 @@ func validateSessionCookieMinimal(o *options.Options) []string { } return msgs } + +// validateRedisSessionStore builds a Redis Client from the options and +// attempts to connect, Set, Get and Del a random health check key +func validateRedisSessionStore(o *options.Options) []string { + if o.Session.Type != options.RedisSessionStoreType { + return []string{} + } + + client, err := redis.NewRedisClient(o.Session.Redis) + if err != nil { + return []string{fmt.Sprintf("unable to initialize a redis client: %v", err)} + } + + nonce, err := encryption.Nonce() + if err != nil { + return []string{fmt.Sprintf("unable to generate a redis initialization test key: %v", err)} + } + + key := fmt.Sprintf("%s-healthcheck-%s", o.Cookie.Name, nonce) + return sendRedisConnectionTest(client, key, nonce) +} + +func sendRedisConnectionTest(client redis.Client, key string, val string) []string { + msgs := []string{} + ctx := context.Background() + + err := client.Set(ctx, key, []byte(val), time.Duration(60)*time.Second) + if err != nil { + msgs = append(msgs, fmt.Sprintf("unable to set a redis initialization key: %v", err)) + } else { + gval, err := client.Get(ctx, key) + if err != nil { + msgs = append(msgs, + fmt.Sprintf("unable to retrieve redis initialization key: %v", err)) + } + if string(gval) != val { + msgs = append(msgs, + "the retrieved redis initialization key did not match the value we set") + } + } + + err = client.Del(ctx, key) + if err != nil { + msgs = append(msgs, fmt.Sprintf("unable to delete the redis initialization key: %v", err)) + } + return msgs +} diff --git a/pkg/validation/sessions_test.go b/pkg/validation/sessions_test.go index a6ffdbd0e3..68fac8e108 100644 --- a/pkg/validation/sessions_test.go +++ b/pkg/validation/sessions_test.go @@ -1,14 +1,17 @@ package validation import ( - "testing" "time" - "github.com/oauth2-proxy/oauth2-proxy/pkg/apis/options" + "github.com/Bose/minisentinel" + "github.com/alicebob/miniredis/v2" + "github.com/oauth2-proxy/oauth2-proxy/v7/pkg/apis/options" + . "github.com/onsi/ginkgo" + . "github.com/onsi/ginkgo/extensions/table" . "github.com/onsi/gomega" ) -func Test_validateSessionCookieMinimal(t *testing.T) { +var _ = Describe("Sessions", func() { const ( passAuthorizationMsg = "pass_authorization_header requires oauth tokens in sessions. session_cookie_minimal cannot be set" setAuthorizationMsg = "set_authorization_header requires oauth tokens in sessions. session_cookie_minimal cannot be set" @@ -16,11 +19,16 @@ func Test_validateSessionCookieMinimal(t *testing.T) { cookieRefreshMsg = "cookie_refresh > 0 requires oauth tokens in sessions. session_cookie_minimal cannot be set" ) - testCases := map[string]struct { + type cookieMinimalTableInput struct { opts *options.Options errStrings []string - }{ - "No minimal cookie session": { + } + + DescribeTable("validateSessionCookieMinimal", + func(o *cookieMinimalTableInput) { + Expect(validateSessionCookieMinimal(o.opts)).To(ConsistOf(o.errStrings)) + }, + Entry("No minimal cookie session", &cookieMinimalTableInput{ opts: &options.Options{ Session: options.SessionOptions{ Cookie: options.CookieStoreOptions{ @@ -29,8 +37,8 @@ func Test_validateSessionCookieMinimal(t *testing.T) { }, }, errStrings: []string{}, - }, - "No minimal cookie session & passAuthorization": { + }), + Entry("No minimal cookie session & passAuthorization", &cookieMinimalTableInput{ opts: &options.Options{ Session: options.SessionOptions{ Cookie: options.CookieStoreOptions{ @@ -40,8 +48,8 @@ func Test_validateSessionCookieMinimal(t *testing.T) { PassAuthorization: true, }, errStrings: []string{}, - }, - "Minimal cookie session no conflicts": { + }), + Entry("Minimal cookie session no conflicts", &cookieMinimalTableInput{ opts: &options.Options{ Session: options.SessionOptions{ Cookie: options.CookieStoreOptions{ @@ -50,8 +58,8 @@ func Test_validateSessionCookieMinimal(t *testing.T) { }, }, errStrings: []string{}, - }, - "PassAuthorization conflict": { + }), + Entry("PassAuthorization conflict", &cookieMinimalTableInput{ opts: &options.Options{ Session: options.SessionOptions{ Cookie: options.CookieStoreOptions{ @@ -61,8 +69,8 @@ func Test_validateSessionCookieMinimal(t *testing.T) { PassAuthorization: true, }, errStrings: []string{passAuthorizationMsg}, - }, - "SetAuthorization conflict": { + }), + Entry("SetAuthorization conflict", &cookieMinimalTableInput{ opts: &options.Options{ Session: options.SessionOptions{ Cookie: options.CookieStoreOptions{ @@ -72,8 +80,8 @@ func Test_validateSessionCookieMinimal(t *testing.T) { SetAuthorization: true, }, errStrings: []string{setAuthorizationMsg}, - }, - "PassAccessToken conflict": { + }), + Entry("PassAccessToken conflict", &cookieMinimalTableInput{ opts: &options.Options{ Session: options.SessionOptions{ Cookie: options.CookieStoreOptions{ @@ -83,8 +91,8 @@ func Test_validateSessionCookieMinimal(t *testing.T) { PassAccessToken: true, }, errStrings: []string{passAccessTokenMsg}, - }, - "CookieRefresh conflict": { + }), + Entry("CookieRefresh conflict", &cookieMinimalTableInput{ opts: &options.Options{ Cookie: options.Cookie{ Refresh: time.Hour, @@ -96,8 +104,8 @@ func Test_validateSessionCookieMinimal(t *testing.T) { }, }, errStrings: []string{cookieRefreshMsg}, - }, - "Multiple conflicts": { + }), + Entry("Multiple conflicts", &cookieMinimalTableInput{ opts: &options.Options{ Session: options.SessionOptions{ Cookie: options.CookieStoreOptions{ @@ -108,14 +116,228 @@ func Test_validateSessionCookieMinimal(t *testing.T) { PassAccessToken: true, }, errStrings: []string{passAuthorizationMsg, passAccessTokenMsg}, - }, - } + }), + ) - for testName, tc := range testCases { - t.Run(testName, func(t *testing.T) { - errStrings := validateSessionCookieMinimal(tc.opts) - g := NewWithT(t) - g.Expect(errStrings).To(ConsistOf(tc.errStrings)) - }) + const ( + clusterAndSentinelMsg = "unable to initialize a redis client: options redis-use-sentinel and redis-use-cluster are mutually exclusive" + parseWrongSchemeMsg = "unable to initialize a redis client: unable to parse redis url: invalid redis URL scheme: https" + parseWrongFormatMsg = "unable to initialize a redis client: unable to parse redis url: invalid redis database number: \"wrong\"" + invalidPasswordSetMsg = "unable to set a redis initialization key: WRONGPASS invalid username-password pair" + invalidPasswordDelMsg = "unable to delete the redis initialization key: WRONGPASS invalid username-password pair" + unreachableRedisSetMsg = "unable to set a redis initialization key: dial tcp 127.0.0.1:65535: connect: connection refused" + unreachableRedisDelMsg = "unable to delete the redis initialization key: dial tcp 127.0.0.1:65535: connect: connection refused" + unreachableSentinelSetMsg = "unable to set a redis initialization key: redis: all sentinels are unreachable" + unrechableSentinelDelMsg = "unable to delete the redis initialization key: redis: all sentinels are unreachable" + ) + + type redisStoreTableInput struct { + // miniredis setup details + password string + useSentinel bool + setAddr bool + setSentinelAddr bool + setMasterName bool + + opts *options.Options + errStrings []string } -} + + DescribeTable("validateRedisSessionStore", + func(o *redisStoreTableInput) { + mr, err := miniredis.Run() + Expect(err).ToNot(HaveOccurred()) + mr.RequireAuth(o.password) + defer mr.Close() + + if o.setAddr && !o.useSentinel { + o.opts.Session.Redis.ConnectionURL = "redis://" + mr.Addr() + } + + if o.useSentinel { + ms := minisentinel.NewSentinel(mr) + Expect(ms.Start()).To(Succeed()) + defer ms.Close() + + if o.setSentinelAddr { + o.opts.Session.Redis.SentinelConnectionURLs = []string{"redis://" + ms.Addr()} + } + if o.setMasterName { + o.opts.Session.Redis.SentinelMasterName = ms.MasterInfo().Name + } + } + + Expect(validateRedisSessionStore(o.opts)).To(ConsistOf(o.errStrings)) + }, + Entry("cookie sessions are skipped", &redisStoreTableInput{ + opts: &options.Options{ + Session: options.SessionOptions{ + Type: options.CookieSessionStoreType, + }, + }, + errStrings: []string{}, + }), + Entry("connect successfully to pure redis", &redisStoreTableInput{ + setAddr: true, + + opts: &options.Options{ + Session: options.SessionOptions{ + Type: options.RedisSessionStoreType, + }, + }, + errStrings: []string{}, + }), + Entry("failed redis connection with wrong address", &redisStoreTableInput{ + opts: &options.Options{ + Session: options.SessionOptions{ + Type: options.RedisSessionStoreType, + Redis: options.RedisStoreOptions{ + ConnectionURL: "redis://127.0.0.1:65535", + }, + }, + }, + errStrings: []string{unreachableRedisSetMsg, unreachableRedisDelMsg}, + }), + Entry("fail to parse an invalid connection URL with wrong scheme", &redisStoreTableInput{ + opts: &options.Options{ + Session: options.SessionOptions{ + Type: options.RedisSessionStoreType, + Redis: options.RedisStoreOptions{ + ConnectionURL: "https://example.com", + }, + }, + }, + errStrings: []string{parseWrongSchemeMsg}, + }), + Entry("fail to parse an invalid connection URL with invalid format", &redisStoreTableInput{ + opts: &options.Options{ + Session: options.SessionOptions{ + Type: options.RedisSessionStoreType, + Redis: options.RedisStoreOptions{ + ConnectionURL: "redis://127.0.0.1:6379/wrong", + }, + }, + }, + errStrings: []string{parseWrongFormatMsg}, + }), + Entry("connect successfully to pure redis with password", &redisStoreTableInput{ + password: "abcdef123", + setAddr: true, + + opts: &options.Options{ + Session: options.SessionOptions{ + Type: options.RedisSessionStoreType, + Redis: options.RedisStoreOptions{ + Password: "abcdef123", + }, + }, + }, + errStrings: []string{}, + }), + Entry("failed connection with wrong password", &redisStoreTableInput{ + password: "abcdef123", + setAddr: true, + + opts: &options.Options{ + Session: options.SessionOptions{ + Type: options.RedisSessionStoreType, + Redis: options.RedisStoreOptions{ + Password: "zyxwtuv987", + }, + }, + }, + errStrings: []string{invalidPasswordSetMsg, invalidPasswordDelMsg}, + }), + Entry("connect successfully to sentinel redis", &redisStoreTableInput{ + useSentinel: true, + setSentinelAddr: true, + setMasterName: true, + + opts: &options.Options{ + Session: options.SessionOptions{ + Type: options.RedisSessionStoreType, + Redis: options.RedisStoreOptions{ + UseSentinel: true, + }, + }, + }, + errStrings: []string{}, + }), + Entry("connect successfully to sentinel redis with password", &redisStoreTableInput{ + password: "abcdef123", + useSentinel: true, + setSentinelAddr: true, + setMasterName: true, + + opts: &options.Options{ + Session: options.SessionOptions{ + Type: options.RedisSessionStoreType, + Redis: options.RedisStoreOptions{ + Password: "abcdef123", + UseSentinel: true, + }, + }, + }, + errStrings: []string{}, + }), + Entry("failed connection to sentinel redis with wrong password", &redisStoreTableInput{ + password: "abcdef123", + useSentinel: true, + setSentinelAddr: true, + setMasterName: true, + + opts: &options.Options{ + Session: options.SessionOptions{ + Type: options.RedisSessionStoreType, + Redis: options.RedisStoreOptions{ + Password: "zyxwtuv987", + UseSentinel: true, + }, + }, + }, + errStrings: []string{invalidPasswordSetMsg, invalidPasswordDelMsg}, + }), + Entry("failed connection to sentinel redis with wrong master name", &redisStoreTableInput{ + useSentinel: true, + setSentinelAddr: true, + + opts: &options.Options{ + Session: options.SessionOptions{ + Type: options.RedisSessionStoreType, + Redis: options.RedisStoreOptions{ + UseSentinel: true, + SentinelMasterName: "WRONG", + }, + }, + }, + errStrings: []string{unreachableSentinelSetMsg, unrechableSentinelDelMsg}, + }), + Entry("failed connection to sentinel redis with wrong address", &redisStoreTableInput{ + useSentinel: true, + setMasterName: true, + + opts: &options.Options{ + Session: options.SessionOptions{ + Type: options.RedisSessionStoreType, + Redis: options.RedisStoreOptions{ + UseSentinel: true, + SentinelConnectionURLs: []string{"redis://127.0.0.1:65535"}, + }, + }, + }, + errStrings: []string{unreachableSentinelSetMsg, unrechableSentinelDelMsg}, + }), + Entry("sentinel and cluster both enabled fails", &redisStoreTableInput{ + opts: &options.Options{ + Session: options.SessionOptions{ + Type: options.RedisSessionStoreType, + Redis: options.RedisStoreOptions{ + UseSentinel: true, + UseCluster: true, + }, + }, + }, + errStrings: []string{clusterAndSentinelMsg}, + }), + ) +}) diff --git a/pkg/validation/upstreams.go b/pkg/validation/upstreams.go index 2b491a5d44..5cfe0b1ea5 100644 --- a/pkg/validation/upstreams.go +++ b/pkg/validation/upstreams.go @@ -5,7 +5,7 @@ import ( "net/url" "time" - "github.com/oauth2-proxy/oauth2-proxy/pkg/apis/options" + "github.com/oauth2-proxy/oauth2-proxy/v7/pkg/apis/options" ) func validateUpstreams(upstreams options.Upstreams) []string { diff --git a/pkg/validation/upstreams_test.go b/pkg/validation/upstreams_test.go index 86f3da6613..6b8f98290b 100644 --- a/pkg/validation/upstreams_test.go +++ b/pkg/validation/upstreams_test.go @@ -3,7 +3,7 @@ package validation import ( "time" - "github.com/oauth2-proxy/oauth2-proxy/pkg/apis/options" + "github.com/oauth2-proxy/oauth2-proxy/v7/pkg/apis/options" . "github.com/onsi/ginkgo" . "github.com/onsi/ginkgo/extensions/table" . "github.com/onsi/gomega" diff --git a/pkg/validation/validation_suite_test.go b/pkg/validation/validation_suite_test.go index 2c6458fecd..c613360a49 100644 --- a/pkg/validation/validation_suite_test.go +++ b/pkg/validation/validation_suite_test.go @@ -3,7 +3,7 @@ package validation import ( "testing" - "github.com/oauth2-proxy/oauth2-proxy/pkg/logger" + "github.com/oauth2-proxy/oauth2-proxy/v7/pkg/logger" . "github.com/onsi/ginkgo" . "github.com/onsi/gomega" ) diff --git a/providers/auth_test.go b/providers/auth_test.go index 55b98a0fb7..2ece923e8c 100644 --- a/providers/auth_test.go +++ b/providers/auth_test.go @@ -5,7 +5,7 @@ import ( "net/http" "net/url" - "github.com/oauth2-proxy/oauth2-proxy/pkg/apis/sessions" + "github.com/oauth2-proxy/oauth2-proxy/v7/pkg/apis/sessions" ) var authorizedAccessToken = "imaginary_access_token" diff --git a/providers/azure.go b/providers/azure.go index 0ae0cba6ed..234aaff279 100644 --- a/providers/azure.go +++ b/providers/azure.go @@ -9,9 +9,9 @@ import ( "time" "github.com/bitly/go-simplejson" - "github.com/oauth2-proxy/oauth2-proxy/pkg/apis/sessions" - "github.com/oauth2-proxy/oauth2-proxy/pkg/logger" - "github.com/oauth2-proxy/oauth2-proxy/pkg/requests" + "github.com/oauth2-proxy/oauth2-proxy/v7/pkg/apis/sessions" + "github.com/oauth2-proxy/oauth2-proxy/v7/pkg/logger" + "github.com/oauth2-proxy/oauth2-proxy/v7/pkg/requests" ) // AzureProvider represents an Azure based Identity Provider @@ -210,3 +210,12 @@ func (p *AzureProvider) GetEmailAddress(ctx context.Context, s *sessions.Session return email, err } + +func (p *AzureProvider) GetLoginURL(redirectURI, state string) string { + extraParams := url.Values{} + if p.ProtectedResource != nil && p.ProtectedResource.String() != "" { + extraParams.Add("resource", p.ProtectedResource.String()) + } + a := makeLoginURL(p.ProviderData, redirectURI, state, extraParams) + return a.String() +} diff --git a/providers/azure_test.go b/providers/azure_test.go index fe9bbb4238..6e2e4e9730 100644 --- a/providers/azure_test.go +++ b/providers/azure_test.go @@ -213,3 +213,10 @@ func TestAzureProviderRedeemReturnsIdToken(t *testing.T) { assert.Equal(t, timestamp, s.ExpiresOn.UTC()) assert.Equal(t, "refresh1234", s.RefreshToken) } + +func TestAzureProviderProtectedResourceConfigured(t *testing.T) { + p := testAzureProvider("") + p.ProtectedResource, _ = url.Parse("http://my.resource.test") + result := p.GetLoginURL("https://my.test.app/oauth", "") + assert.Contains(t, result, "resource="+url.QueryEscape("http://my.resource.test")) +} diff --git a/providers/bitbucket.go b/providers/bitbucket.go index 56f2aa248d..2612a4ba98 100644 --- a/providers/bitbucket.go +++ b/providers/bitbucket.go @@ -5,9 +5,9 @@ import ( "net/url" "strings" - "github.com/oauth2-proxy/oauth2-proxy/pkg/apis/sessions" - "github.com/oauth2-proxy/oauth2-proxy/pkg/logger" - "github.com/oauth2-proxy/oauth2-proxy/pkg/requests" + "github.com/oauth2-proxy/oauth2-proxy/v7/pkg/apis/sessions" + "github.com/oauth2-proxy/oauth2-proxy/v7/pkg/logger" + "github.com/oauth2-proxy/oauth2-proxy/v7/pkg/requests" ) // BitbucketProvider represents an Bitbucket based Identity Provider diff --git a/providers/bitbucket_test.go b/providers/bitbucket_test.go index 917cf4cadc..22ceda572e 100644 --- a/providers/bitbucket_test.go +++ b/providers/bitbucket_test.go @@ -8,7 +8,7 @@ import ( "net/url" "testing" - "github.com/oauth2-proxy/oauth2-proxy/pkg/apis/sessions" + "github.com/oauth2-proxy/oauth2-proxy/v7/pkg/apis/sessions" . "github.com/onsi/gomega" "github.com/stretchr/testify/assert" ) diff --git a/providers/digitalocean.go b/providers/digitalocean.go index c88533e835..94b2ea9001 100644 --- a/providers/digitalocean.go +++ b/providers/digitalocean.go @@ -5,8 +5,8 @@ import ( "errors" "net/url" - "github.com/oauth2-proxy/oauth2-proxy/pkg/apis/sessions" - "github.com/oauth2-proxy/oauth2-proxy/pkg/requests" + "github.com/oauth2-proxy/oauth2-proxy/v7/pkg/apis/sessions" + "github.com/oauth2-proxy/oauth2-proxy/v7/pkg/requests" ) // DigitalOceanProvider represents a DigitalOcean based Identity Provider diff --git a/providers/digitalocean_test.go b/providers/digitalocean_test.go index 4c8d2a1db4..6e10468f67 100644 --- a/providers/digitalocean_test.go +++ b/providers/digitalocean_test.go @@ -7,7 +7,7 @@ import ( "net/url" "testing" - "github.com/oauth2-proxy/oauth2-proxy/pkg/apis/sessions" + "github.com/oauth2-proxy/oauth2-proxy/v7/pkg/apis/sessions" . "github.com/onsi/gomega" "github.com/stretchr/testify/assert" ) diff --git a/providers/facebook.go b/providers/facebook.go index 7bbc0b45f0..d2ae132d5f 100644 --- a/providers/facebook.go +++ b/providers/facebook.go @@ -5,8 +5,8 @@ import ( "errors" "net/url" - "github.com/oauth2-proxy/oauth2-proxy/pkg/apis/sessions" - "github.com/oauth2-proxy/oauth2-proxy/pkg/requests" + "github.com/oauth2-proxy/oauth2-proxy/v7/pkg/apis/sessions" + "github.com/oauth2-proxy/oauth2-proxy/v7/pkg/requests" ) // FacebookProvider represents an Facebook based Identity Provider diff --git a/providers/github.go b/providers/github.go index 4004a88179..40d30799c3 100644 --- a/providers/github.go +++ b/providers/github.go @@ -11,9 +11,9 @@ import ( "strconv" "strings" - "github.com/oauth2-proxy/oauth2-proxy/pkg/apis/sessions" - "github.com/oauth2-proxy/oauth2-proxy/pkg/logger" - "github.com/oauth2-proxy/oauth2-proxy/pkg/requests" + "github.com/oauth2-proxy/oauth2-proxy/v7/pkg/apis/sessions" + "github.com/oauth2-proxy/oauth2-proxy/v7/pkg/logger" + "github.com/oauth2-proxy/oauth2-proxy/v7/pkg/requests" ) // GitHubProvider represents an GitHub based Identity Provider diff --git a/providers/github_test.go b/providers/github_test.go index ab2bb04eea..dba4bcf6d4 100644 --- a/providers/github_test.go +++ b/providers/github_test.go @@ -7,7 +7,7 @@ import ( "net/url" "testing" - "github.com/oauth2-proxy/oauth2-proxy/pkg/apis/sessions" + "github.com/oauth2-proxy/oauth2-proxy/v7/pkg/apis/sessions" . "github.com/onsi/gomega" "github.com/stretchr/testify/assert" ) diff --git a/providers/gitlab.go b/providers/gitlab.go index 5836e3d6f5..ee15a48642 100644 --- a/providers/gitlab.go +++ b/providers/gitlab.go @@ -7,8 +7,8 @@ import ( "time" oidc "github.com/coreos/go-oidc" - "github.com/oauth2-proxy/oauth2-proxy/pkg/apis/sessions" - "github.com/oauth2-proxy/oauth2-proxy/pkg/requests" + "github.com/oauth2-proxy/oauth2-proxy/v7/pkg/apis/sessions" + "github.com/oauth2-proxy/oauth2-proxy/v7/pkg/requests" "golang.org/x/oauth2" ) diff --git a/providers/gitlab_test.go b/providers/gitlab_test.go index 939d634e4f..21bd04f747 100644 --- a/providers/gitlab_test.go +++ b/providers/gitlab_test.go @@ -7,7 +7,7 @@ import ( "net/url" "testing" - "github.com/oauth2-proxy/oauth2-proxy/pkg/apis/sessions" + "github.com/oauth2-proxy/oauth2-proxy/v7/pkg/apis/sessions" "github.com/stretchr/testify/assert" ) diff --git a/providers/google.go b/providers/google.go index d69a09d358..97d1312e29 100644 --- a/providers/google.go +++ b/providers/google.go @@ -13,9 +13,9 @@ import ( "strings" "time" - "github.com/oauth2-proxy/oauth2-proxy/pkg/apis/sessions" - "github.com/oauth2-proxy/oauth2-proxy/pkg/logger" - "github.com/oauth2-proxy/oauth2-proxy/pkg/requests" + "github.com/oauth2-proxy/oauth2-proxy/v7/pkg/apis/sessions" + "github.com/oauth2-proxy/oauth2-proxy/v7/pkg/logger" + "github.com/oauth2-proxy/oauth2-proxy/v7/pkg/requests" "golang.org/x/oauth2/google" admin "google.golang.org/api/admin/directory/v1" "google.golang.org/api/googleapi" diff --git a/providers/internal_util.go b/providers/internal_util.go index b5c2b2844e..346ee80dcf 100644 --- a/providers/internal_util.go +++ b/providers/internal_util.go @@ -5,8 +5,8 @@ import ( "net/http" "net/url" - "github.com/oauth2-proxy/oauth2-proxy/pkg/logger" - "github.com/oauth2-proxy/oauth2-proxy/pkg/requests" + "github.com/oauth2-proxy/oauth2-proxy/v7/pkg/logger" + "github.com/oauth2-proxy/oauth2-proxy/v7/pkg/requests" ) // stripToken is a helper function to obfuscate "access_token" diff --git a/providers/internal_util_test.go b/providers/internal_util_test.go index 03579c011f..991243a11e 100644 --- a/providers/internal_util_test.go +++ b/providers/internal_util_test.go @@ -8,7 +8,7 @@ import ( "net/url" "testing" - "github.com/oauth2-proxy/oauth2-proxy/pkg/apis/sessions" + "github.com/oauth2-proxy/oauth2-proxy/v7/pkg/apis/sessions" "github.com/stretchr/testify/assert" ) diff --git a/providers/keycloak.go b/providers/keycloak.go index 95f907202a..60b3eacaa3 100644 --- a/providers/keycloak.go +++ b/providers/keycloak.go @@ -4,9 +4,9 @@ import ( "context" "net/url" - "github.com/oauth2-proxy/oauth2-proxy/pkg/apis/sessions" - "github.com/oauth2-proxy/oauth2-proxy/pkg/logger" - "github.com/oauth2-proxy/oauth2-proxy/pkg/requests" + "github.com/oauth2-proxy/oauth2-proxy/v7/pkg/apis/sessions" + "github.com/oauth2-proxy/oauth2-proxy/v7/pkg/logger" + "github.com/oauth2-proxy/oauth2-proxy/v7/pkg/requests" ) type KeycloakProvider struct { diff --git a/providers/keycloak_test.go b/providers/keycloak_test.go index 856d2a22a1..3f419f2ee6 100644 --- a/providers/keycloak_test.go +++ b/providers/keycloak_test.go @@ -7,7 +7,7 @@ import ( "net/url" "testing" - "github.com/oauth2-proxy/oauth2-proxy/pkg/apis/sessions" + "github.com/oauth2-proxy/oauth2-proxy/v7/pkg/apis/sessions" . "github.com/onsi/gomega" "github.com/stretchr/testify/assert" ) diff --git a/providers/linkedin.go b/providers/linkedin.go index 99613e43e3..4a45cfe08d 100644 --- a/providers/linkedin.go +++ b/providers/linkedin.go @@ -6,8 +6,8 @@ import ( "net/http" "net/url" - "github.com/oauth2-proxy/oauth2-proxy/pkg/apis/sessions" - "github.com/oauth2-proxy/oauth2-proxy/pkg/requests" + "github.com/oauth2-proxy/oauth2-proxy/v7/pkg/apis/sessions" + "github.com/oauth2-proxy/oauth2-proxy/v7/pkg/requests" ) // LinkedInProvider represents an LinkedIn based Identity Provider diff --git a/providers/linkedin_test.go b/providers/linkedin_test.go index ffbb8b7368..1ba0c18468 100644 --- a/providers/linkedin_test.go +++ b/providers/linkedin_test.go @@ -7,7 +7,7 @@ import ( "net/url" "testing" - "github.com/oauth2-proxy/oauth2-proxy/pkg/apis/sessions" + "github.com/oauth2-proxy/oauth2-proxy/v7/pkg/apis/sessions" . "github.com/onsi/gomega" "github.com/stretchr/testify/assert" ) diff --git a/providers/logingov.go b/providers/logingov.go index c524741fa7..ff48ccc5d8 100644 --- a/providers/logingov.go +++ b/providers/logingov.go @@ -11,8 +11,8 @@ import ( "time" "github.com/dgrijalva/jwt-go" - "github.com/oauth2-proxy/oauth2-proxy/pkg/apis/sessions" - "github.com/oauth2-proxy/oauth2-proxy/pkg/requests" + "github.com/oauth2-proxy/oauth2-proxy/v7/pkg/apis/sessions" + "github.com/oauth2-proxy/oauth2-proxy/v7/pkg/requests" "gopkg.in/square/go-jose.v2" ) @@ -225,20 +225,12 @@ func (p *LoginGovProvider) Redeem(ctx context.Context, redirectURL, code string) // GetLoginURL overrides GetLoginURL to add login.gov parameters func (p *LoginGovProvider) GetLoginURL(redirectURI, state string) string { - a := *p.LoginURL - params, _ := url.ParseQuery(a.RawQuery) - params.Set("redirect_uri", redirectURI) - params.Set("approval_prompt", p.ApprovalPrompt) - params.Add("scope", p.Scope) - params.Set("client_id", p.ClientID) - params.Set("response_type", "code") - params.Add("state", state) - acr := p.AcrValues - if acr == "" { - acr = "http://idmanagement.gov/ns/assurance/loa/1" - } - params.Add("acr_values", acr) - params.Add("nonce", p.Nonce) - a.RawQuery = params.Encode() + extraParams := url.Values{} + if p.AcrValues == "" { + acr := "http://idmanagement.gov/ns/assurance/loa/1" + extraParams.Add("acr_values", acr) + } + extraParams.Add("nonce", p.Nonce) + a := makeLoginURL(p.ProviderData, redirectURI, state, extraParams) return a.String() } diff --git a/providers/logingov_test.go b/providers/logingov_test.go index 2c0f835701..0b70190b57 100644 --- a/providers/logingov_test.go +++ b/providers/logingov_test.go @@ -289,3 +289,10 @@ func TestLoginGovProviderBadNonce(t *testing.T) { // The "badfakenonce" in the idtoken above should cause this to error out assert.Error(t, err) } + +func TestLoginGovProviderGetLoginURL(t *testing.T) { + p, _, _ := newLoginGovProvider() + result := p.GetLoginURL("http://redirect/", "") + assert.Contains(t, result, "acr_values="+url.QueryEscape("http://idmanagement.gov/ns/assurance/loa/1")) + assert.Contains(t, result, "nonce=fakenonce") +} diff --git a/providers/nextcloud.go b/providers/nextcloud.go index a7498073b2..4a074d6a91 100644 --- a/providers/nextcloud.go +++ b/providers/nextcloud.go @@ -4,8 +4,8 @@ import ( "context" "fmt" - "github.com/oauth2-proxy/oauth2-proxy/pkg/apis/sessions" - "github.com/oauth2-proxy/oauth2-proxy/pkg/requests" + "github.com/oauth2-proxy/oauth2-proxy/v7/pkg/apis/sessions" + "github.com/oauth2-proxy/oauth2-proxy/v7/pkg/requests" ) // NextcloudProvider represents an Nextcloud based Identity Provider diff --git a/providers/nextcloud_test.go b/providers/nextcloud_test.go index ac93d87765..cd26885fd3 100644 --- a/providers/nextcloud_test.go +++ b/providers/nextcloud_test.go @@ -7,7 +7,7 @@ import ( "net/url" "testing" - "github.com/oauth2-proxy/oauth2-proxy/pkg/apis/sessions" + "github.com/oauth2-proxy/oauth2-proxy/v7/pkg/apis/sessions" "github.com/stretchr/testify/assert" ) diff --git a/providers/oidc.go b/providers/oidc.go index b14e0b6118..24221caaf8 100644 --- a/providers/oidc.go +++ b/providers/oidc.go @@ -9,8 +9,8 @@ import ( oidc "github.com/coreos/go-oidc" "golang.org/x/oauth2" - "github.com/oauth2-proxy/oauth2-proxy/pkg/apis/sessions" - "github.com/oauth2-proxy/oauth2-proxy/pkg/requests" + "github.com/oauth2-proxy/oauth2-proxy/v7/pkg/apis/sessions" + "github.com/oauth2-proxy/oauth2-proxy/v7/pkg/requests" ) const emailClaim = "email" @@ -22,6 +22,7 @@ type OIDCProvider struct { Verifier *oidc.IDTokenVerifier AllowUnverifiedEmail bool UserIDClaim string + GroupsClaim string } // NewOIDCProvider initiates a new OIDCProvider @@ -123,6 +124,7 @@ func (p *OIDCProvider) redeemRefreshToken(ctx context.Context, s *sessions.Sessi s.IDToken = newSession.IDToken s.Email = newSession.Email s.User = newSession.User + s.Groups = newSession.Groups s.PreferredUsername = newSession.PreferredUsername } @@ -204,6 +206,7 @@ func (p *OIDCProvider) createSessionStateInternal(ctx context.Context, idToken * newSession.Email = claims.UserID // TODO Rename SessionState.Email to .UserID in the near future newSession.User = claims.Subject + newSession.Groups = claims.Groups newSession.PreferredUsername = claims.PreferredUsername verifyEmail := (p.UserIDClaim == emailClaim) && !p.AllowUnverifiedEmail @@ -222,6 +225,7 @@ func (p *OIDCProvider) ValidateSessionState(ctx context.Context, s *sessions.Ses func (p *OIDCProvider) findClaimsFromIDToken(ctx context.Context, idToken *oidc.IDToken, token *oauth2.Token) (*OIDCClaims, error) { claims := &OIDCClaims{} + // Extract default claims. if err := idToken.Claims(&claims); err != nil { return nil, fmt.Errorf("failed to parse default id_token claims: %v", err) @@ -236,6 +240,8 @@ func (p *OIDCProvider) findClaimsFromIDToken(ctx context.Context, idToken *oidc. claims.UserID = fmt.Sprint(userID) } + claims.Groups = p.extractGroupsFromRawClaims(claims.rawClaims) + // userID claim was not present or was empty in the ID Token if claims.UserID == "" { // BearerToken case, allow empty UserID @@ -273,10 +279,27 @@ func (p *OIDCProvider) findClaimsFromIDToken(ctx context.Context, idToken *oidc. return claims, nil } +func (p *OIDCProvider) extractGroupsFromRawClaims(rawClaims map[string]interface{}) []string { + groups := []string{} + + rawGroups, ok := rawClaims[p.GroupsClaim].([]interface{}) + if rawGroups != nil && ok { + for _, rawGroup := range rawGroups { + group, ok := rawGroup.(string) + if ok { + groups = append(groups, group) + } + } + } + + return groups +} + type OIDCClaims struct { rawClaims map[string]interface{} UserID string Subject string `json:"sub"` Verified *bool `json:"email_verified"` PreferredUsername string `json:"preferred_username"` + Groups []string } diff --git a/providers/oidc_test.go b/providers/oidc_test.go index 9e96752d69..5b88b9a70f 100644 --- a/providers/oidc_test.go +++ b/providers/oidc_test.go @@ -20,7 +20,7 @@ import ( "github.com/stretchr/testify/assert" "golang.org/x/oauth2" - "github.com/oauth2-proxy/oauth2-proxy/pkg/apis/sessions" + "github.com/oauth2-proxy/oauth2-proxy/v7/pkg/apis/sessions" ) const accessToken = "access_token" @@ -29,10 +29,12 @@ const clientID = "https://test.myapp.com" const secret = "secret" type idTokenClaims struct { - Name string `json:"name,omitempty"` - Email string `json:"email,omitempty"` - Phone string `json:"phone_number,omitempty"` - Picture string `json:"picture,omitempty"` + Name string `json:"name,omitempty"` + Email string `json:"email,omitempty"` + Phone string `json:"phone_number,omitempty"` + Picture string `json:"picture,omitempty"` + Groups []string `json:"groups,omitempty"` + OtherGroups []string `json:"other_groups,omitempty"` jwt.StandardClaims } @@ -49,6 +51,8 @@ var defaultIDToken idTokenClaims = idTokenClaims{ "janed@me.com", "+4798765432", "http://mugbook.com/janed/me.jpg", + []string{"test:a", "test:b"}, + []string{"test:c", "test:d"}, jwt.StandardClaims{ Audience: "https://test.myapp.com", ExpiresAt: time.Now().Add(time.Duration(5) * time.Minute).Unix(), @@ -65,6 +69,8 @@ var minimalIDToken idTokenClaims = idTokenClaims{ "", "", "", + []string{}, + []string{}, jwt.StandardClaims{ Audience: "https://test.myapp.com", ExpiresAt: time.Now().Add(time.Duration(5) * time.Minute).Unix(), @@ -273,25 +279,39 @@ func TestCreateSessionStateFromBearerToken(t *testing.T) { const profileURLEmail = "janed@me.com" testCases := map[string]struct { - IDToken idTokenClaims - ExpectedUser string - ExpectedEmail string + IDToken idTokenClaims + GroupsClaim string + ExpectedUser string + ExpectedEmail string + ExpectedGroups []string }{ "Default IDToken": { - IDToken: defaultIDToken, - ExpectedUser: defaultIDToken.Subject, - ExpectedEmail: defaultIDToken.Email, + IDToken: defaultIDToken, + GroupsClaim: "groups", + ExpectedUser: defaultIDToken.Subject, + ExpectedEmail: defaultIDToken.Email, + ExpectedGroups: []string{"test:a", "test:b"}, }, "Minimal IDToken with no email claim": { - IDToken: minimalIDToken, - ExpectedUser: minimalIDToken.Subject, - ExpectedEmail: minimalIDToken.Subject, + IDToken: minimalIDToken, + GroupsClaim: "groups", + ExpectedUser: minimalIDToken.Subject, + ExpectedEmail: minimalIDToken.Subject, + ExpectedGroups: []string{}, + }, + "Custom Groups Claim": { + IDToken: defaultIDToken, + GroupsClaim: "other_groups", + ExpectedUser: defaultIDToken.Subject, + ExpectedEmail: defaultIDToken.Email, + ExpectedGroups: []string{"test:c", "test:d"}, }, } for testName, tc := range testCases { t.Run(testName, func(t *testing.T) { jsonResp := []byte(fmt.Sprintf(`{"email":"%s"}`, profileURLEmail)) server, provider := newTestSetup(jsonResp) + provider.GroupsClaim = tc.GroupsClaim defer server.Close() rawIDToken, err := newSignedTestIDToken(tc.IDToken) @@ -311,6 +331,7 @@ func TestCreateSessionStateFromBearerToken(t *testing.T) { assert.Equal(t, tc.ExpectedEmail, ss.Email) assert.Equal(t, rawIDToken, ss.IDToken) assert.Equal(t, rawIDToken, ss.AccessToken) + assert.Equal(t, tc.ExpectedGroups, ss.Groups) assert.Equal(t, "", ss.RefreshToken) }) } diff --git a/providers/provider_data.go b/providers/provider_data.go index 78aee68774..5fce04ec91 100644 --- a/providers/provider_data.go +++ b/providers/provider_data.go @@ -5,7 +5,7 @@ import ( "io/ioutil" "net/url" - "github.com/oauth2-proxy/oauth2-proxy/pkg/logger" + "github.com/oauth2-proxy/oauth2-proxy/v7/pkg/logger" ) // ProviderData contains information required to configure all implementations diff --git a/providers/provider_default.go b/providers/provider_default.go index 58e1b4fc88..8a5a98acc8 100644 --- a/providers/provider_default.go +++ b/providers/provider_default.go @@ -10,8 +10,8 @@ import ( "github.com/coreos/go-oidc" - "github.com/oauth2-proxy/oauth2-proxy/pkg/apis/sessions" - "github.com/oauth2-proxy/oauth2-proxy/pkg/requests" + "github.com/oauth2-proxy/oauth2-proxy/v7/pkg/apis/sessions" + "github.com/oauth2-proxy/oauth2-proxy/v7/pkg/requests" ) var _ Provider = (*ProviderData)(nil) @@ -75,22 +75,8 @@ func (p *ProviderData) Redeem(ctx context.Context, redirectURL, code string) (s // GetLoginURL with typical oauth parameters func (p *ProviderData) GetLoginURL(redirectURI, state string) string { - a := *p.LoginURL - params, _ := url.ParseQuery(a.RawQuery) - params.Set("redirect_uri", redirectURI) - if p.AcrValues != "" { - params.Add("acr_values", p.AcrValues) - } - if p.Prompt != "" { - params.Set("prompt", p.Prompt) - } else { // Legacy variant of the prompt param: - params.Set("approval_prompt", p.ApprovalPrompt) - } - params.Add("scope", p.Scope) - params.Set("client_id", p.ClientID) - params.Set("response_type", "code") - params.Add("state", state) - a.RawQuery = params.Encode() + extraParams := url.Values{} + a := makeLoginURL(p, redirectURI, state, extraParams) return a.String() } @@ -104,11 +90,6 @@ func (p *ProviderData) GetUserName(ctx context.Context, s *sessions.SessionState return "", errors.New("not implemented") } -// GetPreferredUsername returns the Account preferred username -func (p *ProviderData) GetPreferredUsername(ctx context.Context, s *sessions.SessionState) (string, error) { - return "", errors.New("not implemented") -} - // ValidateGroup validates that the provided email exists in the configured provider // email group(s). func (p *ProviderData) ValidateGroup(email string) bool { diff --git a/providers/provider_default_test.go b/providers/provider_default_test.go index 74d7096f92..8597ac6614 100644 --- a/providers/provider_default_test.go +++ b/providers/provider_default_test.go @@ -6,7 +6,7 @@ import ( "testing" "time" - "github.com/oauth2-proxy/oauth2-proxy/pkg/apis/sessions" + "github.com/oauth2-proxy/oauth2-proxy/v7/pkg/apis/sessions" "github.com/stretchr/testify/assert" ) diff --git a/providers/providers.go b/providers/providers.go index a52eff9081..e92b3293f0 100644 --- a/providers/providers.go +++ b/providers/providers.go @@ -4,7 +4,7 @@ import ( "context" "github.com/coreos/go-oidc" - "github.com/oauth2-proxy/oauth2-proxy/pkg/apis/sessions" + "github.com/oauth2-proxy/oauth2-proxy/v7/pkg/apis/sessions" ) // Provider represents an upstream identity provider implementation @@ -12,7 +12,6 @@ type Provider interface { Data() *ProviderData GetEmailAddress(ctx context.Context, s *sessions.SessionState) (string, error) GetUserName(ctx context.Context, s *sessions.SessionState) (string, error) - GetPreferredUsername(ctx context.Context, s *sessions.SessionState) (string, error) Redeem(ctx context.Context, redirectURI, code string) (*sessions.SessionState, error) ValidateGroup(string) bool ValidateSessionState(ctx context.Context, s *sessions.SessionState) bool diff --git a/providers/util.go b/providers/util.go index 374f637e53..b4b65ac63b 100644 --- a/providers/util.go +++ b/providers/util.go @@ -3,6 +3,7 @@ package providers import ( "fmt" "net/http" + "net/url" ) const ( @@ -29,3 +30,28 @@ func makeOIDCHeader(accessToken string) http.Header { } return makeAuthorizationHeader(tokenTypeBearer, accessToken, extraHeaders) } + +func makeLoginURL(p *ProviderData, redirectURI, state string, extraParams url.Values) url.URL { + a := *p.LoginURL + params, _ := url.ParseQuery(a.RawQuery) + params.Set("redirect_uri", redirectURI) + if p.AcrValues != "" { + params.Add("acr_values", p.AcrValues) + } + if p.Prompt != "" { + params.Set("prompt", p.Prompt) + } else { // Legacy variant of the prompt param: + params.Set("approval_prompt", p.ApprovalPrompt) + } + params.Add("scope", p.Scope) + params.Set("client_id", p.ClientID) + params.Set("response_type", "code") + params.Add("state", state) + for n, p := range extraParams { + for _, v := range p { + params.Add(n, v) + } + } + a.RawQuery = params.Encode() + return a +} diff --git a/templates.go b/templates.go index 39e9e14ef3..04aae44c29 100644 --- a/templates.go +++ b/templates.go @@ -5,7 +5,7 @@ import ( "path" "strings" - "github.com/oauth2-proxy/oauth2-proxy/pkg/logger" + "github.com/oauth2-proxy/oauth2-proxy/v7/pkg/logger" ) func loadTemplates(dir string) *template.Template { diff --git a/validator.go b/validator.go index e02a25365d..71f32a2dc8 100644 --- a/validator.go +++ b/validator.go @@ -9,7 +9,7 @@ import ( "sync/atomic" "unsafe" - "github.com/oauth2-proxy/oauth2-proxy/pkg/logger" + "github.com/oauth2-proxy/oauth2-proxy/v7/pkg/logger" ) // UserMap holds information from the authenticated emails file diff --git a/watcher.go b/watcher.go index 642cb36da7..edf1d9bd46 100644 --- a/watcher.go +++ b/watcher.go @@ -9,7 +9,7 @@ import ( "github.com/fsnotify/fsnotify" - "github.com/oauth2-proxy/oauth2-proxy/pkg/logger" + "github.com/oauth2-proxy/oauth2-proxy/v7/pkg/logger" ) // WaitForReplacement waits for a file to exist on disk and then starts a watch diff --git a/watcher_unsupported.go b/watcher_unsupported.go index 986cc462e7..4c5a7209ac 100644 --- a/watcher_unsupported.go +++ b/watcher_unsupported.go @@ -2,7 +2,7 @@ package main -import "github.com/oauth2-proxy/oauth2-proxy/pkg/logger" +import "github.com/oauth2-proxy/oauth2-proxy/v7/pkg/logger" func WatchForUpdates(filename string, done <-chan bool, action func()) { logger.Errorf("file watching not implemented on this platform")