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 | ||||
|   -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 | ||||
|   -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 | ||||
|   -profile-url="": Profile access endpoint | ||||
|   -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.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.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-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)") | ||||
|  |  | |||
|  | @ -42,6 +42,7 @@ type OauthProxy struct { | |||
| 	DisplayHtpasswdForm bool | ||||
| 	serveMux            http.Handler | ||||
| 	PassBasicAuth       bool | ||||
| 	BasicAuthPassword   string | ||||
| 	PassAccessToken     bool | ||||
| 	CookieCipher        *cookie.Cipher | ||||
| 	skipAuthRegex       []string | ||||
|  | @ -141,16 +142,17 @@ func NewOauthProxy(opts *Options, validator func(string) bool) *OauthProxy { | |||
| 		OauthStartPath:    fmt.Sprintf("%s/start", opts.ProxyPrefix), | ||||
| 		OauthCallbackPath: fmt.Sprintf("%s/callback", opts.ProxyPrefix), | ||||
| 
 | ||||
| 		ProxyPrefix:     opts.ProxyPrefix, | ||||
| 		provider:        opts.provider, | ||||
| 		serveMux:        serveMux, | ||||
| 		redirectUrl:     redirectUrl, | ||||
| 		skipAuthRegex:   opts.SkipAuthRegex, | ||||
| 		compiledRegex:   opts.CompiledRegex, | ||||
| 		PassBasicAuth:   opts.PassBasicAuth, | ||||
| 		PassAccessToken: opts.PassAccessToken, | ||||
| 		CookieCipher:    cipher, | ||||
| 		templates:       loadTemplates(opts.CustomTemplatesDir), | ||||
| 		ProxyPrefix:       opts.ProxyPrefix, | ||||
| 		provider:          opts.provider, | ||||
| 		serveMux:          serveMux, | ||||
| 		redirectUrl:       redirectUrl, | ||||
| 		skipAuthRegex:     opts.SkipAuthRegex, | ||||
| 		compiledRegex:     opts.CompiledRegex, | ||||
| 		PassBasicAuth:     opts.PassBasicAuth, | ||||
| 		BasicAuthPassword: opts.BasicAuthPassword, | ||||
| 		PassAccessToken:   opts.PassAccessToken, | ||||
| 		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
 | ||||
| 	if p.PassBasicAuth { | ||||
| 		req.SetBasicAuth(session.User, "") | ||||
| 		req.SetBasicAuth(session.User, p.BasicAuthPassword) | ||||
| 		req.Header["X-Forwarded-User"] = []string{session.User} | ||||
| 		if session.Email != "" { | ||||
| 			req.Header["X-Forwarded-Email"] = []string{session.Email} | ||||
|  |  | |||
|  | @ -1,6 +1,7 @@ | |||
| package main | ||||
| 
 | ||||
| import ( | ||||
| 	"encoding/base64" | ||||
| 	"github.com/bitly/oauth2_proxy/providers" | ||||
| 	"github.com/bmizerany/assert" | ||||
| 	"io/ioutil" | ||||
|  | @ -88,6 +89,101 @@ func TestRobotsTxt(t *testing.T) { | |||
| 	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 { | ||||
| 	*providers.ProviderData | ||||
| 	EmailAddress string | ||||
|  |  | |||
							
								
								
									
										11
									
								
								options.go
								
								
								
								
							
							
						
						
									
										11
									
								
								options.go
								
								
								
								
							|  | @ -37,11 +37,12 @@ type Options struct { | |||
| 	CookieSecure   bool          `flag:"cookie-secure" cfg:"cookie_secure"` | ||||
| 	CookieHttpOnly bool          `flag:"cookie-httponly" cfg:"cookie_httponly"` | ||||
| 
 | ||||
| 	Upstreams       []string `flag:"upstream" cfg:"upstreams"` | ||||
| 	SkipAuthRegex   []string `flag:"skip-auth-regex" cfg:"skip_auth_regex"` | ||||
| 	PassBasicAuth   bool     `flag:"pass-basic-auth" cfg:"pass_basic_auth"` | ||||
| 	PassAccessToken bool     `flag:"pass-access-token" cfg:"pass_access_token"` | ||||
| 	PassHostHeader  bool     `flag:"pass-host-header" cfg:"pass_host_header"` | ||||
| 	Upstreams         []string `flag:"upstream" cfg:"upstreams"` | ||||
| 	SkipAuthRegex     []string `flag:"skip-auth-regex" cfg:"skip_auth_regex"` | ||||
| 	PassBasicAuth     bool     `flag:"pass-basic-auth" cfg:"pass_basic_auth"` | ||||
| 	BasicAuthPassword string   `flag:"basic-auth-password" cfg:"basic_auth_password"` | ||||
| 	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
 | ||||
| 	// potential overrides.
 | ||||
|  |  | |||
		Loading…
	
		Reference in New Issue