From 220b3708fc4e1de5e30135b48c8d8a2ef7efd518 Mon Sep 17 00:00:00 2001 From: Stefan Sedich Date: Thu, 25 Feb 2021 13:02:23 -0800 Subject: [PATCH] Add support for setting groups on session when using basic auth (#1064) * Add support for setting groups on session when using basic auth * Refactoring based on feedback * Attribution --- CHANGELOG.md | 1 + docs/docs/configuration/overview.md | 1 + oauthproxy.go | 2 +- pkg/apis/options/options.go | 2 ++ pkg/middleware/basic_session.go | 13 +++++++------ pkg/middleware/basic_session_test.go | 9 ++++++++- 6 files changed, 20 insertions(+), 8 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index f1f3dde1..349127dd 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -8,6 +8,7 @@ ## Changes since v7.0.1 +- [#1064](https://github.com/oauth2-proxy/oauth2-proxy/pull/1064) Add support for setting groups on session when using basic auth (@stefansedich) - [#1056](https://github.com/oauth2-proxy/oauth2-proxy/pull/1056) Add option for custom logos on the sign in page (@JoelSpeed) - [#1054](https://github.com/oauth2-proxy/oauth2-proxy/pull/1054) Update to Go 1.16 (@JoelSpeed) - [#1052](https://github.com/oauth2-proxy/oauth2-proxy/pull/1052) Update golangci-lint to latest version (v1.36.0) (@JoelSpeed) diff --git a/docs/docs/configuration/overview.md b/docs/docs/configuration/overview.md index 66750004..5a4c7e2d 100644 --- a/docs/docs/configuration/overview.md +++ b/docs/docs/configuration/overview.md @@ -62,6 +62,7 @@ An example [oauth2-proxy.cfg](https://github.com/oauth2-proxy/oauth2-proxy/blob/ | `--google-group` | string | restrict logins to members of this google group (may be given multiple times). | | | `--google-service-account-json` | string | the path to the service account json credentials | | | `--htpasswd-file` | string | additionally authenticate against a htpasswd file. Entries must be created with `htpasswd -B` for bcrypt encryption | | +| `--htpasswd-user-group` | string \| list | the groups to be set on sessions for htpasswd users | | | `--http-address` | string | `[http://]:` or `unix://` to listen on for HTTP clients | `"127.0.0.1:4180"` | | `--https-address` | string | `:` to listen on for HTTPS clients | `":443"` | | `--logging-compress` | bool | Should rotated log files be compressed using gzip | false | diff --git a/oauthproxy.go b/oauthproxy.go index 82f89e6b..8041b4f7 100644 --- a/oauthproxy.go +++ b/oauthproxy.go @@ -277,7 +277,7 @@ func buildSessionChain(opts *options.Options, sessionStore sessionsapi.SessionSt } if validator != nil { - chain = chain.Append(middleware.NewBasicAuthSessionLoader(validator)) + chain = chain.Append(middleware.NewBasicAuthSessionLoader(validator, opts.HtpasswdUserGroups)) } chain = chain.Append(middleware.NewStoredSessionLoader(&middleware.StoredSessionLoaderOptions{ diff --git a/pkg/apis/options/options.go b/pkg/apis/options/options.go index 77a3fbda..ef0d090f 100644 --- a/pkg/apis/options/options.go +++ b/pkg/apis/options/options.go @@ -54,6 +54,7 @@ type Options struct { GoogleAdminEmail string `flag:"google-admin-email" cfg:"google_admin_email"` GoogleServiceAccountJSON string `flag:"google-service-account-json" cfg:"google_service_account_json"` HtpasswdFile string `flag:"htpasswd-file" cfg:"htpasswd_file"` + HtpasswdUserGroups []string `flag:"htpasswd-user-group" cfg:"htpasswd_user_groups"` Cookie Cookie `cfg:",squash"` Session SessionOptions `cfg:",squash"` @@ -199,6 +200,7 @@ func NewFlagSet() *pflag.FlagSet { flagSet.String("client-secret-file", "", "the file with OAuth Client Secret") flagSet.String("authenticated-emails-file", "", "authenticate against emails via file (one per line)") flagSet.String("htpasswd-file", "", "additionally authenticate against a htpasswd file. Entries must be created with \"htpasswd -B\" for bcrypt encryption") + flagSet.StringSlice("htpasswd-user-group", []string{}, "the groups to be set on sessions for htpasswd users (may be given multiple times)") flagSet.String("proxy-prefix", "/oauth2", "the url root path that this proxy should be nested under (e.g. //sign_in)") flagSet.String("ping-path", "/ping", "the ping endpoint that can be used for basic health checks") flagSet.String("ping-user-agent", "", "special User-Agent that will be used for basic health checks") diff --git a/pkg/middleware/basic_session.go b/pkg/middleware/basic_session.go index 7de1bf2b..54fc559c 100644 --- a/pkg/middleware/basic_session.go +++ b/pkg/middleware/basic_session.go @@ -11,9 +11,9 @@ import ( "github.com/oauth2-proxy/oauth2-proxy/v7/pkg/logger" ) -func NewBasicAuthSessionLoader(validator basic.Validator) alice.Constructor { +func NewBasicAuthSessionLoader(validator basic.Validator, sessionGroups []string) alice.Constructor { return func(next http.Handler) http.Handler { - return loadBasicAuthSession(validator, next) + return loadBasicAuthSession(validator, sessionGroups, next) } } @@ -22,7 +22,7 @@ func NewBasicAuthSessionLoader(validator basic.Validator) alice.Constructor { // If no authorization header is found, or the header is invalid, no session // will be loaded and the request will be passed to the next handler. // If a session was loaded by a previous handler, it will not be replaced. -func loadBasicAuthSession(validator basic.Validator, next http.Handler) http.Handler { +func loadBasicAuthSession(validator basic.Validator, sessionGroups []string, next http.Handler) http.Handler { return http.HandlerFunc(func(rw http.ResponseWriter, req *http.Request) { scope := middlewareapi.GetRequestScope(req) // If scope is nil, this will panic. @@ -33,7 +33,7 @@ func loadBasicAuthSession(validator basic.Validator, next http.Handler) http.Han return } - session, err := getBasicSession(validator, req) + session, err := getBasicSession(validator, sessionGroups, req) if err != nil { logger.Errorf("Error retrieving session from token in Authorization header: %v", err) } @@ -47,7 +47,7 @@ func loadBasicAuthSession(validator basic.Validator, next http.Handler) http.Han // getBasicSession attempts to load a basic session from the request. // If the credentials in the request exist within the htpasswdMap, // a new session will be created. -func getBasicSession(validator basic.Validator, req *http.Request) (*sessionsapi.SessionState, error) { +func getBasicSession(validator basic.Validator, sessionGroups []string, req *http.Request) (*sessionsapi.SessionState, error) { auth := req.Header.Get("Authorization") if auth == "" { // No auth header provided, so don't attempt to load a session @@ -61,7 +61,8 @@ func getBasicSession(validator basic.Validator, req *http.Request) (*sessionsapi if validator.Validate(user, password) { logger.PrintAuthf(user, req, logger.AuthSuccess, "Authenticated via basic auth and HTpasswd File") - return &sessionsapi.SessionState{User: user}, nil + + return &sessionsapi.SessionState{User: user, Groups: sessionGroups}, nil } logger.PrintAuthf(user, req, logger.AuthFailure, "Invalid authentication via basic auth: not in Htpasswd File") diff --git a/pkg/middleware/basic_session_test.go b/pkg/middleware/basic_session_test.go index 14c49c43..94cc4248 100644 --- a/pkg/middleware/basic_session_test.go +++ b/pkg/middleware/basic_session_test.go @@ -26,6 +26,7 @@ var _ = Describe("Basic Auth Session Suite", func() { type basicAuthSessionLoaderTableInput struct { authorizationHeader string + sessionGroups []string existingSession *sessionsapi.SessionState expectedSession *sessionsapi.SessionState } @@ -54,7 +55,7 @@ var _ = Describe("Basic Auth Session Suite", func() { // Create the handler with a next handler that will capture the session // from the scope var gotSession *sessionsapi.SessionState - handler := NewBasicAuthSessionLoader(validator)(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + handler := NewBasicAuthSessionLoader(validator, in.sessionGroups)(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { gotSession = middlewareapi.GetRequestScope(r).Session })) handler.ServeHTTP(rw, req) @@ -111,6 +112,12 @@ var _ = Describe("Basic Auth Session Suite", func() { existingSession: nil, expectedSession: &sessionsapi.SessionState{User: "admin"}, }), + Entry("Basic with groups", basicAuthSessionLoaderTableInput{ + authorizationHeader: "Basic YWRtaW46QWRtMW4xc3RyJHQwcg==", + sessionGroups: []string{"a", "b"}, + existingSession: nil, + expectedSession: &sessionsapi.SessionState{User: "admin", Groups: []string{"a", "b"}}, + }), ) }) })