Merge pull request #129 from jburnham/basic_auth_password
Add support for setting the basic auth password.
This commit is contained in:
		
						commit
						c086bddcbe
					
				|  | @ -116,6 +116,7 @@ Usage of oauth2_proxy: | ||||||
|   -login-url="": Authentication endpoint |   -login-url="": Authentication endpoint | ||||||
|   -pass-access-token=false: pass OAuth access_token to upstream via X-Forwarded-Access-Token header |   -pass-access-token=false: pass OAuth access_token to upstream via X-Forwarded-Access-Token header | ||||||
|   -pass-basic-auth=true: pass HTTP Basic Auth, X-Forwarded-User and X-Forwarded-Email information to upstream |   -pass-basic-auth=true: pass HTTP Basic Auth, X-Forwarded-User and X-Forwarded-Email information to upstream | ||||||
|  |   -basic-auth-password="": the password to set when passing the HTTP Basic Auth header | ||||||
|   -pass-host-header=true: pass the request Host Header to upstream |   -pass-host-header=true: pass the request Host Header to upstream | ||||||
|   -profile-url="": Profile access endpoint |   -profile-url="": Profile access endpoint | ||||||
|   -provider="google": OAuth provider |   -provider="google": OAuth provider | ||||||
|  |  | ||||||
							
								
								
									
										1
									
								
								main.go
								
								
								
								
							
							
						
						
									
										1
									
								
								main.go
								
								
								
								
							|  | @ -31,6 +31,7 @@ func main() { | ||||||
| 	flagSet.String("redirect-url", "", "the OAuth Redirect URL. ie: \"https://internalapp.yourcompany.com/oauth2/callback\"") | 	flagSet.String("redirect-url", "", "the OAuth Redirect URL. ie: \"https://internalapp.yourcompany.com/oauth2/callback\"") | ||||||
| 	flagSet.Var(&upstreams, "upstream", "the http url(s) of the upstream endpoint. If multiple, routing is based on path") | 	flagSet.Var(&upstreams, "upstream", "the http url(s) of the upstream endpoint. If multiple, routing is based on path") | ||||||
| 	flagSet.Bool("pass-basic-auth", true, "pass HTTP Basic Auth, X-Forwarded-User and X-Forwarded-Email information to upstream") | 	flagSet.Bool("pass-basic-auth", true, "pass HTTP Basic Auth, X-Forwarded-User and X-Forwarded-Email information to upstream") | ||||||
|  | 	flagSet.String("basic-auth-password", "", "the password to set when passing the HTTP Basic Auth header") | ||||||
| 	flagSet.Bool("pass-access-token", false, "pass OAuth access_token to upstream via X-Forwarded-Access-Token header") | 	flagSet.Bool("pass-access-token", false, "pass OAuth access_token to upstream via X-Forwarded-Access-Token header") | ||||||
| 	flagSet.Bool("pass-host-header", true, "pass the request Host Header to upstream") | 	flagSet.Bool("pass-host-header", true, "pass the request Host Header to upstream") | ||||||
| 	flagSet.Var(&skipAuthRegex, "skip-auth-regex", "bypass authentication for requests path's that match (may be given multiple times)") | 	flagSet.Var(&skipAuthRegex, "skip-auth-regex", "bypass authentication for requests path's that match (may be given multiple times)") | ||||||
|  |  | ||||||
|  | @ -42,6 +42,7 @@ type OauthProxy struct { | ||||||
| 	DisplayHtpasswdForm bool | 	DisplayHtpasswdForm bool | ||||||
| 	serveMux            http.Handler | 	serveMux            http.Handler | ||||||
| 	PassBasicAuth       bool | 	PassBasicAuth       bool | ||||||
|  | 	BasicAuthPassword   string | ||||||
| 	PassAccessToken     bool | 	PassAccessToken     bool | ||||||
| 	CookieCipher        *cookie.Cipher | 	CookieCipher        *cookie.Cipher | ||||||
| 	skipAuthRegex       []string | 	skipAuthRegex       []string | ||||||
|  | @ -141,16 +142,17 @@ func NewOauthProxy(opts *Options, validator func(string) bool) *OauthProxy { | ||||||
| 		OauthStartPath:    fmt.Sprintf("%s/start", opts.ProxyPrefix), | 		OauthStartPath:    fmt.Sprintf("%s/start", opts.ProxyPrefix), | ||||||
| 		OauthCallbackPath: fmt.Sprintf("%s/callback", opts.ProxyPrefix), | 		OauthCallbackPath: fmt.Sprintf("%s/callback", opts.ProxyPrefix), | ||||||
| 
 | 
 | ||||||
| 		ProxyPrefix:     opts.ProxyPrefix, | 		ProxyPrefix:       opts.ProxyPrefix, | ||||||
| 		provider:        opts.provider, | 		provider:          opts.provider, | ||||||
| 		serveMux:        serveMux, | 		serveMux:          serveMux, | ||||||
| 		redirectUrl:     redirectUrl, | 		redirectUrl:       redirectUrl, | ||||||
| 		skipAuthRegex:   opts.SkipAuthRegex, | 		skipAuthRegex:     opts.SkipAuthRegex, | ||||||
| 		compiledRegex:   opts.CompiledRegex, | 		compiledRegex:     opts.CompiledRegex, | ||||||
| 		PassBasicAuth:   opts.PassBasicAuth, | 		PassBasicAuth:     opts.PassBasicAuth, | ||||||
| 		PassAccessToken: opts.PassAccessToken, | 		BasicAuthPassword: opts.BasicAuthPassword, | ||||||
| 		CookieCipher:    cipher, | 		PassAccessToken:   opts.PassAccessToken, | ||||||
| 		templates:       loadTemplates(opts.CustomTemplatesDir), | 		CookieCipher:      cipher, | ||||||
|  | 		templates:         loadTemplates(opts.CustomTemplatesDir), | ||||||
| 	} | 	} | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
|  | @ -518,7 +520,7 @@ func (p *OauthProxy) Proxy(rw http.ResponseWriter, req *http.Request) { | ||||||
| 
 | 
 | ||||||
| 	// At this point, the user is authenticated. proxy normally
 | 	// At this point, the user is authenticated. proxy normally
 | ||||||
| 	if p.PassBasicAuth { | 	if p.PassBasicAuth { | ||||||
| 		req.SetBasicAuth(session.User, "") | 		req.SetBasicAuth(session.User, p.BasicAuthPassword) | ||||||
| 		req.Header["X-Forwarded-User"] = []string{session.User} | 		req.Header["X-Forwarded-User"] = []string{session.User} | ||||||
| 		if session.Email != "" { | 		if session.Email != "" { | ||||||
| 			req.Header["X-Forwarded-Email"] = []string{session.Email} | 			req.Header["X-Forwarded-Email"] = []string{session.Email} | ||||||
|  |  | ||||||
|  | @ -1,6 +1,7 @@ | ||||||
| package main | package main | ||||||
| 
 | 
 | ||||||
| import ( | import ( | ||||||
|  | 	"encoding/base64" | ||||||
| 	"github.com/bitly/oauth2_proxy/providers" | 	"github.com/bitly/oauth2_proxy/providers" | ||||||
| 	"github.com/bmizerany/assert" | 	"github.com/bmizerany/assert" | ||||||
| 	"io/ioutil" | 	"io/ioutil" | ||||||
|  | @ -88,6 +89,101 @@ func TestRobotsTxt(t *testing.T) { | ||||||
| 	assert.Equal(t, "User-agent: *\nDisallow: /", rw.Body.String()) | 	assert.Equal(t, "User-agent: *\nDisallow: /", rw.Body.String()) | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
|  | func TestBasicAuthPassword(t *testing.T) { | ||||||
|  | 	provider_server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { | ||||||
|  | 		log.Printf("%#v", r) | ||||||
|  | 		url := r.URL | ||||||
|  | 		payload := "" | ||||||
|  | 		switch url.Path { | ||||||
|  | 		case "/oauth/token": | ||||||
|  | 			payload = `{"access_token": "my_auth_token"}` | ||||||
|  | 		default: | ||||||
|  | 			payload = r.Header.Get("Authorization") | ||||||
|  | 			if payload == "" { | ||||||
|  | 				payload = "No Authorization header found." | ||||||
|  | 			} | ||||||
|  | 		} | ||||||
|  | 		w.WriteHeader(200) | ||||||
|  | 		w.Write([]byte(payload)) | ||||||
|  | 	})) | ||||||
|  | 	opts := NewOptions() | ||||||
|  | 	opts.Upstreams = append(opts.Upstreams, provider_server.URL) | ||||||
|  | 	// The CookieSecret must be 32 bytes in order to create the AES
 | ||||||
|  | 	// cipher.
 | ||||||
|  | 	opts.CookieSecret = "xyzzyplughxyzzyplughxyzzyplughxp" | ||||||
|  | 	opts.ClientID = "bazquux" | ||||||
|  | 	opts.ClientSecret = "foobar" | ||||||
|  | 	opts.CookieSecure = false | ||||||
|  | 	opts.PassBasicAuth = true | ||||||
|  | 	opts.BasicAuthPassword = "This is a secure password" | ||||||
|  | 	opts.Validate() | ||||||
|  | 
 | ||||||
|  | 	provider_url, _ := url.Parse(provider_server.URL) | ||||||
|  | 	const email_address = "michael.bland@gsa.gov" | ||||||
|  | 	const user_name = "michael.bland" | ||||||
|  | 
 | ||||||
|  | 	opts.provider = &TestProvider{ | ||||||
|  | 		ProviderData: &providers.ProviderData{ | ||||||
|  | 			ProviderName: "Test Provider", | ||||||
|  | 			LoginUrl: &url.URL{ | ||||||
|  | 				Scheme: "http", | ||||||
|  | 				Host:   provider_url.Host, | ||||||
|  | 				Path:   "/oauth/authorize", | ||||||
|  | 			}, | ||||||
|  | 			RedeemUrl: &url.URL{ | ||||||
|  | 				Scheme: "http", | ||||||
|  | 				Host:   provider_url.Host, | ||||||
|  | 				Path:   "/oauth/token", | ||||||
|  | 			}, | ||||||
|  | 			ProfileUrl: &url.URL{ | ||||||
|  | 				Scheme: "http", | ||||||
|  | 				Host:   provider_url.Host, | ||||||
|  | 				Path:   "/api/v1/profile", | ||||||
|  | 			}, | ||||||
|  | 			Scope: "profile.email", | ||||||
|  | 		}, | ||||||
|  | 		EmailAddress: email_address, | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	proxy := NewOauthProxy(opts, func(email string) bool { | ||||||
|  | 		return email == email_address | ||||||
|  | 	}) | ||||||
|  | 
 | ||||||
|  | 	rw := httptest.NewRecorder() | ||||||
|  | 	req, _ := http.NewRequest("GET", "/oauth2/callback?code=callback_code", | ||||||
|  | 		strings.NewReader("")) | ||||||
|  | 	proxy.ServeHTTP(rw, req) | ||||||
|  | 	cookie := rw.HeaderMap["Set-Cookie"][0] | ||||||
|  | 
 | ||||||
|  | 	cookieName := proxy.CookieName | ||||||
|  | 	var value string | ||||||
|  | 	key_prefix := cookieName + "=" | ||||||
|  | 
 | ||||||
|  | 	for _, field := range strings.Split(cookie, "; ") { | ||||||
|  | 		value = strings.TrimPrefix(field, key_prefix) | ||||||
|  | 		if value != field { | ||||||
|  | 			break | ||||||
|  | 		} else { | ||||||
|  | 			value = "" | ||||||
|  | 		} | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	req, _ = http.NewRequest("GET", "/", strings.NewReader("")) | ||||||
|  | 	req.AddCookie(&http.Cookie{ | ||||||
|  | 		Name:     cookieName, | ||||||
|  | 		Value:    value, | ||||||
|  | 		Path:     "/", | ||||||
|  | 		Expires:  time.Now().Add(time.Duration(24)), | ||||||
|  | 		HttpOnly: true, | ||||||
|  | 	}) | ||||||
|  | 
 | ||||||
|  | 	rw = httptest.NewRecorder() | ||||||
|  | 	proxy.ServeHTTP(rw, req) | ||||||
|  | 	expectedHeader := "Basic " + base64.StdEncoding.EncodeToString([]byte(user_name+":"+opts.BasicAuthPassword)) | ||||||
|  | 	assert.Equal(t, expectedHeader, rw.Body.String()) | ||||||
|  | 	provider_server.Close() | ||||||
|  | } | ||||||
|  | 
 | ||||||
| type TestProvider struct { | type TestProvider struct { | ||||||
| 	*providers.ProviderData | 	*providers.ProviderData | ||||||
| 	EmailAddress string | 	EmailAddress string | ||||||
|  |  | ||||||
							
								
								
									
										11
									
								
								options.go
								
								
								
								
							
							
						
						
									
										11
									
								
								options.go
								
								
								
								
							|  | @ -37,11 +37,12 @@ type Options struct { | ||||||
| 	CookieSecure   bool          `flag:"cookie-secure" cfg:"cookie_secure"` | 	CookieSecure   bool          `flag:"cookie-secure" cfg:"cookie_secure"` | ||||||
| 	CookieHttpOnly bool          `flag:"cookie-httponly" cfg:"cookie_httponly"` | 	CookieHttpOnly bool          `flag:"cookie-httponly" cfg:"cookie_httponly"` | ||||||
| 
 | 
 | ||||||
| 	Upstreams       []string `flag:"upstream" cfg:"upstreams"` | 	Upstreams         []string `flag:"upstream" cfg:"upstreams"` | ||||||
| 	SkipAuthRegex   []string `flag:"skip-auth-regex" cfg:"skip_auth_regex"` | 	SkipAuthRegex     []string `flag:"skip-auth-regex" cfg:"skip_auth_regex"` | ||||||
| 	PassBasicAuth   bool     `flag:"pass-basic-auth" cfg:"pass_basic_auth"` | 	PassBasicAuth     bool     `flag:"pass-basic-auth" cfg:"pass_basic_auth"` | ||||||
| 	PassAccessToken bool     `flag:"pass-access-token" cfg:"pass_access_token"` | 	BasicAuthPassword string   `flag:"basic-auth-password" cfg:"basic_auth_password"` | ||||||
| 	PassHostHeader  bool     `flag:"pass-host-header" cfg:"pass_host_header"` | 	PassAccessToken   bool     `flag:"pass-access-token" cfg:"pass_access_token"` | ||||||
|  | 	PassHostHeader    bool     `flag:"pass-host-header" cfg:"pass_host_header"` | ||||||
| 
 | 
 | ||||||
| 	// These options allow for other providers besides Google, with
 | 	// These options allow for other providers besides Google, with
 | ||||||
| 	// potential overrides.
 | 	// potential overrides.
 | ||||||
|  |  | ||||||
		Loading…
	
		Reference in New Issue