2020-04-13 14:50:34 +02:00
package validation
2015-03-15 18:23:13 +02:00
import (
2015-11-16 05:08:30 +02:00
"crypto"
2016-07-19 21:51:25 +02:00
"fmt"
2020-02-15 15:44:39 +02:00
"io/ioutil"
2015-03-17 21:15:15 +02:00
"net/url"
2020-02-15 15:44:39 +02:00
"os"
2015-03-15 18:23:13 +02:00
"strings"
"testing"
2015-05-09 23:16:19 +02:00
"time"
2015-03-15 18:23:13 +02:00
2020-04-13 14:50:34 +02:00
"github.com/oauth2-proxy/oauth2-proxy/pkg/apis/options"
2017-10-23 18:23:46 +02:00
"github.com/stretchr/testify/assert"
2015-03-15 18:23:13 +02:00
)
2020-03-14 12:14:15 +02:00
const (
2020-02-24 13:23:35 +02:00
cookieSecret = "secretthirtytwobytes+abcdefghijk"
2020-03-14 12:14:15 +02:00
clientID = "bazquux"
clientSecret = "xyzzyplugh"
)
2020-03-14 11:58:29 +02:00
2020-04-13 14:50:34 +02:00
func testOptions ( ) * options . Options {
o := options . NewOptions ( )
2015-03-15 18:23:13 +02:00
o . Upstreams = append ( o . Upstreams , "http://127.0.0.1:8080/" )
2020-04-12 15:00:59 +02:00
o . Cookie . Secret = cookieSecret
2020-03-14 11:58:29 +02:00
o . ClientID = clientID
o . ClientSecret = clientSecret
2015-07-24 22:23:19 +02:00
o . EmailDomains = [ ] string { "*" }
2015-03-15 18:23:13 +02:00
return o
}
2015-03-17 21:15:15 +02:00
func errorMsg ( msgs [ ] string ) string {
2015-03-15 18:23:13 +02:00
result := make ( [ ] string , 0 )
2020-04-14 10:36:44 +02:00
result = append ( result , "invalid configuration:" )
2015-03-15 18:23:13 +02:00
result = append ( result , msgs ... )
return strings . Join ( result , "\n " )
}
func TestNewOptions ( t * testing . T ) {
2020-04-13 14:50:34 +02:00
o := options . NewOptions ( )
2015-07-24 22:23:19 +02:00
o . EmailDomains = [ ] string { "*" }
2020-04-13 14:50:34 +02:00
err := Validate ( o )
2015-03-15 18:23:13 +02:00
assert . NotEqual ( t , nil , err )
expected := errorMsg ( [ ] string {
"missing setting: cookie-secret" ,
"missing setting: client-id" ,
2020-02-15 15:44:39 +02:00
"missing setting: client-secret or client-secret-file" } )
2015-03-15 18:23:13 +02:00
assert . Equal ( t , expected , err . Error ( ) )
}
2020-02-15 15:44:39 +02:00
func TestClientSecretFileOptionFails ( t * testing . T ) {
2020-04-13 14:50:34 +02:00
o := options . NewOptions ( )
2020-04-12 15:00:59 +02:00
o . Cookie . Secret = cookieSecret
2020-03-14 11:58:29 +02:00
o . ClientID = clientID
o . ClientSecretFile = clientSecret
2020-02-15 15:44:39 +02:00
o . EmailDomains = [ ] string { "*" }
2020-04-13 14:50:34 +02:00
err := Validate ( o )
2020-02-15 15:44:39 +02:00
assert . NotEqual ( t , nil , err )
2020-04-13 14:50:34 +02:00
p := o . GetProvider ( ) . Data ( )
2020-03-14 11:58:29 +02:00
assert . Equal ( t , clientSecret , p . ClientSecretFile )
2020-02-15 15:44:39 +02:00
assert . Equal ( t , "" , p . ClientSecret )
s , err := p . GetClientSecret ( )
assert . NotEqual ( t , nil , err )
assert . Equal ( t , "" , s )
}
func TestClientSecretFileOption ( t * testing . T ) {
var err error
f , err := ioutil . TempFile ( "" , "client_secret_temp_file_" )
if err != nil {
t . Fatalf ( "failed to create temp file: %v" , err )
}
f . WriteString ( "testcase" )
if err := f . Close ( ) ; err != nil {
t . Fatalf ( "failed to close temp file: %v" , err )
}
clientSecretFileName := f . Name ( )
defer os . Remove ( clientSecretFileName )
2020-04-13 14:50:34 +02:00
o := options . NewOptions ( )
2020-04-12 15:00:59 +02:00
o . Cookie . Secret = cookieSecret
2020-03-14 11:58:29 +02:00
o . ClientID = clientID
2020-02-15 15:44:39 +02:00
o . ClientSecretFile = clientSecretFileName
o . EmailDomains = [ ] string { "*" }
2020-04-13 14:50:34 +02:00
err = Validate ( o )
2020-02-15 15:44:39 +02:00
assert . Equal ( t , nil , err )
2020-04-13 14:50:34 +02:00
p := o . GetProvider ( ) . Data ( )
2020-02-15 15:44:39 +02:00
assert . Equal ( t , clientSecretFileName , p . ClientSecretFile )
assert . Equal ( t , "" , p . ClientSecret )
s , err := p . GetClientSecret ( )
assert . Equal ( t , nil , err )
assert . Equal ( t , "testcase" , s )
}
2015-08-20 12:07:02 +02:00
func TestGoogleGroupOptions ( t * testing . T ) {
o := testOptions ( )
o . GoogleGroups = [ ] string { "googlegroup" }
2020-04-13 14:50:34 +02:00
err := Validate ( o )
2015-08-20 12:07:02 +02:00
assert . NotEqual ( t , nil , err )
expected := errorMsg ( [ ] string {
"missing setting: google-admin-email" ,
"missing setting: google-service-account-json" } )
assert . Equal ( t , expected , err . Error ( ) )
}
func TestGoogleGroupInvalidFile ( t * testing . T ) {
o := testOptions ( )
o . GoogleGroups = [ ] string { "test_group" }
o . GoogleAdminEmail = "admin@example.com"
o . GoogleServiceAccountJSON = "file_doesnt_exist.json"
2020-04-13 14:50:34 +02:00
err := Validate ( o )
2015-08-20 12:07:02 +02:00
assert . NotEqual ( t , nil , err )
expected := errorMsg ( [ ] string {
"invalid Google credentials file: file_doesnt_exist.json" ,
} )
assert . Equal ( t , expected , err . Error ( ) )
}
2015-03-15 18:23:13 +02:00
func TestInitializedOptions ( t * testing . T ) {
o := testOptions ( )
2020-04-13 14:50:34 +02:00
assert . Equal ( t , nil , Validate ( o ) )
2015-03-15 18:23:13 +02:00
}
// Note that it's not worth testing nonparseable URLs, since url.Parse()
// seems to parse damn near anything.
2015-11-09 01:47:44 +02:00
func TestRedirectURL ( t * testing . T ) {
2015-03-15 18:23:13 +02:00
o := testOptions ( )
2020-04-13 14:50:34 +02:00
o . RawRedirectURL = "https://myhost.com/oauth2/callback"
assert . Equal ( t , nil , Validate ( o ) )
2015-03-15 18:23:13 +02:00
expected := & url . URL {
Scheme : "https" , Host : "myhost.com" , Path : "/oauth2/callback" }
2020-04-13 14:50:34 +02:00
assert . Equal ( t , expected , o . GetRedirectURL ( ) )
2015-03-15 18:23:13 +02:00
}
2015-11-09 01:47:44 +02:00
func TestProxyURLs ( t * testing . T ) {
2015-03-15 18:23:13 +02:00
o := testOptions ( )
o . Upstreams = append ( o . Upstreams , "http://127.0.0.1:8081" )
2020-04-13 14:50:34 +02:00
assert . Equal ( t , nil , Validate ( o ) )
2015-03-15 18:23:13 +02:00
expected := [ ] * url . URL {
2018-11-29 16:26:41 +02:00
{ Scheme : "http" , Host : "127.0.0.1:8080" , Path : "/" } ,
2015-03-15 18:23:13 +02:00
// note the '/' was added
2018-11-29 16:26:41 +02:00
{ Scheme : "http" , Host : "127.0.0.1:8081" , Path : "/" } ,
2015-03-15 18:23:13 +02:00
}
2020-04-13 14:50:34 +02:00
assert . Equal ( t , expected , o . GetProxyURLs ( ) )
2015-03-15 18:23:13 +02:00
}
2017-05-05 21:47:40 +02:00
func TestProxyURLsError ( t * testing . T ) {
o := testOptions ( )
o . Upstreams = append ( o . Upstreams , "127.0.0.1:8081" )
2020-04-13 14:50:34 +02:00
err := Validate ( o )
2017-05-05 21:47:40 +02:00
assert . NotEqual ( t , nil , err )
2020-03-14 12:14:15 +02:00
assert . Contains ( t , err . Error ( ) , "error parsing upstream" )
2017-05-05 21:47:40 +02:00
}
2015-03-15 18:23:13 +02:00
func TestCompiledRegex ( t * testing . T ) {
o := testOptions ( )
regexps := [ ] string { "/foo/.*" , "/ba[rz]/quux" }
o . SkipAuthRegex = regexps
2020-04-13 14:50:34 +02:00
assert . Equal ( t , nil , Validate ( o ) )
2015-03-15 18:23:13 +02:00
actual := make ( [ ] string , 0 )
2020-04-13 14:50:34 +02:00
for _ , regex := range o . GetCompiledRegex ( ) {
2015-03-15 18:23:13 +02:00
actual = append ( actual , regex . String ( ) )
}
assert . Equal ( t , regexps , actual )
}
func TestCompiledRegexError ( t * testing . T ) {
o := testOptions ( )
o . SkipAuthRegex = [ ] string { "(foobaz" , "barquux)" }
2020-04-13 14:50:34 +02:00
err := Validate ( o )
2015-03-15 18:23:13 +02:00
assert . NotEqual ( t , nil , err )
expected := errorMsg ( [ ] string {
"error compiling regex=\"(foobaz\" error parsing regexp: " +
"missing closing ): `(foobaz`" ,
"error compiling regex=\"barquux)\" error parsing regexp: " +
"unexpected ): `barquux)`" } )
assert . Equal ( t , expected , err . Error ( ) )
2017-05-05 21:47:40 +02:00
o . SkipAuthRegex = [ ] string { "foobaz" , "barquux)" }
2020-04-13 14:50:34 +02:00
err = Validate ( o )
2017-05-05 21:47:40 +02:00
assert . NotEqual ( t , nil , err )
expected = errorMsg ( [ ] string {
"error compiling regex=\"barquux)\" error parsing regexp: " +
"unexpected ): `barquux)`" } )
assert . Equal ( t , expected , err . Error ( ) )
2015-03-15 18:23:13 +02:00
}
2015-03-30 21:48:30 +02:00
func TestDefaultProviderApiSettings ( t * testing . T ) {
o := testOptions ( )
2020-04-13 14:50:34 +02:00
assert . Equal ( t , nil , Validate ( o ) )
p := o . GetProvider ( ) . Data ( )
2015-06-23 19:23:47 +02:00
assert . Equal ( t , "https://accounts.google.com/o/oauth2/auth?access_type=offline" ,
2015-11-09 01:47:44 +02:00
p . LoginURL . String ( ) )
2015-06-23 19:23:47 +02:00
assert . Equal ( t , "https://www.googleapis.com/oauth2/v3/token" ,
2015-11-09 01:47:44 +02:00
p . RedeemURL . String ( ) )
assert . Equal ( t , "" , p . ProfileURL . String ( ) )
2015-03-30 21:48:30 +02:00
assert . Equal ( t , "profile email" , p . Scope )
}
2015-04-05 15:43:40 +02:00
func TestPassAccessTokenRequiresSpecificCookieSecretLengths ( t * testing . T ) {
o := testOptions ( )
2020-04-13 14:50:34 +02:00
assert . Equal ( t , nil , Validate ( o ) )
2015-04-05 15:43:40 +02:00
assert . Equal ( t , false , o . PassAccessToken )
o . PassAccessToken = true
2020-04-12 15:00:59 +02:00
o . Cookie . Secret = "cookie of invalid length-"
2020-04-13 14:50:34 +02:00
assert . NotEqual ( t , nil , Validate ( o ) )
2015-04-05 15:43:40 +02:00
2015-05-09 23:31:13 +02:00
o . PassAccessToken = false
2020-04-12 15:00:59 +02:00
o . Cookie . Refresh = time . Duration ( 24 ) * time . Hour
2020-04-13 14:50:34 +02:00
assert . NotEqual ( t , nil , Validate ( o ) )
2015-05-09 23:31:13 +02:00
2020-04-12 15:00:59 +02:00
o . Cookie . Secret = "16 bytes AES-128"
2020-04-13 14:50:34 +02:00
assert . Equal ( t , nil , Validate ( o ) )
2015-04-05 15:43:40 +02:00
2020-04-12 15:00:59 +02:00
o . Cookie . Secret = "24 byte secret AES-192--"
2020-04-13 14:50:34 +02:00
assert . Equal ( t , nil , Validate ( o ) )
2015-04-05 15:43:40 +02:00
2020-04-12 15:00:59 +02:00
o . Cookie . Secret = "32 byte secret for AES-256------"
2020-04-13 14:50:34 +02:00
assert . Equal ( t , nil , Validate ( o ) )
2015-04-05 15:43:40 +02:00
}
2015-05-09 23:16:19 +02:00
func TestCookieRefreshMustBeLessThanCookieExpire ( t * testing . T ) {
o := testOptions ( )
2020-04-13 14:50:34 +02:00
assert . Equal ( t , nil , Validate ( o ) )
2015-05-09 23:16:19 +02:00
2020-05-21 20:29:45 +02:00
o . Cookie . Secret = "0123456789abcdef"
2020-04-12 15:00:59 +02:00
o . Cookie . Refresh = o . Cookie . Expire
2020-04-13 14:50:34 +02:00
assert . NotEqual ( t , nil , Validate ( o ) )
2015-05-09 23:16:19 +02:00
2020-04-12 15:00:59 +02:00
o . Cookie . Refresh -= time . Duration ( 1 )
2020-04-13 14:50:34 +02:00
assert . Equal ( t , nil , Validate ( o ) )
2015-05-09 23:16:19 +02:00
}
2015-11-16 05:08:30 +02:00
2016-06-20 13:17:39 +02:00
func TestBase64CookieSecret ( t * testing . T ) {
o := testOptions ( )
2020-04-13 14:50:34 +02:00
assert . Equal ( t , nil , Validate ( o ) )
2016-06-20 13:17:39 +02:00
// 32 byte, base64 (urlsafe) encoded key
2020-04-12 15:00:59 +02:00
o . Cookie . Secret = "yHBw2lh2Cvo6aI_jn_qMTr-pRAjtq0nzVgDJNb36jgQ="
2020-04-13 14:50:34 +02:00
assert . Equal ( t , nil , Validate ( o ) )
2016-06-20 13:17:39 +02:00
// 32 byte, base64 (urlsafe) encoded key, w/o padding
2020-04-12 15:00:59 +02:00
o . Cookie . Secret = "yHBw2lh2Cvo6aI_jn_qMTr-pRAjtq0nzVgDJNb36jgQ"
2020-04-13 14:50:34 +02:00
assert . Equal ( t , nil , Validate ( o ) )
2016-06-20 13:17:39 +02:00
// 24 byte, base64 (urlsafe) encoded key
2020-04-12 15:00:59 +02:00
o . Cookie . Secret = "Kp33Gj-GQmYtz4zZUyUDdqQKx5_Hgkv3"
2020-04-13 14:50:34 +02:00
assert . Equal ( t , nil , Validate ( o ) )
2016-06-20 13:17:39 +02:00
// 16 byte, base64 (urlsafe) encoded key
2020-04-12 15:00:59 +02:00
o . Cookie . Secret = "LFEqZYvYUwKwzn0tEuTpLA=="
2020-04-13 14:50:34 +02:00
assert . Equal ( t , nil , Validate ( o ) )
2016-06-20 13:17:39 +02:00
// 16 byte, base64 (urlsafe) encoded key, w/o padding
2020-04-12 15:00:59 +02:00
o . Cookie . Secret = "LFEqZYvYUwKwzn0tEuTpLA"
2020-04-13 14:50:34 +02:00
assert . Equal ( t , nil , Validate ( o ) )
2016-06-20 13:17:39 +02:00
}
2015-11-16 05:08:30 +02:00
func TestValidateSignatureKey ( t * testing . T ) {
o := testOptions ( )
o . SignatureKey = "sha1:secret"
2020-04-13 14:50:34 +02:00
assert . Equal ( t , nil , Validate ( o ) )
assert . Equal ( t , o . GetSignatureData ( ) . Hash , crypto . SHA1 )
assert . Equal ( t , o . GetSignatureData ( ) . Key , "secret" )
2015-11-16 05:08:30 +02:00
}
func TestValidateSignatureKeyInvalidSpec ( t * testing . T ) {
o := testOptions ( )
o . SignatureKey = "invalid spec"
2020-04-13 14:50:34 +02:00
err := Validate ( o )
2020-04-14 10:36:44 +02:00
assert . Equal ( t , err . Error ( ) , "invalid configuration:\n" +
2015-11-16 05:08:30 +02:00
" invalid signature hash:key spec: " + o . SignatureKey )
}
func TestValidateSignatureKeyUnsupportedAlgorithm ( t * testing . T ) {
o := testOptions ( )
o . SignatureKey = "unsupported:default secret"
2020-04-13 14:50:34 +02:00
err := Validate ( o )
2020-04-14 10:36:44 +02:00
assert . Equal ( t , err . Error ( ) , "invalid configuration:\n" +
2015-11-16 05:08:30 +02:00
" unsupported signature hash algorithm: " + o . SignatureKey )
}
2016-07-19 21:51:25 +02:00
func TestValidateCookie ( t * testing . T ) {
o := testOptions ( )
2020-04-12 15:00:59 +02:00
o . Cookie . Name = "_valid_cookie_name"
2020-04-13 14:50:34 +02:00
assert . Equal ( t , nil , Validate ( o ) )
2016-07-19 21:51:25 +02:00
}
func TestValidateCookieBadName ( t * testing . T ) {
o := testOptions ( )
2020-04-12 15:00:59 +02:00
o . Cookie . Name = "_bad_cookie_name{}"
2020-04-13 14:50:34 +02:00
err := Validate ( o )
2020-04-14 10:36:44 +02:00
assert . Equal ( t , err . Error ( ) , "invalid configuration:\n" +
2020-04-12 15:00:59 +02:00
fmt . Sprintf ( " invalid cookie name: %q" , o . Cookie . Name ) )
2016-07-19 21:51:25 +02:00
}
2019-03-04 15:54:22 +02:00
func TestSkipOIDCDiscovery ( t * testing . T ) {
o := testOptions ( )
2020-04-13 14:50:34 +02:00
o . ProviderType = "oidc"
2019-03-04 15:54:22 +02:00
o . OIDCIssuerURL = "https://login.microsoftonline.com/fabrikamb2c.onmicrosoft.com/v2.0/"
o . SkipOIDCDiscovery = true
2020-04-13 14:50:34 +02:00
err := Validate ( o )
2020-04-14 10:36:44 +02:00
assert . Equal ( t , "invalid configuration:\n" +
" missing setting: login-url\n missing setting: redeem-url\n missing setting: oidc-jwks-url" , err . Error ( ) )
2019-03-04 15:54:22 +02:00
o . LoginURL = "https://login.microsoftonline.com/fabrikamb2c.onmicrosoft.com/oauth2/v2.0/authorize?p=b2c_1_sign_in"
o . RedeemURL = "https://login.microsoftonline.com/fabrikamb2c.onmicrosoft.com/oauth2/v2.0/token?p=b2c_1_sign_in"
o . OIDCJwksURL = "https://login.microsoftonline.com/fabrikamb2c.onmicrosoft.com/discovery/v2.0/keys"
2020-04-13 14:50:34 +02:00
assert . Equal ( t , nil , Validate ( o ) )
2019-03-04 15:54:22 +02:00
}
2019-03-25 18:56:56 +02:00
func TestGCPHealthcheck ( t * testing . T ) {
o := testOptions ( )
o . GCPHealthChecks = true
2020-04-13 14:50:34 +02:00
assert . Equal ( t , nil , Validate ( o ) )
2019-03-25 18:56:56 +02:00
}
2020-05-12 19:41:25 +02:00
func TestRealClientIPHeader ( t * testing . T ) {
// Ensure nil if ReverseProxy not set.
2020-04-13 14:50:34 +02:00
o := testOptions ( )
2020-05-12 19:41:25 +02:00
o . RealClientIPHeader = "X-Real-IP"
2020-04-13 14:50:34 +02:00
assert . Equal ( t , nil , Validate ( o ) )
assert . Nil ( t , o . GetRealClientIPParser ( ) )
2020-05-12 19:41:25 +02:00
// Ensure simple use case works.
o = testOptions ( )
o . ReverseProxy = true
o . RealClientIPHeader = "X-Forwarded-For"
2020-04-13 14:50:34 +02:00
assert . Equal ( t , nil , Validate ( o ) )
assert . NotNil ( t , o . GetRealClientIPParser ( ) )
2020-05-12 19:41:25 +02:00
// Ensure unknown header format process an error.
o = testOptions ( )
o . ReverseProxy = true
o . RealClientIPHeader = "Forwarded"
2020-04-13 14:50:34 +02:00
err := Validate ( o )
2020-05-12 19:41:25 +02:00
assert . NotEqual ( t , nil , err )
2020-04-13 14:50:34 +02:00
expected := errorMsg ( [ ] string {
2020-05-12 19:41:25 +02:00
"real_client_ip_header (Forwarded) not accepted parameter value: the http header key (Forwarded) is either invalid or unsupported" ,
} )
assert . Equal ( t , expected , err . Error ( ) )
2020-04-13 14:50:34 +02:00
assert . Nil ( t , o . GetRealClientIPParser ( ) )
2020-05-12 19:41:25 +02:00
// Ensure invalid header format produces an error.
o = testOptions ( )
o . ReverseProxy = true
o . RealClientIPHeader = "!934invalidheader-23:"
2020-04-13 14:50:34 +02:00
err = Validate ( o )
2020-05-12 19:41:25 +02:00
assert . NotEqual ( t , nil , err )
expected = errorMsg ( [ ] string {
"real_client_ip_header (!934invalidheader-23:) not accepted parameter value: the http header key (!934invalidheader-23:) is either invalid or unsupported" ,
} )
assert . Equal ( t , expected , err . Error ( ) )
2020-04-13 14:50:34 +02:00
assert . Nil ( t , o . GetRealClientIPParser ( ) )
2020-05-12 19:41:25 +02:00
}
2020-07-03 17:09:17 +02:00
func TestProviderCAFilesError ( t * testing . T ) {
file , err := ioutil . TempFile ( "" , "absent.*.crt" )
assert . NoError ( t , err )
assert . NoError ( t , file . Close ( ) )
assert . NoError ( t , os . Remove ( file . Name ( ) ) )
o := testOptions ( )
o . ProviderCAFiles = append ( o . ProviderCAFiles , file . Name ( ) )
err = Validate ( o )
assert . Error ( t , err )
assert . Contains ( t , err . Error ( ) , "unable to load provider CA file(s)" )
}