Merge branch 'master' of github.com:outlook/oauth2_proxy into work

This commit is contained in:
Pavel Sorokin 2017-09-07 13:58:22 +08:00
commit 51ac937bcc
8 changed files with 78 additions and 25 deletions

View File

@ -4,7 +4,7 @@ oauth2_proxy
A reverse proxy and static file server that provides authentication using Providers (Google, GitHub, and others) A reverse proxy and static file server that provides authentication using Providers (Google, GitHub, and others)
to validate accounts by email, domain or group. to validate accounts by email, domain or group.
[![Build Status](https://secure.travis-ci.org/bitly/oauth2_proxy.png?branch=master)](http://travis-ci.org/bitly/oauth2_proxy) [![Build Status](https://secure.travis-ci.org/bitly/oauth2_proxy.svg?branch=master)](http://travis-ci.org/bitly/oauth2_proxy)
![Sign In Page](https://cloud.githubusercontent.com/assets/45028/4970624/7feb7dd8-6886-11e4-93e0-c9904af44ea8.png) ![Sign In Page](https://cloud.githubusercontent.com/assets/45028/4970624/7feb7dd8-6886-11e4-93e0-c9904af44ea8.png)
@ -161,7 +161,7 @@ To authorize by email domain use `--email-domain=yourcompany.com`. To authorize
`oauth2_proxy` can be configured via [config file](#config-file), [command line options](#command-line-options) or [environment variables](#environment-variables). `oauth2_proxy` can be configured via [config file](#config-file), [command line options](#command-line-options) or [environment variables](#environment-variables).
To generate a strong cookie secret use `python -c 'import os,base64; print base64.b64encode(os.urandom(16))'` To generate a strong cookie secret use `python -c 'import os,base64; print base64.urlsafe_b64encode(os.urandom(16))'`
### Config File ### Config File

View File

@ -491,7 +491,11 @@ func (p *OAuthProxy) SignIn(rw http.ResponseWriter, req *http.Request) {
p.SaveSession(rw, req, session) p.SaveSession(rw, req, session)
http.Redirect(rw, req, redirect, 302) http.Redirect(rw, req, redirect, 302)
} else { } else {
p.SignInPage(rw, req, 200) if p.SkipProviderButton {
p.OAuthStart(rw, req)
} else {
p.SignInPage(rw, req, http.StatusOK)
}
} }
} }

View File

@ -3,9 +3,6 @@ package main
import ( import (
"crypto" "crypto"
"encoding/base64" "encoding/base64"
"github.com/18F/hmacauth"
"github.com/bitly/oauth2_proxy/providers"
"github.com/bmizerany/assert"
"io" "io"
"io/ioutil" "io/ioutil"
"log" "log"
@ -17,6 +14,10 @@ import (
"strings" "strings"
"testing" "testing"
"time" "time"
"github.com/18F/hmacauth"
"github.com/bitly/oauth2_proxy/providers"
"github.com/bmizerany/assert"
) )
func init() { func init() {
@ -359,26 +360,30 @@ func TestDoNotForwardAccessTokenUpstream(t *testing.T) {
} }
type SignInPageTest struct { type SignInPageTest struct {
opts *Options opts *Options
proxy *OAuthProxy proxy *OAuthProxy
sign_in_regexp *regexp.Regexp sign_in_regexp *regexp.Regexp
sign_in_provider_regexp *regexp.Regexp
} }
const signInRedirectPattern = `<input type="hidden" name="rd" value="(.*)">` const signInRedirectPattern = `<input type="hidden" name="rd" value="(.*)">`
const signInSkipProvider = `>Found<`
func NewSignInPageTest() *SignInPageTest { func NewSignInPageTest(skipProvider bool) *SignInPageTest {
var sip_test SignInPageTest var sip_test SignInPageTest
sip_test.opts = NewOptions() sip_test.opts = NewOptions()
sip_test.opts.CookieSecret = "foobar" sip_test.opts.CookieSecret = "foobar"
sip_test.opts.ClientID = "bazquux" sip_test.opts.ClientID = "bazquux"
sip_test.opts.ClientSecret = "xyzzyplugh" sip_test.opts.ClientSecret = "xyzzyplugh"
sip_test.opts.SkipProviderButton = skipProvider
sip_test.opts.Validate() sip_test.opts.Validate()
sip_test.proxy = NewOAuthProxy(sip_test.opts, func(email string) bool { sip_test.proxy = NewOAuthProxy(sip_test.opts, func(email string) bool {
return true return true
}) })
sip_test.sign_in_regexp = regexp.MustCompile(signInRedirectPattern) sip_test.sign_in_regexp = regexp.MustCompile(signInRedirectPattern)
sip_test.sign_in_provider_regexp = regexp.MustCompile(signInSkipProvider)
return &sip_test return &sip_test
} }
@ -391,7 +396,7 @@ func (sip_test *SignInPageTest) GetEndpoint(endpoint string) (int, string) {
} }
func TestSignInPageIncludesTargetRedirect(t *testing.T) { func TestSignInPageIncludesTargetRedirect(t *testing.T) {
sip_test := NewSignInPageTest() sip_test := NewSignInPageTest(false)
const endpoint = "/some/random/endpoint" const endpoint = "/some/random/endpoint"
code, body := sip_test.GetEndpoint(endpoint) code, body := sip_test.GetEndpoint(endpoint)
@ -409,7 +414,7 @@ func TestSignInPageIncludesTargetRedirect(t *testing.T) {
} }
func TestSignInPageDirectAccessRedirectsToRoot(t *testing.T) { func TestSignInPageDirectAccessRedirectsToRoot(t *testing.T) {
sip_test := NewSignInPageTest() sip_test := NewSignInPageTest(false)
code, body := sip_test.GetEndpoint("/oauth2/sign_in") code, body := sip_test.GetEndpoint("/oauth2/sign_in")
assert.Equal(t, 200, code) assert.Equal(t, 200, code)
@ -423,6 +428,34 @@ func TestSignInPageDirectAccessRedirectsToRoot(t *testing.T) {
} }
} }
func TestSignInPageSkipProvider(t *testing.T) {
sip_test := NewSignInPageTest(true)
const endpoint = "/some/random/endpoint"
code, body := sip_test.GetEndpoint(endpoint)
assert.Equal(t, 302, code)
match := sip_test.sign_in_provider_regexp.FindStringSubmatch(body)
if match == nil {
t.Fatal("Did not find pattern in body: " +
signInSkipProvider + "\nBody:\n" + body)
}
}
func TestSignInPageSkipProviderDirect(t *testing.T) {
sip_test := NewSignInPageTest(true)
const endpoint = "/sign_in"
code, body := sip_test.GetEndpoint(endpoint)
assert.Equal(t, 302, code)
match := sip_test.sign_in_provider_regexp.FindStringSubmatch(body)
if match == nil {
t.Fatal("Did not find pattern in body: " +
signInSkipProvider + "\nBody:\n" + body)
}
}
type ProcessCookieTest struct { type ProcessCookieTest struct {
opts *Options opts *Options
proxy *OAuthProxy proxy *OAuthProxy

View File

@ -111,6 +111,7 @@ func NewOptions() *Options {
PassHostHeader: true, PassHostHeader: true,
ApprovalPrompt: "", ApprovalPrompt: "",
RequestLogging: true, RequestLogging: true,
Provider: "google",
} }
} }
@ -125,9 +126,6 @@ func parseURL(to_parse string, urltype string, msgs []string) (*url.URL, []strin
func (o *Options) Validate() error { func (o *Options) Validate() error {
msgs := make([]string, 0) msgs := make([]string, 0)
if len(o.Upstreams) < 1 {
msgs = append(msgs, "missing setting: upstream")
}
if o.CookieSecret == "" { if o.CookieSecret == "" {
msgs = append(msgs, "missing setting: cookie-secret") msgs = append(msgs, "missing setting: cookie-secret")
} }
@ -138,7 +136,8 @@ func (o *Options) Validate() error {
msgs = append(msgs, "missing setting: client-secret") msgs = append(msgs, "missing setting: client-secret")
} }
if o.AuthenticatedEmailsFile == "" && len(o.EmailDomains) == 0 && o.HtpasswdFile == "" { if o.AuthenticatedEmailsFile == "" && len(o.EmailDomains) == 0 && o.HtpasswdFile == "" {
msgs = append(msgs, "missing setting for email validation: email-domain or authenticated-emails-file required.\n use email-domain=* to authorize all email addresses") msgs = append(msgs, "missing setting for email validation: email-domain or authenticated-emails-file required."+
"\n use email-domain=* to authorize all email addresses")
} }
o.redirectURL, msgs = parseURL(o.RedirectURL, "redirect", msgs) o.redirectURL, msgs = parseURL(o.RedirectURL, "redirect", msgs)
@ -146,14 +145,13 @@ func (o *Options) Validate() error {
for _, u := range o.Upstreams { for _, u := range o.Upstreams {
upstreamURL, err := url.Parse(u) upstreamURL, err := url.Parse(u)
if err != nil { if err != nil {
msgs = append(msgs, fmt.Sprintf( msgs = append(msgs, fmt.Sprintf("error parsing upstream: %s", err))
"error parsing upstream=%q %s", } else {
upstreamURL, err)) if upstreamURL.Path == "" {
upstreamURL.Path = "/"
}
o.proxyURLs = append(o.proxyURLs, upstreamURL)
} }
if upstreamURL.Path == "" {
upstreamURL.Path = "/"
}
o.proxyURLs = append(o.proxyURLs, upstreamURL)
} }
for _, u := range o.SkipAuthRegex { for _, u := range o.SkipAuthRegex {

View File

@ -35,7 +35,6 @@ func TestNewOptions(t *testing.T) {
assert.NotEqual(t, nil, err) assert.NotEqual(t, nil, err)
expected := errorMsg([]string{ expected := errorMsg([]string{
"missing setting: upstream",
"missing setting: cookie-secret", "missing setting: cookie-secret",
"missing setting: client-id", "missing setting: client-id",
"missing setting: client-secret"}) "missing setting: client-secret"})

View File

@ -206,7 +206,11 @@ func (p *AzureProvider) GetLoginURL(redirectURI, state string) string {
params.Add("state", state) params.Add("state", state)
params.Set("prompt", p.ApprovalPrompt) params.Set("prompt", p.ApprovalPrompt)
params.Set("nonce", "FIXME") params.Set("nonce", "FIXME")
if p.ProtectedResource != nil && p.ProtectedResource.String() != "" {
params.Add("resource", p.ProtectedResource.String())
}
a.RawQuery = params.Encode() a.RawQuery = params.Encode()
return a.String() return a.String()
} }

View File

@ -351,3 +351,18 @@ func TestAzureRightPermittedGroups(t *testing.T) {
assert.Equal(t, true, result) assert.Equal(t, true, result)
} }
func TestAzureLoginURLnoResource(t *testing.T) {
p := testAzureProvider("")
p.ProtectedResource = nil
result := p.GetLoginURL("http://redirect/url", "state")
assert.Equal(t, "?client_id=&nonce=FIXME&prompt=&redirect_uri=http%3A%2F%2Fredirect%2Furl&response_mode=form_post&response_type=id_token+code&scope=openid&state=state", result)
}
func TestAzureLoginURL(t *testing.T) {
p := testAzureProvider("")
result := p.GetLoginURL("http://redirect/url", "state")
assert.Equal(t, "?client_id=&nonce=FIXME&prompt=&redirect_uri=http%3A%2F%2Fredirect%2Furl&resource=https%3A%2F%2Fgraph.microsoft.com&response_mode=form_post&response_type=id_token+code&scope=openid&state=state", result)
}

View File

@ -57,7 +57,7 @@ func validateToken(p Provider, access_token string, header http.Header) bool {
} }
resp, err := api.RequestUnparsedResponse(endpoint, header) resp, err := api.RequestUnparsedResponse(endpoint, header)
if err != nil { if err != nil {
log.Printf("GET %s", endpoint) log.Printf("GET %s", stripToken(endpoint))
log.Printf("token validation request failed: %s", err) log.Printf("token validation request failed: %s", err)
return false return false
} }