2014-12-01 04:12:33 +03:00
package main
import (
2019-04-27 05:16:45 +03:00
"context"
2015-11-16 06:08:30 +03:00
"crypto"
2015-07-24 12:17:43 +03:00
"encoding/base64"
2019-04-24 18:25:29 +03:00
"fmt"
2015-11-16 06:08:30 +03:00
"io"
2014-12-01 04:12:33 +03:00
"net/http"
"net/http/httptest"
"net/url"
2015-04-07 05:10:03 +03:00
"regexp"
2015-04-03 03:57:17 +03:00
"strings"
2014-12-01 04:12:33 +03:00
"testing"
2015-04-03 03:57:17 +03:00
"time"
2017-06-22 01:02:34 +03:00
2021-07-17 19:55:05 +03:00
"github.com/coreos/go-oidc/v3/oidc"
2017-09-13 01:59:00 +03:00
"github.com/mbland/hmacauth"
2020-09-29 19:44:42 +03:00
"github.com/oauth2-proxy/oauth2-proxy/v7/pkg/apis/options"
"github.com/oauth2-proxy/oauth2-proxy/v7/pkg/apis/sessions"
2021-04-21 12:33:27 +03:00
"github.com/oauth2-proxy/oauth2-proxy/v7/pkg/cookies"
2020-09-29 19:44:42 +03:00
"github.com/oauth2-proxy/oauth2-proxy/v7/pkg/logger"
2022-02-15 20:24:48 +03:00
internaloidc "github.com/oauth2-proxy/oauth2-proxy/v7/pkg/providers/oidc"
2020-09-29 19:44:42 +03:00
sessionscookie "github.com/oauth2-proxy/oauth2-proxy/v7/pkg/sessions/cookie"
"github.com/oauth2-proxy/oauth2-proxy/v7/pkg/upstream"
"github.com/oauth2-proxy/oauth2-proxy/v7/pkg/validation"
"github.com/oauth2-proxy/oauth2-proxy/v7/providers"
2017-10-23 19:23:46 +03:00
"github.com/stretchr/testify/assert"
2021-11-12 21:36:29 +03:00
"github.com/stretchr/testify/require"
2014-12-01 04:12:33 +03:00
)
2020-02-24 14:23:35 +03:00
const (
// The rawCookieSecret is 32 bytes and the base64CookieSecret is the base64
// encoded version of this.
rawCookieSecret = "secretthirtytwobytes+abcdefghijk"
2020-05-25 16:00:49 +03:00
base64CookieSecret = "c2VjcmV0dGhpcnR5dHdvYnl0ZXMrYWJjZGVmZ2hpams"
2020-07-13 22:56:05 +03:00
clientID = "3984n253984d7348dm8234yf982t"
clientSecret = "gv3498mfc9t23y23974dm2394dm9"
2020-02-24 14:23:35 +03:00
)
2015-05-21 06:23:48 +03:00
func init ( ) {
2019-02-10 19:37:45 +03:00
logger . SetFlags ( logger . Lshortfile )
2015-05-21 06:23:48 +03:00
}
2015-05-10 22:15:52 +03:00
func TestRobotsTxt ( t * testing . T ) {
2020-05-25 16:00:49 +03:00
opts := baseTestOptions ( )
2020-07-13 22:56:05 +03:00
err := validation . Validate ( opts )
assert . NoError ( t , err )
2015-05-10 22:15:52 +03:00
2020-05-25 16:00:49 +03:00
proxy , err := NewOAuthProxy ( opts , func ( string ) bool { return true } )
2020-07-13 22:56:05 +03:00
if err != nil {
t . Fatal ( err )
}
2015-05-10 22:15:52 +03:00
rw := httptest . NewRecorder ( )
req , _ := http . NewRequest ( "GET" , "/robots.txt" , nil )
proxy . ServeHTTP ( rw , req )
assert . Equal ( t , 200 , rw . Code )
2021-03-13 12:55:54 +03:00
assert . Equal ( t , "User-agent: *\nDisallow: /\n" , rw . Body . String ( ) )
2015-05-10 22:15:52 +03:00
}
2015-11-16 06:08:30 +03:00
type TestProvider struct {
* providers . ProviderData
2019-06-21 02:57:20 +03:00
EmailAddress string
ValidToken bool
GroupValidator func ( string ) bool
2015-11-16 06:08:30 +03:00
}
2020-05-05 18:53:33 +03:00
var _ providers . Provider = ( * TestProvider ) ( nil )
2018-11-29 17:26:41 +03:00
func NewTestProvider ( providerURL * url . URL , emailAddress string ) * TestProvider {
2015-11-16 06:08:30 +03:00
return & TestProvider {
ProviderData : & providers . ProviderData {
ProviderName : "Test Provider" ,
LoginURL : & url . URL {
Scheme : "http" ,
2018-11-29 17:26:41 +03:00
Host : providerURL . Host ,
2015-11-16 06:08:30 +03:00
Path : "/oauth/authorize" ,
} ,
RedeemURL : & url . URL {
Scheme : "http" ,
2018-11-29 17:26:41 +03:00
Host : providerURL . Host ,
2015-11-16 06:08:30 +03:00
Path : "/oauth/token" ,
} ,
ProfileURL : & url . URL {
Scheme : "http" ,
2018-11-29 17:26:41 +03:00
Host : providerURL . Host ,
2015-11-16 06:08:30 +03:00
Path : "/api/v1/profile" ,
} ,
Scope : "profile.email" ,
} ,
2018-11-29 17:26:41 +03:00
EmailAddress : emailAddress ,
2019-06-21 02:57:20 +03:00
GroupValidator : func ( s string ) bool {
return true
} ,
2015-11-16 06:08:30 +03:00
}
}
2020-09-27 21:46:29 +03:00
func ( tp * TestProvider ) GetEmailAddress ( _ context . Context , _ * sessions . SessionState ) ( string , error ) {
2015-11-16 06:08:30 +03:00
return tp . EmailAddress , nil
}
2020-10-24 08:06:50 +03:00
func ( tp * TestProvider ) ValidateSession ( _ context . Context , _ * sessions . SessionState ) bool {
2015-11-16 06:08:30 +03:00
return tp . ValidToken
}
2020-09-27 01:17:26 +03:00
func Test_redeemCode ( t * testing . T ) {
opts := baseTestOptions ( )
err := validation . Validate ( opts )
assert . NoError ( t , err )
proxy , err := NewOAuthProxy ( opts , func ( string ) bool { return true } )
if err != nil {
t . Fatal ( err )
}
2021-01-03 01:20:48 +03:00
req := httptest . NewRequest ( http . MethodGet , "/" , nil )
2022-03-13 13:08:33 +03:00
_ , err = proxy . redeemCode ( req , "" )
2021-01-03 01:20:48 +03:00
assert . Equal ( t , providers . ErrMissingCode , err )
2020-09-27 01:17:26 +03:00
}
func Test_enrichSession ( t * testing . T ) {
const (
sessionUser = "Mr Session"
sessionEmail = "session@example.com"
providerEmail = "provider@example.com"
)
testCases := map [ string ] struct {
session * sessions . SessionState
expectedUser string
expectedEmail string
} {
"Session already has enrichable fields" : {
session : & sessions . SessionState {
User : sessionUser ,
Email : sessionEmail ,
} ,
expectedUser : sessionUser ,
expectedEmail : sessionEmail ,
} ,
"Session is missing Email and GetEmailAddress is implemented" : {
session : & sessions . SessionState {
User : sessionUser ,
} ,
expectedUser : sessionUser ,
expectedEmail : providerEmail ,
} ,
"Session is missing User and GetUserName is not implemented" : {
session : & sessions . SessionState {
Email : sessionEmail ,
} ,
expectedUser : "" ,
expectedEmail : sessionEmail ,
} ,
}
for name , tc := range testCases {
t . Run ( name , func ( t * testing . T ) {
opts := baseTestOptions ( )
err := validation . Validate ( opts )
assert . NoError ( t , err )
proxy , err := NewOAuthProxy ( opts , func ( string ) bool { return true } )
if err != nil {
t . Fatal ( err )
}
2022-02-15 15:00:06 +03:00
proxy . provider = NewTestProvider ( & url . URL { Host : "www.example.com" } , providerEmail )
2020-09-27 01:17:26 +03:00
2020-09-27 21:46:29 +03:00
err = proxy . enrichSessionState ( context . Background ( ) , tc . session )
2020-09-27 01:17:26 +03:00
assert . NoError ( t , err )
assert . Equal ( t , tc . expectedUser , tc . session . User )
assert . Equal ( t , tc . expectedEmail , tc . session . Email )
} )
}
}
2015-07-24 12:17:43 +03:00
func TestBasicAuthPassword ( t * testing . T ) {
2018-11-29 17:26:41 +03:00
providerServer := httptest . NewServer ( http . HandlerFunc ( func ( w http . ResponseWriter , r * http . Request ) {
2019-02-10 19:37:45 +03:00
logger . Printf ( "%#v" , r )
2018-11-29 17:26:41 +03:00
var payload string
switch r . URL . Path {
2015-07-24 12:17:43 +03:00
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 )
2020-07-13 22:56:05 +03:00
_ , err := w . Write ( [ ] byte ( payload ) )
if err != nil {
t . Fatal ( err )
}
2015-07-24 12:17:43 +03:00
} ) )
2020-07-29 22:10:14 +03:00
basicAuthPassword := "This is a secure password"
2020-05-25 16:00:49 +03:00
opts := baseTestOptions ( )
2021-09-17 14:08:18 +03:00
opts . UpstreamServers = options . UpstreamConfig {
Upstreams : [ ] options . Upstream {
2021-08-09 16:32:15 +03:00
{
ID : providerServer . URL ,
Path : "/" ,
URI : providerServer . URL ,
} ,
2020-05-26 22:06:27 +03:00
} ,
}
2020-04-12 16:00:59 +03:00
opts . Cookie . Secure = false
2020-07-29 22:10:14 +03:00
opts . InjectRequestHeaders = [ ] options . Header {
{
Name : "Authorization" ,
Values : [ ] options . HeaderValue {
{
ClaimSource : & options . ClaimSource {
Claim : "email" ,
BasicAuthPassword : & options . SecretSource {
2020-11-19 22:58:50 +03:00
Value : [ ] byte ( basicAuthPassword ) ,
2020-07-29 22:10:14 +03:00
} ,
} ,
} ,
} ,
} ,
}
2020-07-13 22:56:05 +03:00
err := validation . Validate ( opts )
assert . NoError ( t , err )
2015-07-24 12:17:43 +03:00
2018-11-29 17:26:41 +03:00
providerURL , _ := url . Parse ( providerServer . URL )
2019-05-07 12:36:00 +03:00
const emailAddress = "john.doe@example.com"
2015-07-24 12:17:43 +03:00
2020-05-25 16:00:49 +03:00
proxy , err := NewOAuthProxy ( opts , func ( email string ) bool {
2018-11-29 17:26:41 +03:00
return email == emailAddress
2015-07-24 12:17:43 +03:00
} )
2020-07-13 22:56:05 +03:00
if err != nil {
t . Fatal ( err )
}
2022-02-15 15:00:06 +03:00
proxy . provider = NewTestProvider ( providerURL , emailAddress )
2015-07-24 12:17:43 +03:00
2020-07-29 22:10:14 +03:00
// Save the required session
2015-07-24 12:17:43 +03:00
rw := httptest . NewRecorder ( )
2020-07-29 22:10:14 +03:00
req , _ := http . NewRequest ( "GET" , "/" , nil )
err = proxy . sessionStore . Save ( rw , req , & sessions . SessionState {
Email : emailAddress ,
2015-07-24 12:17:43 +03:00
} )
2020-07-29 22:10:14 +03:00
assert . NoError ( t , err )
// Extract the cookie value to inject into the test request
cookie := rw . Header ( ) . Values ( "Set-Cookie" ) [ 0 ]
2015-07-24 12:17:43 +03:00
2020-07-29 22:10:14 +03:00
req , _ = http . NewRequest ( "GET" , "/" , nil )
req . Header . Set ( "Cookie" , cookie )
2015-07-24 12:17:43 +03:00
rw = httptest . NewRecorder ( )
proxy . ServeHTTP ( rw , req )
2017-03-28 04:14:38 +03:00
2019-05-09 12:14:01 +03:00
// The username in the basic auth credentials is expected to be equal to the email address from the
// auth response, so we use the same variable here.
2020-07-29 22:10:14 +03:00
expectedHeader := "Basic " + base64 . StdEncoding . EncodeToString ( [ ] byte ( emailAddress + ":" + basicAuthPassword ) )
2015-07-24 12:17:43 +03:00
assert . Equal ( t , expectedHeader , rw . Body . String ( ) )
2018-11-29 17:26:41 +03:00
providerServer . Close ( )
2015-07-24 12:17:43 +03:00
}
2020-07-29 22:10:14 +03:00
func TestPassGroupsHeadersWithGroups ( t * testing . T ) {
2020-05-25 16:00:49 +03:00
opts := baseTestOptions ( )
2020-07-29 22:10:14 +03:00
opts . InjectRequestHeaders = [ ] options . Header {
{
Name : "X-Forwarded-Groups" ,
Values : [ ] options . HeaderValue {
{
ClaimSource : & options . ClaimSource {
Claim : "groups" ,
} ,
} ,
} ,
} ,
2020-03-04 01:27:43 +03:00
}
2020-02-29 20:38:32 +03:00
2020-07-28 21:42:09 +03:00
err := validation . Validate ( opts )
assert . NoError ( t , err )
const emailAddress = "john.doe@example.com"
const userName = "9fcab5c9b889a557"
groups := [ ] string { "a" , "b" }
created := time . Now ( )
session := & sessions . SessionState {
User : userName ,
Groups : groups ,
Email : emailAddress ,
AccessToken : "oauth_token" ,
CreatedAt : & created ,
}
2020-07-29 22:10:14 +03:00
proxy , err := NewOAuthProxy ( opts , func ( email string ) bool {
return email == emailAddress
} )
assert . NoError ( t , err )
2020-07-15 01:46:44 +03:00
2020-07-29 22:10:14 +03:00
// Save the required session
rw := httptest . NewRecorder ( )
req , _ := http . NewRequest ( "GET" , "/" , nil )
err = proxy . sessionStore . Save ( rw , req , session )
assert . NoError ( t , err )
2020-07-15 01:46:44 +03:00
2020-07-29 22:10:14 +03:00
// Extract the cookie value to inject into the test request
cookie := rw . Header ( ) . Values ( "Set-Cookie" ) [ 0 ]
2020-07-15 01:46:44 +03:00
2020-07-29 22:10:14 +03:00
req , _ = http . NewRequest ( "GET" , "/" , nil )
req . Header . Set ( "Cookie" , cookie )
rw = httptest . NewRecorder ( )
proxy . ServeHTTP ( rw , req )
2020-07-15 01:46:44 +03:00
2021-01-22 11:48:34 +03:00
assert . Equal ( t , [ ] string { "a,b" } , req . Header [ "X-Forwarded-Groups" ] )
2020-07-15 01:46:44 +03:00
}
2015-04-03 03:57:17 +03:00
type PassAccessTokenTest struct {
2018-11-29 17:26:41 +03:00
providerServer * httptest . Server
proxy * OAuthProxy
2020-04-13 15:50:34 +03:00
opts * options . Options
2015-04-03 03:57:17 +03:00
}
type PassAccessTokenTestOptions struct {
PassAccessToken bool
2021-11-12 21:36:29 +03:00
ValidToken bool
2020-05-26 22:06:27 +03:00
ProxyUpstream options . Upstream
2015-04-03 03:57:17 +03:00
}
2020-07-13 22:56:05 +03:00
func NewPassAccessTokenTest ( opts PassAccessTokenTestOptions ) ( * PassAccessTokenTest , error ) {
patt := & PassAccessTokenTest { }
2015-04-03 03:57:17 +03:00
2020-07-13 22:56:05 +03:00
patt . providerServer = httptest . NewServer (
2015-04-03 03:57:17 +03:00
http . HandlerFunc ( func ( w http . ResponseWriter , r * http . Request ) {
2018-11-29 17:26:41 +03:00
var payload string
switch r . URL . Path {
2015-04-03 03:57:17 +03:00
case "/oauth/token" :
payload = ` { "access_token": "my_auth_token"} `
default :
2015-05-21 06:23:48 +03:00
payload = r . Header . Get ( "X-Forwarded-Access-Token" )
if payload == "" {
2015-04-03 03:57:17 +03:00
payload = "No access token found."
}
}
w . WriteHeader ( 200 )
2020-07-13 22:56:05 +03:00
_ , err := w . Write ( [ ] byte ( payload ) )
if err != nil {
panic ( err )
}
2015-04-03 03:57:17 +03:00
} ) )
2020-07-13 22:56:05 +03:00
patt . opts = baseTestOptions ( )
2021-09-17 14:08:18 +03:00
patt . opts . UpstreamServers = options . UpstreamConfig {
Upstreams : [ ] options . Upstream {
2021-08-09 16:32:15 +03:00
{
ID : patt . providerServer . URL ,
Path : "/" ,
URI : patt . providerServer . URL ,
} ,
2020-05-26 22:06:27 +03:00
} ,
2020-07-13 22:56:05 +03:00
}
2020-05-26 22:06:27 +03:00
if opts . ProxyUpstream . ID != "" {
2021-09-17 14:08:18 +03:00
patt . opts . UpstreamServers . Upstreams = append ( patt . opts . UpstreamServers . Upstreams , opts . ProxyUpstream )
2020-05-26 22:06:27 +03:00
}
2020-07-13 22:56:05 +03:00
patt . opts . Cookie . Secure = false
2020-07-29 22:10:14 +03:00
if opts . PassAccessToken {
patt . opts . InjectRequestHeaders = [ ] options . Header {
{
Name : "X-Forwarded-Access-Token" ,
Values : [ ] options . HeaderValue {
{
ClaimSource : & options . ClaimSource {
Claim : "access_token" ,
} ,
} ,
} ,
} ,
}
}
2020-07-13 22:56:05 +03:00
err := validation . Validate ( patt . opts )
if err != nil {
return nil , err
}
providerURL , _ := url . Parse ( patt . providerServer . URL )
2018-11-29 17:26:41 +03:00
const emailAddress = "michael.bland@gsa.gov"
2015-04-03 03:57:17 +03:00
2021-11-12 21:36:29 +03:00
testProvider := NewTestProvider ( providerURL , emailAddress )
testProvider . ValidToken = opts . ValidToken
2020-07-13 22:56:05 +03:00
patt . proxy , err = NewOAuthProxy ( patt . opts , func ( email string ) bool {
2018-11-29 17:26:41 +03:00
return email == emailAddress
2015-04-03 03:57:17 +03:00
} )
2022-02-15 15:00:06 +03:00
patt . proxy . provider = testProvider
2020-05-25 16:00:49 +03:00
if err != nil {
2020-07-13 22:56:05 +03:00
return nil , err
2020-05-25 16:00:49 +03:00
}
2020-07-13 22:56:05 +03:00
return patt , nil
2015-04-03 03:57:17 +03:00
}
2018-11-29 17:26:41 +03:00
func ( patTest * PassAccessTokenTest ) Close ( ) {
patTest . providerServer . Close ( )
2015-04-03 03:57:17 +03:00
}
2021-04-21 12:33:27 +03:00
func ( patTest * PassAccessTokenTest ) getCallbackEndpoint ( ) ( httpCode int , cookie string ) {
2015-04-03 03:57:17 +03:00
rw := httptest . NewRecorder ( )
2021-04-21 12:33:27 +03:00
2022-03-13 13:08:33 +03:00
csrf , err := cookies . NewCSRF ( patTest . proxy . CookieOptions , "" )
2021-04-21 12:33:27 +03:00
if err != nil {
panic ( err )
}
req , err := http . NewRequest (
http . MethodGet ,
fmt . Sprintf (
"/oauth2/callback?code=callback_code&state=%s" ,
encodeState ( csrf . HashOAuthState ( ) , "%2F" ) ,
) ,
strings . NewReader ( "" ) ,
)
2015-04-03 03:57:17 +03:00
if err != nil {
return 0 , ""
}
2021-04-21 12:33:27 +03:00
// rw is a dummy here, we just want the csrfCookie to add to our req
csrfCookie , err := csrf . SetCookie ( httptest . NewRecorder ( ) , req )
if err != nil {
panic ( err )
}
req . AddCookie ( csrfCookie )
2018-11-29 17:26:41 +03:00
patTest . proxy . ServeHTTP ( rw , req )
2021-04-21 12:33:27 +03:00
2021-11-12 21:36:29 +03:00
if len ( rw . Header ( ) . Values ( "Set-Cookie" ) ) >= 2 {
cookie = rw . Header ( ) . Values ( "Set-Cookie" ) [ 1 ]
}
return rw . Code , cookie
2015-04-03 03:57:17 +03:00
}
2019-10-04 16:39:31 +03:00
// getEndpointWithCookie makes a requests againt the oauthproxy with passed requestPath
// and cookie and returns body and status code.
func ( patTest * PassAccessTokenTest ) getEndpointWithCookie ( cookie string , endpoint string ) ( httpCode int , accessToken string ) {
2021-04-21 12:33:27 +03:00
cookieName := patTest . proxy . CookieOptions . Name
2015-04-03 03:57:17 +03:00
var value string
2018-11-29 17:26:41 +03:00
keyPrefix := cookieName + "="
2015-04-03 03:57:17 +03:00
for _ , field := range strings . Split ( cookie , "; " ) {
2018-11-29 17:26:41 +03:00
value = strings . TrimPrefix ( field , keyPrefix )
2015-04-03 03:57:17 +03:00
if value != field {
break
} else {
value = ""
}
}
if value == "" {
return 0 , ""
}
2019-10-04 16:39:31 +03:00
req , err := http . NewRequest ( "GET" , endpoint , strings . NewReader ( "" ) )
2015-04-03 03:57:17 +03:00
if err != nil {
return 0 , ""
}
req . AddCookie ( & http . Cookie {
2015-06-08 06:52:28 +03:00
Name : cookieName ,
2015-04-03 03:57:17 +03:00
Value : value ,
Path : "/" ,
Expires : time . Now ( ) . Add ( time . Duration ( 24 ) ) ,
HttpOnly : true ,
} )
rw := httptest . NewRecorder ( )
2018-11-29 17:26:41 +03:00
patTest . proxy . ServeHTTP ( rw , req )
2015-04-03 03:57:17 +03:00
return rw . Code , rw . Body . String ( )
}
func TestForwardAccessTokenUpstream ( t * testing . T ) {
2020-07-13 22:56:05 +03:00
patTest , err := NewPassAccessTokenTest ( PassAccessTokenTestOptions {
2015-04-03 03:57:17 +03:00
PassAccessToken : true ,
2021-11-12 21:36:29 +03:00
ValidToken : true ,
2015-04-03 03:57:17 +03:00
} )
2020-07-13 22:56:05 +03:00
if err != nil {
t . Fatal ( err )
}
t . Cleanup ( patTest . Close )
2015-04-03 03:57:17 +03:00
// A successful validation will redirect and set the auth cookie.
2018-11-29 17:26:41 +03:00
code , cookie := patTest . getCallbackEndpoint ( )
2017-03-28 04:14:38 +03:00
if code != 302 {
t . Fatalf ( "expected 302; got %d" , code )
}
2020-07-13 22:56:05 +03:00
assert . NotNil ( t , cookie )
2015-04-03 03:57:17 +03:00
// Now we make a regular request; the access_token from the cookie is
// forwarded as the "X-Forwarded-Access-Token" header. The token is
// read by the test provider server and written in the response body.
2019-10-04 16:39:31 +03:00
code , payload := patTest . getEndpointWithCookie ( cookie , "/" )
2017-03-28 04:14:38 +03:00
if code != 200 {
t . Fatalf ( "expected 200; got %d" , code )
}
2015-04-03 03:57:17 +03:00
assert . Equal ( t , "my_auth_token" , payload )
}
2019-09-19 12:03:38 +03:00
func TestStaticProxyUpstream ( t * testing . T ) {
2020-07-13 22:56:05 +03:00
patTest , err := NewPassAccessTokenTest ( PassAccessTokenTestOptions {
2019-09-19 12:03:38 +03:00
PassAccessToken : true ,
2021-11-12 21:36:29 +03:00
ValidToken : true ,
2020-05-26 22:06:27 +03:00
ProxyUpstream : options . Upstream {
ID : "static-proxy" ,
Path : "/static-proxy" ,
Static : true ,
} ,
2019-09-19 12:03:38 +03:00
} )
2020-07-13 22:56:05 +03:00
if err != nil {
t . Fatal ( err )
}
t . Cleanup ( patTest . Close )
2019-09-19 12:03:38 +03:00
// A successful validation will redirect and set the auth cookie.
code , cookie := patTest . getCallbackEndpoint ( )
if code != 302 {
t . Fatalf ( "expected 302; got %d" , code )
}
assert . NotEqual ( t , nil , cookie )
2020-04-14 11:36:44 +03:00
// Now we make a regular request against the upstream proxy; And validate
2019-10-04 16:39:31 +03:00
// the returned status code through the static proxy.
code , payload := patTest . getEndpointWithCookie ( cookie , "/static-proxy" )
2019-09-19 12:03:38 +03:00
if code != 200 {
t . Fatalf ( "expected 200; got %d" , code )
}
assert . Equal ( t , "Authenticated" , payload )
}
2015-04-03 03:57:17 +03:00
func TestDoNotForwardAccessTokenUpstream ( t * testing . T ) {
2020-07-13 22:56:05 +03:00
patTest , err := NewPassAccessTokenTest ( PassAccessTokenTestOptions {
2015-04-03 03:57:17 +03:00
PassAccessToken : false ,
2021-11-12 21:36:29 +03:00
ValidToken : true ,
2015-04-03 03:57:17 +03:00
} )
2020-07-13 22:56:05 +03:00
if err != nil {
t . Fatal ( err )
}
t . Cleanup ( patTest . Close )
2015-04-03 03:57:17 +03:00
// A successful validation will redirect and set the auth cookie.
2018-11-29 17:26:41 +03:00
code , cookie := patTest . getCallbackEndpoint ( )
2017-03-28 04:14:38 +03:00
if code != 302 {
t . Fatalf ( "expected 302; got %d" , code )
}
2015-04-03 03:57:17 +03:00
assert . NotEqual ( t , nil , cookie )
// Now we make a regular request, but the access token header should
// not be present.
2019-10-04 16:39:31 +03:00
code , payload := patTest . getEndpointWithCookie ( cookie , "/" )
2017-03-28 04:14:38 +03:00
if code != 200 {
t . Fatalf ( "expected 200; got %d" , code )
}
2015-04-03 03:57:17 +03:00
assert . Equal ( t , "No access token found." , payload )
}
2015-04-07 05:10:03 +03:00
2021-11-12 21:36:29 +03:00
func TestSessionValidationFailure ( t * testing . T ) {
patTest , err := NewPassAccessTokenTest ( PassAccessTokenTestOptions {
ValidToken : false ,
} )
require . NoError ( t , err )
t . Cleanup ( patTest . Close )
// An unsuccessful validation will return 403 and not set the auth cookie.
code , cookie := patTest . getCallbackEndpoint ( )
assert . Equal ( t , http . StatusForbidden , code )
assert . Equal ( t , "" , cookie )
}
2015-04-07 05:10:03 +03:00
type SignInPageTest struct {
2020-04-13 15:50:34 +03:00
opts * options . Options
2018-11-29 17:26:41 +03:00
proxy * OAuthProxy
signInRegexp * regexp . Regexp
signInProviderRegexp * regexp . Regexp
2015-04-07 05:10:03 +03:00
}
const signInRedirectPattern = ` <input type="hidden" name="rd" value="(.*)"> `
2017-06-22 01:02:34 +03:00
const signInSkipProvider = ` >Found< `
2015-04-07 05:10:03 +03:00
2020-07-13 22:56:05 +03:00
func NewSignInPageTest ( skipProvider bool ) ( * SignInPageTest , error ) {
2018-11-29 17:26:41 +03:00
var sipTest SignInPageTest
2015-04-07 05:10:03 +03:00
2020-05-25 16:00:49 +03:00
sipTest . opts = baseTestOptions ( )
2018-11-29 17:26:41 +03:00
sipTest . opts . SkipProviderButton = skipProvider
2020-07-13 22:56:05 +03:00
err := validation . Validate ( sipTest . opts )
if err != nil {
return nil , err
}
2015-04-07 05:10:03 +03:00
2020-05-25 16:00:49 +03:00
sipTest . proxy , err = NewOAuthProxy ( sipTest . opts , func ( email string ) bool {
2015-04-07 05:10:03 +03:00
return true
} )
2020-05-25 16:00:49 +03:00
if err != nil {
2020-07-13 22:56:05 +03:00
return nil , err
2020-05-25 16:00:49 +03:00
}
2018-11-29 17:26:41 +03:00
sipTest . signInRegexp = regexp . MustCompile ( signInRedirectPattern )
sipTest . signInProviderRegexp = regexp . MustCompile ( signInSkipProvider )
2015-04-07 05:10:03 +03:00
2020-07-13 22:56:05 +03:00
return & sipTest , nil
2015-04-07 05:10:03 +03:00
}
2018-11-29 17:26:41 +03:00
func ( sipTest * SignInPageTest ) GetEndpoint ( endpoint string ) ( int , string ) {
2015-04-07 05:10:03 +03:00
rw := httptest . NewRecorder ( )
req , _ := http . NewRequest ( "GET" , endpoint , strings . NewReader ( "" ) )
2018-11-29 17:26:41 +03:00
sipTest . proxy . ServeHTTP ( rw , req )
2015-04-07 05:10:03 +03:00
return rw . Code , rw . Body . String ( )
}
2021-09-26 23:13:48 +03:00
type AlwaysSuccessfulValidator struct {
}
func ( AlwaysSuccessfulValidator ) Validate ( user , password string ) bool {
return true
}
func TestManualSignInStoresUserGroupsInTheSession ( t * testing . T ) {
userGroups := [ ] string { "somegroup" , "someothergroup" }
opts := baseTestOptions ( )
opts . HtpasswdUserGroups = userGroups
err := validation . Validate ( opts )
if err != nil {
t . Fatal ( err )
}
proxy , err := NewOAuthProxy ( opts , func ( email string ) bool {
return true
} )
if err != nil {
t . Fatal ( err )
}
proxy . basicAuthValidator = AlwaysSuccessfulValidator { }
rw := httptest . NewRecorder ( )
formData := url . Values { }
formData . Set ( "username" , "someuser" )
formData . Set ( "password" , "somepass" )
signInReq , _ := http . NewRequest ( http . MethodPost , "/oauth2/sign_in" , strings . NewReader ( formData . Encode ( ) ) )
signInReq . Header . Add ( "Content-Type" , "application/x-www-form-urlencoded" )
proxy . ServeHTTP ( rw , signInReq )
assert . Equal ( t , http . StatusFound , rw . Code )
req , _ := http . NewRequest ( http . MethodGet , "/something" , strings . NewReader ( formData . Encode ( ) ) )
for _ , c := range rw . Result ( ) . Cookies ( ) {
req . AddCookie ( c )
}
s , err := proxy . sessionStore . Load ( req )
if err != nil {
t . Fatal ( err )
}
assert . Equal ( t , userGroups , s . Groups )
}
2022-06-30 18:11:43 +03:00
type ManualSignInValidator struct { }
func ( ManualSignInValidator ) Validate ( user , password string ) bool {
switch {
case user == "admin" && password == "adminPass" :
return true
default :
return false
}
}
func ManualSignInWithCredentials ( t * testing . T , user , pass string ) int {
opts := baseTestOptions ( )
err := validation . Validate ( opts )
if err != nil {
t . Fatal ( err )
}
proxy , err := NewOAuthProxy ( opts , func ( email string ) bool {
return true
} )
if err != nil {
t . Fatal ( err )
}
proxy . basicAuthValidator = ManualSignInValidator { }
rw := httptest . NewRecorder ( )
formData := url . Values { }
formData . Set ( "username" , user )
formData . Set ( "password" , pass )
signInReq , _ := http . NewRequest ( http . MethodPost , "/oauth2/sign_in" , strings . NewReader ( formData . Encode ( ) ) )
signInReq . Header . Add ( "Content-Type" , "application/x-www-form-urlencoded" )
proxy . ServeHTTP ( rw , signInReq )
return rw . Code
}
func TestManualSignInEmptyUsernameAlert ( t * testing . T ) {
statusCode := ManualSignInWithCredentials ( t , "" , "" )
assert . Equal ( t , http . StatusBadRequest , statusCode )
}
func TestManualSignInInvalidCredentialsAlert ( t * testing . T ) {
statusCode := ManualSignInWithCredentials ( t , "admin" , "" )
assert . Equal ( t , http . StatusUnauthorized , statusCode )
}
func TestManualSignInCorrectCredentials ( t * testing . T ) {
statusCode := ManualSignInWithCredentials ( t , "admin" , "adminPass" )
assert . Equal ( t , http . StatusFound , statusCode )
}
2015-04-07 05:10:03 +03:00
func TestSignInPageIncludesTargetRedirect ( t * testing . T ) {
2020-07-13 22:56:05 +03:00
sipTest , err := NewSignInPageTest ( false )
if err != nil {
t . Fatal ( err )
}
2015-04-07 05:10:03 +03:00
const endpoint = "/some/random/endpoint"
2018-11-29 17:26:41 +03:00
code , body := sipTest . GetEndpoint ( endpoint )
2015-04-07 05:10:03 +03:00
assert . Equal ( t , 403 , code )
2018-11-29 17:26:41 +03:00
match := sipTest . signInRegexp . FindStringSubmatch ( body )
2015-04-07 05:10:03 +03:00
if match == nil {
t . Fatal ( "Did not find pattern in body: " +
signInRedirectPattern + "\nBody:\n" + body )
}
if match [ 1 ] != endpoint {
t . Fatal ( ` expected redirect to " ` + endpoint +
` ", but was " ` + match [ 1 ] + ` " ` )
}
}
2022-05-20 16:26:21 +03:00
func TestSignInPageInvalidQueryStringReturnsBadRequest ( t * testing . T ) {
sipTest , err := NewSignInPageTest ( true )
if err != nil {
t . Fatal ( err )
}
const endpoint = "/?q=%va"
code , _ := sipTest . GetEndpoint ( endpoint )
assert . Equal ( t , 400 , code )
}
2015-04-07 05:10:03 +03:00
func TestSignInPageDirectAccessRedirectsToRoot ( t * testing . T ) {
2020-07-13 22:56:05 +03:00
sipTest , err := NewSignInPageTest ( false )
if err != nil {
t . Fatal ( err )
}
2018-11-29 17:26:41 +03:00
code , body := sipTest . GetEndpoint ( "/oauth2/sign_in" )
2015-04-07 05:10:03 +03:00
assert . Equal ( t , 200 , code )
2018-11-29 17:26:41 +03:00
match := sipTest . signInRegexp . FindStringSubmatch ( body )
2015-04-07 05:10:03 +03:00
if match == nil {
t . Fatal ( "Did not find pattern in body: " +
signInRedirectPattern + "\nBody:\n" + body )
}
if match [ 1 ] != "/" {
t . Fatal ( ` expected redirect to "/", but was " ` + match [ 1 ] + ` " ` )
}
}
2015-05-08 18:52:03 +03:00
2017-06-22 01:02:34 +03:00
func TestSignInPageSkipProvider ( t * testing . T ) {
2020-07-13 22:56:05 +03:00
sipTest , err := NewSignInPageTest ( true )
if err != nil {
t . Fatal ( err )
}
endpoint := "/some/random/endpoint"
2017-06-22 01:02:34 +03:00
2018-11-29 17:26:41 +03:00
code , body := sipTest . GetEndpoint ( endpoint )
2017-06-22 01:02:34 +03:00
assert . Equal ( t , 302 , code )
2018-11-29 17:26:41 +03:00
match := sipTest . signInProviderRegexp . FindStringSubmatch ( body )
2017-06-22 01:02:34 +03:00
if match == nil {
t . Fatal ( "Did not find pattern in body: " +
signInSkipProvider + "\nBody:\n" + body )
}
}
func TestSignInPageSkipProviderDirect ( t * testing . T ) {
2020-07-13 22:56:05 +03:00
sipTest , err := NewSignInPageTest ( true )
if err != nil {
t . Fatal ( err )
}
endpoint := "/sign_in"
2017-06-22 01:02:34 +03:00
2018-11-29 17:26:41 +03:00
code , body := sipTest . GetEndpoint ( endpoint )
2017-06-22 01:02:34 +03:00
assert . Equal ( t , 302 , code )
2018-11-29 17:26:41 +03:00
match := sipTest . signInProviderRegexp . FindStringSubmatch ( body )
2017-06-22 01:02:34 +03:00
if match == nil {
t . Fatal ( "Did not find pattern in body: " +
signInSkipProvider + "\nBody:\n" + body )
}
}
2015-05-08 18:52:03 +03:00
type ProcessCookieTest struct {
2020-04-13 15:50:34 +03:00
opts * options . Options
2018-11-29 17:26:41 +03:00
proxy * OAuthProxy
rw * httptest . ResponseRecorder
req * http . Request
validateUser bool
2015-05-08 18:52:03 +03:00
}
2015-05-13 04:48:13 +03:00
type ProcessCookieTestOpts struct {
2018-11-29 17:26:41 +03:00
providerValidateCookieResponse bool
2015-05-13 04:48:13 +03:00
}
2020-04-13 15:50:34 +03:00
type OptionsModifier func ( * options . Options )
2019-05-07 18:13:55 +03:00
2020-07-13 22:56:05 +03:00
func NewProcessCookieTest ( opts ProcessCookieTestOpts , modifiers ... OptionsModifier ) ( * ProcessCookieTest , error ) {
2018-11-29 17:26:41 +03:00
var pcTest ProcessCookieTest
2015-05-08 18:52:03 +03:00
2020-05-25 16:00:49 +03:00
pcTest . opts = baseTestOptions ( )
2019-05-07 18:13:55 +03:00
for _ , modifier := range modifiers {
modifier ( pcTest . opts )
}
2015-05-09 23:08:55 +03:00
// First, set the CookieRefresh option so proxy.AesCipher is created,
// needed to encrypt the access_token.
2020-04-12 16:00:59 +03:00
pcTest . opts . Cookie . Refresh = time . Hour
2020-07-13 22:56:05 +03:00
err := validation . Validate ( pcTest . opts )
if err != nil {
return nil , err
}
2015-05-08 18:52:03 +03:00
2020-05-25 16:00:49 +03:00
pcTest . proxy , err = NewOAuthProxy ( pcTest . opts , func ( email string ) bool {
2018-11-29 17:26:41 +03:00
return pcTest . validateUser
2015-05-08 18:52:03 +03:00
} )
2020-05-25 16:00:49 +03:00
if err != nil {
2020-07-13 22:56:05 +03:00
return nil , err
2020-05-25 16:00:49 +03:00
}
2022-02-15 15:00:06 +03:00
testProvider := & TestProvider {
2020-09-27 05:00:44 +03:00
ProviderData : & providers . ProviderData { } ,
ValidToken : opts . providerValidateCookieResponse ,
2015-05-13 04:48:13 +03:00
}
2022-02-15 15:00:06 +03:00
groups := pcTest . opts . Providers [ 0 ] . AllowedGroups
2022-10-21 13:57:51 +03:00
testProvider . ProviderData . AllowedGroups = make ( map [ string ] struct { } , len ( groups ) )
2022-02-15 15:00:06 +03:00
for _ , group := range groups {
2022-10-21 13:57:51 +03:00
testProvider . ProviderData . AllowedGroups [ group ] = struct { } { }
2022-02-15 15:00:06 +03:00
}
pcTest . proxy . provider = testProvider
2015-05-08 18:52:03 +03:00
2021-04-21 12:33:27 +03:00
// Now, zero-out proxy.CookieRefresh for the cases that don't involve
// access_token validation.
pcTest . proxy . CookieOptions . Refresh = time . Duration ( 0 )
2018-11-29 17:26:41 +03:00
pcTest . rw = httptest . NewRecorder ( )
pcTest . req , _ = http . NewRequest ( "GET" , "/" , strings . NewReader ( "" ) )
pcTest . validateUser = true
2020-07-13 22:56:05 +03:00
return & pcTest , nil
2015-05-08 18:52:03 +03:00
}
2020-07-13 22:56:05 +03:00
func NewProcessCookieTestWithDefaults ( ) ( * ProcessCookieTest , error ) {
2015-05-13 04:48:13 +03:00
return NewProcessCookieTest ( ProcessCookieTestOpts {
2018-11-29 17:26:41 +03:00
providerValidateCookieResponse : true ,
2015-05-13 04:48:13 +03:00
} )
2015-05-09 22:09:31 +03:00
}
2020-07-13 22:56:05 +03:00
func NewProcessCookieTestWithOptionsModifiers ( modifiers ... OptionsModifier ) ( * ProcessCookieTest , error ) {
2019-05-07 18:13:55 +03:00
return NewProcessCookieTest ( ProcessCookieTestOpts {
providerValidateCookieResponse : true ,
} , modifiers ... )
}
func ( p * ProcessCookieTest ) SaveSession ( s * sessions . SessionState ) error {
err := p . proxy . SaveSession ( p . rw , p . req , s )
2015-06-23 14:23:39 +03:00
if err != nil {
return err
}
2019-05-07 18:13:55 +03:00
for _ , cookie := range p . rw . Result ( ) . Cookies ( ) {
p . req . AddCookie ( cookie )
2018-01-28 01:48:52 +03:00
}
2015-06-23 14:23:39 +03:00
return nil
2015-05-08 18:52:03 +03:00
}
2019-05-07 18:13:55 +03:00
func ( p * ProcessCookieTest ) LoadCookiedSession ( ) ( * sessions . SessionState , error ) {
2015-06-23 14:23:39 +03:00
return p . proxy . LoadCookiedSession ( p . req )
2015-05-08 18:52:03 +03:00
}
2015-06-23 14:23:39 +03:00
func TestLoadCookiedSession ( t * testing . T ) {
2020-07-13 22:56:05 +03:00
pcTest , err := NewProcessCookieTestWithDefaults ( )
if err != nil {
t . Fatal ( err )
}
2015-05-09 22:09:31 +03:00
2020-05-30 10:53:38 +03:00
created := time . Now ( )
startSession := & sessions . SessionState { Email : "john.doe@example.com" , AccessToken : "my_access_token" , CreatedAt : & created }
2020-07-13 22:56:05 +03:00
err = pcTest . SaveSession ( startSession )
assert . NoError ( t , err )
2015-06-23 14:23:39 +03:00
2019-05-07 18:13:55 +03:00
session , err := pcTest . LoadCookiedSession ( )
2020-07-13 22:56:05 +03:00
if err != nil {
t . Fatal ( err )
}
2015-06-23 14:23:39 +03:00
assert . Equal ( t , startSession . Email , session . Email )
2020-05-12 18:04:51 +03:00
assert . Equal ( t , "" , session . User )
2015-06-23 14:23:39 +03:00
assert . Equal ( t , startSession . AccessToken , session . AccessToken )
2015-05-08 18:52:03 +03:00
}
2015-05-08 17:00:57 +03:00
func TestProcessCookieNoCookieError ( t * testing . T ) {
2020-07-13 22:56:05 +03:00
pcTest , err := NewProcessCookieTestWithDefaults ( )
if err != nil {
t . Fatal ( err )
}
2015-05-08 17:00:57 +03:00
2019-05-07 18:13:55 +03:00
session , err := pcTest . LoadCookiedSession ( )
2020-07-13 22:56:05 +03:00
assert . Error ( t , err , "cookie \"_oauth2_proxy\" not present" )
2015-06-23 14:23:39 +03:00
if session != nil {
t . Errorf ( "expected nil session. got %#v" , session )
}
2015-05-09 23:31:18 +03:00
}
2015-05-08 17:00:57 +03:00
func TestProcessCookieRefreshNotSet ( t * testing . T ) {
2020-07-13 22:56:05 +03:00
pcTest , err := NewProcessCookieTestWithOptionsModifiers ( func ( opts * options . Options ) {
2020-04-12 16:00:59 +03:00
opts . Cookie . Expire = time . Duration ( 23 ) * time . Hour
2019-05-07 18:13:55 +03:00
} )
2020-07-13 22:56:05 +03:00
if err != nil {
t . Fatal ( err )
}
2015-06-22 22:10:08 +03:00
reference := time . Now ( ) . Add ( time . Duration ( - 2 ) * time . Hour )
2015-05-08 17:00:57 +03:00
2020-05-30 10:53:38 +03:00
startSession := & sessions . SessionState { Email : "michael.bland@gsa.gov" , AccessToken : "my_access_token" , CreatedAt : & reference }
2020-07-13 22:56:05 +03:00
err = pcTest . SaveSession ( startSession )
assert . NoError ( t , err )
2015-05-09 22:09:31 +03:00
2019-05-07 18:13:55 +03:00
session , err := pcTest . LoadCookiedSession ( )
2015-06-23 14:23:39 +03:00
assert . Equal ( t , nil , err )
2019-05-07 18:13:55 +03:00
if session . Age ( ) < time . Duration ( - 2 ) * time . Hour {
t . Errorf ( "cookie too young %v" , session . Age ( ) )
2015-06-23 14:23:39 +03:00
}
assert . Equal ( t , startSession . Email , session . Email )
2015-05-10 07:11:26 +03:00
}
2015-06-22 22:10:08 +03:00
func TestProcessCookieFailIfCookieExpired ( t * testing . T ) {
2020-07-13 22:56:05 +03:00
pcTest , err := NewProcessCookieTestWithOptionsModifiers ( func ( opts * options . Options ) {
2020-04-12 16:00:59 +03:00
opts . Cookie . Expire = time . Duration ( 24 ) * time . Hour
2019-05-07 18:13:55 +03:00
} )
2020-07-13 22:56:05 +03:00
if err != nil {
t . Fatal ( err )
}
2015-06-22 22:10:08 +03:00
reference := time . Now ( ) . Add ( time . Duration ( 25 ) * time . Hour * - 1 )
2020-05-30 10:53:38 +03:00
startSession := & sessions . SessionState { Email : "michael.bland@gsa.gov" , AccessToken : "my_access_token" , CreatedAt : & reference }
2020-07-13 22:56:05 +03:00
err = pcTest . SaveSession ( startSession )
assert . NoError ( t , err )
2015-06-22 22:10:08 +03:00
2019-05-07 18:13:55 +03:00
session , err := pcTest . LoadCookiedSession ( )
2015-06-23 14:23:39 +03:00
assert . NotEqual ( t , nil , err )
if session != nil {
t . Errorf ( "expected nil session %#v" , session )
2015-06-22 22:10:08 +03:00
}
}
func TestProcessCookieFailIfRefreshSetAndCookieExpired ( t * testing . T ) {
2020-07-13 22:56:05 +03:00
pcTest , err := NewProcessCookieTestWithOptionsModifiers ( func ( opts * options . Options ) {
2020-04-12 16:00:59 +03:00
opts . Cookie . Expire = time . Duration ( 24 ) * time . Hour
2019-05-07 18:13:55 +03:00
} )
2020-07-13 22:56:05 +03:00
if err != nil {
t . Fatal ( err )
}
2015-06-22 22:10:08 +03:00
reference := time . Now ( ) . Add ( time . Duration ( 25 ) * time . Hour * - 1 )
2020-05-30 10:53:38 +03:00
startSession := & sessions . SessionState { Email : "michael.bland@gsa.gov" , AccessToken : "my_access_token" , CreatedAt : & reference }
2020-07-13 22:56:05 +03:00
err = pcTest . SaveSession ( startSession )
assert . NoError ( t , err )
2015-06-22 22:10:08 +03:00
2021-04-21 12:33:27 +03:00
pcTest . proxy . CookieOptions . Refresh = time . Hour
2019-05-07 18:13:55 +03:00
session , err := pcTest . LoadCookiedSession ( )
2015-06-23 14:23:39 +03:00
assert . NotEqual ( t , nil , err )
if session != nil {
t . Errorf ( "expected nil session %#v" , session )
2015-06-22 22:10:08 +03:00
}
}
2015-10-08 16:27:00 +03:00
2020-07-13 22:56:05 +03:00
func NewUserInfoEndpointTest ( ) ( * ProcessCookieTest , error ) {
pcTest , err := NewProcessCookieTestWithDefaults ( )
if err != nil {
return nil , err
}
2019-11-08 01:38:36 +03:00
pcTest . req , _ = http . NewRequest ( "GET" ,
pcTest . opts . ProxyPrefix + "/userinfo" , nil )
2020-07-13 22:56:05 +03:00
return pcTest , nil
2019-11-08 01:38:36 +03:00
}
func TestUserInfoEndpointAccepted ( t * testing . T ) {
2020-11-26 05:55:16 +03:00
testCases := [ ] struct {
name string
session * sessions . SessionState
expectedResponse string
} {
{
name : "Full session" ,
session : & sessions . SessionState {
User : "john.doe" ,
Email : "john.doe@example.com" ,
Groups : [ ] string { "example" , "groups" } ,
AccessToken : "my_access_token" ,
} ,
expectedResponse : "{\"user\":\"john.doe\",\"email\":\"john.doe@example.com\",\"groups\":[\"example\",\"groups\"]}\n" ,
} ,
{
name : "Minimal session" ,
session : & sessions . SessionState {
User : "john.doe" ,
Email : "john.doe@example.com" ,
Groups : [ ] string { "example" , "groups" } ,
} ,
expectedResponse : "{\"user\":\"john.doe\",\"email\":\"john.doe@example.com\",\"groups\":[\"example\",\"groups\"]}\n" ,
} ,
{
name : "No groups" ,
session : & sessions . SessionState {
User : "john.doe" ,
Email : "john.doe@example.com" ,
AccessToken : "my_access_token" ,
} ,
expectedResponse : "{\"user\":\"john.doe\",\"email\":\"john.doe@example.com\"}\n" ,
} ,
{
name : "With Preferred Username" ,
session : & sessions . SessionState {
User : "john.doe" ,
PreferredUsername : "john" ,
Email : "john.doe@example.com" ,
Groups : [ ] string { "example" , "groups" } ,
AccessToken : "my_access_token" ,
} ,
expectedResponse : "{\"user\":\"john.doe\",\"email\":\"john.doe@example.com\",\"groups\":[\"example\",\"groups\"],\"preferredUsername\":\"john\"}\n" ,
} ,
2020-07-13 22:56:05 +03:00
}
2020-11-26 05:55:16 +03:00
for _ , tc := range testCases {
t . Run ( tc . name , func ( t * testing . T ) {
test , err := NewUserInfoEndpointTest ( )
if err != nil {
t . Fatal ( err )
}
err = test . SaveSession ( tc . session )
assert . NoError ( t , err )
2020-10-24 05:35:15 +03:00
2020-11-26 05:55:16 +03:00
test . proxy . ServeHTTP ( test . rw , test . req )
assert . Equal ( t , http . StatusOK , test . rw . Code )
2022-10-21 13:57:51 +03:00
bodyBytes , _ := io . ReadAll ( test . rw . Body )
2020-11-26 05:55:16 +03:00
assert . Equal ( t , tc . expectedResponse , string ( bodyBytes ) )
} )
}
2019-11-08 01:38:36 +03:00
}
func TestUserInfoEndpointUnauthorizedOnNoCookieSetError ( t * testing . T ) {
2020-07-13 22:56:05 +03:00
test , err := NewUserInfoEndpointTest ( )
if err != nil {
t . Fatal ( err )
}
2019-11-08 01:38:36 +03:00
test . proxy . ServeHTTP ( test . rw , test . req )
assert . Equal ( t , http . StatusUnauthorized , test . rw . Code )
}
2021-08-09 15:46:26 +03:00
func TestEncodedUrlsStayEncoded ( t * testing . T ) {
encodeTest , err := NewSignInPageTest ( false )
if err != nil {
t . Fatal ( err )
}
code , _ := encodeTest . GetEndpoint ( "/%2F/test1/%2F/test2" )
assert . Equal ( t , 403 , code )
}
2020-10-19 04:14:32 +03:00
func NewAuthOnlyEndpointTest ( querystring string , modifiers ... OptionsModifier ) ( * ProcessCookieTest , error ) {
2020-07-13 22:56:05 +03:00
pcTest , err := NewProcessCookieTestWithOptionsModifiers ( modifiers ... )
if err != nil {
return nil , err
}
2020-10-19 04:14:32 +03:00
pcTest . req , _ = http . NewRequest (
"GET" ,
fmt . Sprintf ( "%s/auth%s" , pcTest . opts . ProxyPrefix , querystring ) ,
nil )
2020-07-13 22:56:05 +03:00
return pcTest , nil
2015-10-08 16:27:00 +03:00
}
func TestAuthOnlyEndpointAccepted ( t * testing . T ) {
2020-10-19 04:14:32 +03:00
test , err := NewAuthOnlyEndpointTest ( "" )
2020-07-13 22:56:05 +03:00
if err != nil {
t . Fatal ( err )
}
2020-05-30 10:53:38 +03:00
created := time . Now ( )
2019-05-05 15:33:13 +03:00
startSession := & sessions . SessionState {
2020-05-30 10:53:38 +03:00
Email : "michael.bland@gsa.gov" , AccessToken : "my_access_token" , CreatedAt : & created }
2020-07-13 22:56:05 +03:00
err = test . SaveSession ( startSession )
assert . NoError ( t , err )
2015-10-08 16:27:00 +03:00
test . proxy . ServeHTTP ( test . rw , test . req )
assert . Equal ( t , http . StatusAccepted , test . rw . Code )
2022-10-21 13:57:51 +03:00
bodyBytes , _ := io . ReadAll ( test . rw . Body )
2015-10-08 16:27:00 +03:00
assert . Equal ( t , "" , string ( bodyBytes ) )
}
func TestAuthOnlyEndpointUnauthorizedOnNoCookieSetError ( t * testing . T ) {
2020-10-19 04:14:32 +03:00
test , err := NewAuthOnlyEndpointTest ( "" )
2020-07-13 22:56:05 +03:00
if err != nil {
t . Fatal ( err )
}
2015-10-08 16:27:00 +03:00
test . proxy . ServeHTTP ( test . rw , test . req )
assert . Equal ( t , http . StatusUnauthorized , test . rw . Code )
2022-10-21 13:57:51 +03:00
bodyBytes , _ := io . ReadAll ( test . rw . Body )
2020-11-18 06:03:41 +03:00
assert . Equal ( t , "Unauthorized\n" , string ( bodyBytes ) )
2015-10-08 16:27:00 +03:00
}
func TestAuthOnlyEndpointUnauthorizedOnExpiration ( t * testing . T ) {
2020-10-19 04:14:32 +03:00
test , err := NewAuthOnlyEndpointTest ( "" , func ( opts * options . Options ) {
2020-04-12 16:00:59 +03:00
opts . Cookie . Expire = time . Duration ( 24 ) * time . Hour
2019-05-07 18:13:55 +03:00
} )
2020-07-13 22:56:05 +03:00
if err != nil {
t . Fatal ( err )
}
2015-10-08 16:27:00 +03:00
reference := time . Now ( ) . Add ( time . Duration ( 25 ) * time . Hour * - 1 )
2019-05-05 15:33:13 +03:00
startSession := & sessions . SessionState {
2020-05-30 10:53:38 +03:00
Email : "michael.bland@gsa.gov" , AccessToken : "my_access_token" , CreatedAt : & reference }
2020-07-13 22:56:05 +03:00
err = test . SaveSession ( startSession )
assert . NoError ( t , err )
2015-10-08 16:27:00 +03:00
test . proxy . ServeHTTP ( test . rw , test . req )
assert . Equal ( t , http . StatusUnauthorized , test . rw . Code )
2022-10-21 13:57:51 +03:00
bodyBytes , _ := io . ReadAll ( test . rw . Body )
2020-11-18 06:03:41 +03:00
assert . Equal ( t , "Unauthorized\n" , string ( bodyBytes ) )
2015-10-08 16:27:00 +03:00
}
func TestAuthOnlyEndpointUnauthorizedOnEmailValidationFailure ( t * testing . T ) {
2020-10-19 04:14:32 +03:00
test , err := NewAuthOnlyEndpointTest ( "" )
2020-07-13 22:56:05 +03:00
if err != nil {
t . Fatal ( err )
}
2020-05-30 10:53:38 +03:00
created := time . Now ( )
2019-05-05 15:33:13 +03:00
startSession := & sessions . SessionState {
2020-05-30 10:53:38 +03:00
Email : "michael.bland@gsa.gov" , AccessToken : "my_access_token" , CreatedAt : & created }
2020-07-13 22:56:05 +03:00
err = test . SaveSession ( startSession )
assert . NoError ( t , err )
2018-11-29 17:26:41 +03:00
test . validateUser = false
2015-10-08 16:27:00 +03:00
test . proxy . ServeHTTP ( test . rw , test . req )
assert . Equal ( t , http . StatusUnauthorized , test . rw . Code )
2022-10-21 13:57:51 +03:00
bodyBytes , _ := io . ReadAll ( test . rw . Body )
2020-11-18 06:03:41 +03:00
assert . Equal ( t , "Unauthorized\n" , string ( bodyBytes ) )
2015-10-08 16:27:00 +03:00
}
2015-11-16 06:08:30 +03:00
2016-10-20 15:19:59 +03:00
func TestAuthOnlyEndpointSetXAuthRequestHeaders ( t * testing . T ) {
2018-11-29 17:26:41 +03:00
var pcTest ProcessCookieTest
2016-10-20 15:19:59 +03:00
2020-05-25 16:00:49 +03:00
pcTest . opts = baseTestOptions ( )
2020-07-29 22:10:14 +03:00
pcTest . opts . InjectResponseHeaders = [ ] options . Header {
{
Name : "X-Auth-Request-User" ,
Values : [ ] options . HeaderValue {
{
ClaimSource : & options . ClaimSource {
Claim : "user" ,
} ,
} ,
} ,
} ,
{
Name : "X-Auth-Request-Email" ,
Values : [ ] options . HeaderValue {
{
ClaimSource : & options . ClaimSource {
Claim : "email" ,
} ,
} ,
} ,
} ,
{
Name : "X-Auth-Request-Groups" ,
Values : [ ] options . HeaderValue {
{
ClaimSource : & options . ClaimSource {
Claim : "groups" ,
} ,
} ,
} ,
} ,
{
Name : "X-Forwarded-Preferred-Username" ,
Values : [ ] options . HeaderValue {
{
ClaimSource : & options . ClaimSource {
Claim : "preferred_username" ,
} ,
} ,
} ,
} ,
}
2021-04-03 19:06:30 +03:00
pcTest . opts . Providers [ 0 ] . AllowedGroups = [ ] string { "oauth_groups" }
2020-05-25 16:00:49 +03:00
err := validation . Validate ( pcTest . opts )
2020-07-13 22:56:05 +03:00
assert . NoError ( t , err )
2016-10-20 15:19:59 +03:00
2020-05-25 16:00:49 +03:00
pcTest . proxy , err = NewOAuthProxy ( pcTest . opts , func ( email string ) bool {
2018-11-29 17:26:41 +03:00
return pcTest . validateUser
2016-10-20 15:19:59 +03:00
} )
2020-05-25 16:00:49 +03:00
if err != nil {
2020-07-13 22:56:05 +03:00
t . Fatal ( err )
2020-05-25 16:00:49 +03:00
}
2018-11-29 17:26:41 +03:00
pcTest . proxy . provider = & TestProvider {
2020-09-27 05:00:44 +03:00
ProviderData : & providers . ProviderData { } ,
ValidToken : true ,
2016-10-20 15:19:59 +03:00
}
2018-11-29 17:26:41 +03:00
pcTest . validateUser = true
2016-10-20 15:19:59 +03:00
2018-11-29 17:26:41 +03:00
pcTest . rw = httptest . NewRecorder ( )
pcTest . req , _ = http . NewRequest ( "GET" ,
pcTest . opts . ProxyPrefix + "/auth" , nil )
2016-10-20 15:19:59 +03:00
2020-05-30 10:53:38 +03:00
created := time . Now ( )
2019-05-05 15:33:13 +03:00
startSession := & sessions . SessionState {
2020-07-28 21:42:09 +03:00
User : "oauth_user" , Groups : [ ] string { "oauth_groups" } , Email : "oauth_user@example.com" , AccessToken : "oauth_token" , CreatedAt : & created }
2020-07-13 22:56:05 +03:00
err = pcTest . SaveSession ( startSession )
assert . NoError ( t , err )
2016-10-20 15:19:59 +03:00
2018-11-29 17:26:41 +03:00
pcTest . proxy . ServeHTTP ( pcTest . rw , pcTest . req )
assert . Equal ( t , http . StatusAccepted , pcTest . rw . Code )
2020-04-14 11:36:44 +03:00
assert . Equal ( t , "oauth_user" , pcTest . rw . Header ( ) . Get ( "X-Auth-Request-User" ) )
2020-07-28 21:42:09 +03:00
assert . Equal ( t , startSession . Groups , pcTest . rw . Header ( ) . Values ( "X-Auth-Request-Groups" ) )
2020-04-14 11:36:44 +03:00
assert . Equal ( t , "oauth_user@example.com" , pcTest . rw . Header ( ) . Get ( "X-Auth-Request-Email" ) )
2016-10-20 15:19:59 +03:00
}
2020-04-10 16:41:28 +03:00
func TestAuthOnlyEndpointSetBasicAuthTrueRequestHeaders ( t * testing . T ) {
var pcTest ProcessCookieTest
2020-05-25 16:00:49 +03:00
pcTest . opts = baseTestOptions ( )
2020-07-29 22:10:14 +03:00
pcTest . opts . InjectResponseHeaders = [ ] options . Header {
{
Name : "X-Auth-Request-User" ,
Values : [ ] options . HeaderValue {
{
ClaimSource : & options . ClaimSource {
Claim : "user" ,
} ,
} ,
} ,
} ,
{
Name : "X-Auth-Request-Email" ,
Values : [ ] options . HeaderValue {
{
ClaimSource : & options . ClaimSource {
Claim : "email" ,
} ,
} ,
} ,
} ,
{
Name : "X-Auth-Request-Groups" ,
Values : [ ] options . HeaderValue {
{
ClaimSource : & options . ClaimSource {
Claim : "groups" ,
} ,
} ,
} ,
} ,
{
Name : "X-Forwarded-Preferred-Username" ,
Values : [ ] options . HeaderValue {
{
ClaimSource : & options . ClaimSource {
Claim : "preferred_username" ,
} ,
} ,
} ,
} ,
{
Name : "Authorization" ,
Values : [ ] options . HeaderValue {
{
ClaimSource : & options . ClaimSource {
Claim : "user" ,
BasicAuthPassword : & options . SecretSource {
2020-11-19 22:58:50 +03:00
Value : [ ] byte ( "This is a secure password" ) ,
2020-07-29 22:10:14 +03:00
} ,
} ,
} ,
} ,
} ,
}
2020-07-13 22:56:05 +03:00
err := validation . Validate ( pcTest . opts )
assert . NoError ( t , err )
2020-04-10 16:41:28 +03:00
2020-05-25 16:00:49 +03:00
pcTest . proxy , err = NewOAuthProxy ( pcTest . opts , func ( email string ) bool {
2020-04-10 16:41:28 +03:00
return pcTest . validateUser
} )
2020-05-25 16:00:49 +03:00
if err != nil {
2020-07-13 22:56:05 +03:00
t . Fatal ( err )
2020-05-25 16:00:49 +03:00
}
2020-04-10 16:41:28 +03:00
pcTest . proxy . provider = & TestProvider {
2020-09-27 05:00:44 +03:00
ProviderData : & providers . ProviderData { } ,
ValidToken : true ,
2020-04-10 16:41:28 +03:00
}
pcTest . validateUser = true
pcTest . rw = httptest . NewRecorder ( )
pcTest . req , _ = http . NewRequest ( "GET" ,
pcTest . opts . ProxyPrefix + "/auth" , nil )
2020-05-30 10:53:38 +03:00
created := time . Now ( )
2020-04-10 16:41:28 +03:00
startSession := & sessions . SessionState {
2020-05-30 10:53:38 +03:00
User : "oauth_user" , Email : "oauth_user@example.com" , AccessToken : "oauth_token" , CreatedAt : & created }
2020-07-13 22:56:05 +03:00
err = pcTest . SaveSession ( startSession )
assert . NoError ( t , err )
2020-04-10 16:41:28 +03:00
pcTest . proxy . ServeHTTP ( pcTest . rw , pcTest . req )
assert . Equal ( t , http . StatusAccepted , pcTest . rw . Code )
2020-04-14 11:36:44 +03:00
assert . Equal ( t , "oauth_user" , pcTest . rw . Header ( ) . Values ( "X-Auth-Request-User" ) [ 0 ] )
assert . Equal ( t , "oauth_user@example.com" , pcTest . rw . Header ( ) . Values ( "X-Auth-Request-Email" ) [ 0 ] )
2020-07-29 22:10:14 +03:00
expectedHeader := "Basic " + base64 . StdEncoding . EncodeToString ( [ ] byte ( "oauth_user:This is a secure password" ) )
2020-04-14 11:36:44 +03:00
assert . Equal ( t , expectedHeader , pcTest . rw . Header ( ) . Values ( "Authorization" ) [ 0 ] )
2020-04-10 16:41:28 +03:00
}
func TestAuthOnlyEndpointSetBasicAuthFalseRequestHeaders ( t * testing . T ) {
var pcTest ProcessCookieTest
2020-05-25 16:00:49 +03:00
pcTest . opts = baseTestOptions ( )
2020-07-29 22:10:14 +03:00
pcTest . opts . InjectResponseHeaders = [ ] options . Header {
{
Name : "X-Auth-Request-User" ,
Values : [ ] options . HeaderValue {
{
ClaimSource : & options . ClaimSource {
Claim : "user" ,
} ,
} ,
} ,
} ,
{
Name : "X-Auth-Request-Email" ,
Values : [ ] options . HeaderValue {
{
ClaimSource : & options . ClaimSource {
Claim : "email" ,
} ,
} ,
} ,
} ,
{
Name : "X-Auth-Request-Groups" ,
Values : [ ] options . HeaderValue {
{
ClaimSource : & options . ClaimSource {
Claim : "groups" ,
} ,
} ,
} ,
} ,
{
Name : "X-Forwarded-Preferred-Username" ,
Values : [ ] options . HeaderValue {
{
ClaimSource : & options . ClaimSource {
Claim : "preferred_username" ,
} ,
} ,
} ,
} ,
}
2020-07-13 22:56:05 +03:00
err := validation . Validate ( pcTest . opts )
assert . NoError ( t , err )
2020-04-10 16:41:28 +03:00
2020-05-25 16:00:49 +03:00
pcTest . proxy , err = NewOAuthProxy ( pcTest . opts , func ( email string ) bool {
2020-04-10 16:41:28 +03:00
return pcTest . validateUser
} )
2020-05-25 16:00:49 +03:00
if err != nil {
2020-07-13 22:56:05 +03:00
t . Fatal ( err )
2020-05-25 16:00:49 +03:00
}
2020-04-10 16:41:28 +03:00
pcTest . proxy . provider = & TestProvider {
2020-09-27 05:00:44 +03:00
ProviderData : & providers . ProviderData { } ,
ValidToken : true ,
2020-04-10 16:41:28 +03:00
}
pcTest . validateUser = true
pcTest . rw = httptest . NewRecorder ( )
pcTest . req , _ = http . NewRequest ( "GET" ,
pcTest . opts . ProxyPrefix + "/auth" , nil )
2020-05-30 10:53:38 +03:00
created := time . Now ( )
2020-04-10 16:41:28 +03:00
startSession := & sessions . SessionState {
2020-05-30 10:53:38 +03:00
User : "oauth_user" , Email : "oauth_user@example.com" , AccessToken : "oauth_token" , CreatedAt : & created }
2020-07-13 22:56:05 +03:00
err = pcTest . SaveSession ( startSession )
assert . NoError ( t , err )
2020-04-10 16:41:28 +03:00
pcTest . proxy . ServeHTTP ( pcTest . rw , pcTest . req )
assert . Equal ( t , http . StatusAccepted , pcTest . rw . Code )
2020-04-14 11:36:44 +03:00
assert . Equal ( t , "oauth_user" , pcTest . rw . Header ( ) . Values ( "X-Auth-Request-User" ) [ 0 ] )
assert . Equal ( t , "oauth_user@example.com" , pcTest . rw . Header ( ) . Values ( "X-Auth-Request-Email" ) [ 0 ] )
assert . Equal ( t , 0 , len ( pcTest . rw . Header ( ) . Values ( "Authorization" ) ) , "should not have Authorization header entries" )
2020-04-10 16:41:28 +03:00
}
2017-04-07 14:55:48 +03:00
func TestAuthSkippedForPreflightRequests ( t * testing . T ) {
2020-09-24 06:16:05 +03:00
upstreamServer := httptest . NewServer ( http . HandlerFunc ( func ( w http . ResponseWriter , r * http . Request ) {
2017-04-07 14:55:48 +03:00
w . WriteHeader ( 200 )
2020-07-13 22:56:05 +03:00
_ , err := w . Write ( [ ] byte ( "response" ) )
if err != nil {
t . Fatal ( err )
}
2017-04-07 14:55:48 +03:00
} ) )
2020-09-24 06:16:05 +03:00
t . Cleanup ( upstreamServer . Close )
2017-04-07 14:55:48 +03:00
2020-05-25 16:00:49 +03:00
opts := baseTestOptions ( )
2021-09-17 14:08:18 +03:00
opts . UpstreamServers = options . UpstreamConfig {
Upstreams : [ ] options . Upstream {
2021-08-09 16:32:15 +03:00
{
ID : upstreamServer . URL ,
Path : "/" ,
URI : upstreamServer . URL ,
} ,
2020-05-26 22:06:27 +03:00
} ,
}
2017-04-07 14:55:48 +03:00
opts . SkipAuthPreflight = true
2020-07-13 22:56:05 +03:00
err := validation . Validate ( opts )
assert . NoError ( t , err )
2017-04-07 14:55:48 +03:00
2020-09-24 06:16:05 +03:00
upstreamURL , _ := url . Parse ( upstreamServer . URL )
2017-04-07 14:55:48 +03:00
2020-05-25 16:00:49 +03:00
proxy , err := NewOAuthProxy ( opts , func ( string ) bool { return false } )
2020-07-13 22:56:05 +03:00
if err != nil {
t . Fatal ( err )
}
2022-02-15 15:00:06 +03:00
proxy . provider = NewTestProvider ( upstreamURL , "" )
2017-04-07 14:55:48 +03:00
rw := httptest . NewRecorder ( )
req , _ := http . NewRequest ( "OPTIONS" , "/preflight-request" , nil )
proxy . ServeHTTP ( rw , req )
assert . Equal ( t , 200 , rw . Code )
assert . Equal ( t , "response" , rw . Body . String ( ) )
}
2015-11-16 06:08:30 +03:00
type SignatureAuthenticator struct {
auth hmacauth . HmacAuth
}
2017-03-23 06:18:34 +03:00
func ( v * SignatureAuthenticator ) Authenticate ( w http . ResponseWriter , r * http . Request ) {
2015-11-16 06:08:30 +03:00
result , headerSig , computedSig := v . auth . AuthenticateRequest ( r )
2020-07-13 22:56:05 +03:00
var msg string
switch result {
case hmacauth . ResultNoSignature :
msg = "no signature received"
case hmacauth . ResultMatch :
msg = "signatures match"
case hmacauth . ResultMismatch :
msg = fmt . Sprintf (
"signatures do not match:\n received: %s\n computed: %s" ,
headerSig ,
computedSig )
default :
panic ( "unknown result value: " + result . String ( ) )
}
_ , err := w . Write ( [ ] byte ( msg ) )
if err != nil {
panic ( err )
2015-11-16 06:08:30 +03:00
}
}
type SignatureTest struct {
2020-04-13 15:50:34 +03:00
opts * options . Options
2015-11-16 06:08:30 +03:00
upstream * httptest . Server
2018-11-29 17:26:41 +03:00
upstreamHost string
2015-11-16 06:08:30 +03:00
provider * httptest . Server
header http . Header
rw * httptest . ResponseRecorder
authenticator * SignatureAuthenticator
2022-02-15 15:00:06 +03:00
authProvider providers . Provider
2015-11-16 06:08:30 +03:00
}
2020-07-13 22:56:05 +03:00
func NewSignatureTest ( ) ( * SignatureTest , error ) {
2020-05-25 16:00:49 +03:00
opts := baseTestOptions ( )
2015-11-16 06:08:30 +03:00
opts . EmailDomains = [ ] string { "acm.org" }
authenticator := & SignatureAuthenticator { }
2020-09-24 06:16:05 +03:00
upstreamServer := httptest . NewServer (
2015-11-16 06:08:30 +03:00
http . HandlerFunc ( authenticator . Authenticate ) )
2020-09-24 06:16:05 +03:00
upstreamURL , err := url . Parse ( upstreamServer . URL )
2020-07-13 22:56:05 +03:00
if err != nil {
return nil , err
}
2021-09-17 14:08:18 +03:00
opts . UpstreamServers = options . UpstreamConfig {
Upstreams : [ ] options . Upstream {
2021-08-09 16:32:15 +03:00
{
ID : upstreamServer . URL ,
Path : "/" ,
URI : upstreamServer . URL ,
} ,
2020-05-26 22:06:27 +03:00
} ,
}
2015-11-16 06:08:30 +03:00
providerHandler := func ( w http . ResponseWriter , r * http . Request ) {
2020-07-13 22:56:05 +03:00
_ , err := w . Write ( [ ] byte ( ` { "access_token": "my_auth_token"} ` ) )
if err != nil {
panic ( err )
}
2015-11-16 06:08:30 +03:00
}
provider := httptest . NewServer ( http . HandlerFunc ( providerHandler ) )
2020-07-13 22:56:05 +03:00
providerURL , err := url . Parse ( provider . URL )
if err != nil {
return nil , err
}
2022-02-15 15:00:06 +03:00
testProvider := NewTestProvider ( providerURL , "mbland@acm.org" )
2015-11-16 06:08:30 +03:00
return & SignatureTest {
opts ,
2020-09-24 06:16:05 +03:00
upstreamServer ,
2018-11-29 17:26:41 +03:00
upstreamURL . Host ,
2015-11-16 06:08:30 +03:00
provider ,
make ( http . Header ) ,
httptest . NewRecorder ( ) ,
authenticator ,
2022-02-15 15:00:06 +03:00
testProvider ,
2020-07-13 22:56:05 +03:00
} , nil
2015-11-16 06:08:30 +03:00
}
func ( st * SignatureTest ) Close ( ) {
st . provider . Close ( )
st . upstream . Close ( )
}
// fakeNetConn simulates an http.Request.Body buffer that will be consumed
// when it is read by the hmacauth.HmacAuth if not handled properly. See:
2022-09-11 18:09:32 +03:00
//
// https://github.com/18F/hmacauth/pull/4
2015-11-16 06:08:30 +03:00
type fakeNetConn struct {
reqBody string
}
func ( fnc * fakeNetConn ) Read ( p [ ] byte ) ( n int , err error ) {
if bodyLen := len ( fnc . reqBody ) ; bodyLen != 0 {
copy ( p , fnc . reqBody )
fnc . reqBody = ""
return bodyLen , io . EOF
}
return 0 , io . EOF
}
2020-07-13 22:56:05 +03:00
func ( st * SignatureTest ) MakeRequestWithExpectedKey ( method , body , key string ) error {
2020-04-13 15:50:34 +03:00
err := validation . Validate ( st . opts )
2015-11-16 06:08:30 +03:00
if err != nil {
2020-07-13 22:56:05 +03:00
return err
2015-11-16 06:08:30 +03:00
}
2020-05-25 16:00:49 +03:00
proxy , err := NewOAuthProxy ( st . opts , func ( email string ) bool { return true } )
if err != nil {
2020-07-13 22:56:05 +03:00
return err
2020-05-25 16:00:49 +03:00
}
2022-02-15 15:00:06 +03:00
proxy . provider = st . authProvider
2015-11-16 06:08:30 +03:00
var bodyBuf io . ReadCloser
if body != "" {
2022-10-21 13:57:51 +03:00
bodyBuf = io . NopCloser ( & fakeNetConn { reqBody : body } )
2015-11-16 06:08:30 +03:00
}
2017-03-23 06:18:34 +03:00
req := httptest . NewRequest ( method , "/foo/bar" , bodyBuf )
2015-11-16 06:08:30 +03:00
req . Header = st . header
2019-05-05 15:33:13 +03:00
state := & sessions . SessionState {
2015-11-16 06:08:30 +03:00
Email : "mbland@acm.org" , AccessToken : "my_access_token" }
2019-05-07 19:01:52 +03:00
err = proxy . SaveSession ( st . rw , req , state )
2015-11-16 06:08:30 +03:00
if err != nil {
2020-07-13 22:56:05 +03:00
return err
2015-11-16 06:08:30 +03:00
}
2019-05-07 19:01:52 +03:00
for _ , c := range st . rw . Result ( ) . Cookies ( ) {
2018-01-28 01:48:52 +03:00
req . AddCookie ( c )
}
2015-11-16 06:08:30 +03:00
// This is used by the upstream to validate the signature.
st . authenticator . auth = hmacauth . NewHmacAuth (
2020-05-26 22:06:27 +03:00
crypto . SHA1 , [ ] byte ( key ) , upstream . SignatureHeader , upstream . SignatureHeaders )
2015-11-16 06:08:30 +03:00
proxy . ServeHTTP ( st . rw , req )
2020-07-13 22:56:05 +03:00
return nil
2015-11-16 06:08:30 +03:00
}
2020-07-13 22:56:05 +03:00
func TestRequestSignature ( t * testing . T ) {
testCases := map [ string ] struct {
method string
body string
key string
resp string
} {
"No request signature" : {
method : "GET" ,
body : "" ,
key : "" ,
resp : "no signature received" ,
} ,
"Get request" : {
method : "GET" ,
body : "" ,
key : "7d9e1aa87a5954e6f9fc59266b3af9d7c35fda2d" ,
resp : "signatures match" ,
} ,
"Post request" : {
method : "POST" ,
body : ` { "hello": "world!" } ` ,
key : "d90df39e2d19282840252612dd7c81421a372f61" ,
resp : "signatures match" ,
} ,
}
for name , tc := range testCases {
t . Run ( name , func ( t * testing . T ) {
st , err := NewSignatureTest ( )
if err != nil {
t . Fatal ( err )
}
t . Cleanup ( st . Close )
if tc . key != "" {
st . opts . SignatureKey = fmt . Sprintf ( "sha1:%s" , tc . key )
}
err = st . MakeRequestWithExpectedKey ( tc . method , tc . body , tc . key )
assert . NoError ( t , err )
assert . Equal ( t , 200 , st . rw . Code )
assert . Equal ( t , tc . resp , st . rw . Body . String ( ) )
} )
}
2015-11-16 06:08:30 +03:00
}
2019-01-29 15:13:02 +03:00
2019-01-30 13:13:12 +03:00
type ajaxRequestTest struct {
2020-04-13 15:50:34 +03:00
opts * options . Options
2019-01-30 13:13:12 +03:00
proxy * OAuthProxy
}
2021-10-05 12:24:47 +03:00
func newAjaxRequestTest ( forceJSONErrors bool ) ( * ajaxRequestTest , error ) {
2019-01-30 13:13:12 +03:00
test := & ajaxRequestTest { }
2020-05-25 16:00:49 +03:00
test . opts = baseTestOptions ( )
2021-10-05 12:24:47 +03:00
test . opts . ForceJSONErrors = forceJSONErrors
2020-07-13 22:56:05 +03:00
err := validation . Validate ( test . opts )
if err != nil {
return nil , err
}
2020-05-25 16:00:49 +03:00
test . proxy , err = NewOAuthProxy ( test . opts , func ( email string ) bool {
2019-01-30 13:13:12 +03:00
return true
} )
2020-05-25 16:00:49 +03:00
if err != nil {
2020-07-13 22:56:05 +03:00
return nil , err
2020-05-25 16:00:49 +03:00
}
2020-07-13 22:56:05 +03:00
return test , nil
2019-01-30 13:13:12 +03:00
}
2021-10-05 12:24:47 +03:00
func ( test * ajaxRequestTest ) getEndpoint ( endpoint string , header http . Header ) ( int , http . Header , [ ] byte , error ) {
2019-01-30 13:13:12 +03:00
rw := httptest . NewRecorder ( )
req , err := http . NewRequest ( http . MethodGet , endpoint , strings . NewReader ( "" ) )
if err != nil {
2021-10-05 12:24:47 +03:00
return 0 , nil , nil , err
2019-01-30 13:13:12 +03:00
}
req . Header = header
test . proxy . ServeHTTP ( rw , req )
2021-10-05 12:24:47 +03:00
return rw . Code , rw . Header ( ) , rw . Body . Bytes ( ) , nil
2019-01-30 13:13:12 +03:00
}
2021-10-05 12:24:47 +03:00
func testAjaxUnauthorizedRequest ( t * testing . T , header http . Header , forceJSONErrors bool ) {
test , err := newAjaxRequestTest ( forceJSONErrors )
2020-07-13 22:56:05 +03:00
if err != nil {
t . Fatal ( err )
}
2019-01-31 18:22:30 +03:00
endpoint := "/test"
2019-01-30 13:13:12 +03:00
2021-10-05 12:24:47 +03:00
code , rh , body , err := test . getEndpoint ( endpoint , header )
2019-01-30 13:13:12 +03:00
assert . NoError ( t , err )
assert . Equal ( t , http . StatusUnauthorized , code )
mime := rh . Get ( "Content-Type" )
2019-01-31 18:22:30 +03:00
assert . Equal ( t , applicationJSON , mime )
2021-10-05 12:24:47 +03:00
assert . Equal ( t , [ ] byte ( "{}" ) , body )
2019-01-30 13:13:12 +03:00
}
func TestAjaxUnauthorizedRequest1 ( t * testing . T ) {
header := make ( http . Header )
2019-01-31 18:22:30 +03:00
header . Add ( "accept" , applicationJSON )
2019-01-30 13:13:12 +03:00
2021-10-05 12:24:47 +03:00
testAjaxUnauthorizedRequest ( t , header , false )
2019-01-30 13:13:12 +03:00
}
func TestAjaxUnauthorizedRequest2 ( t * testing . T ) {
header := make ( http . Header )
2019-01-31 18:22:30 +03:00
header . Add ( "Accept" , applicationJSON )
2019-01-30 13:13:12 +03:00
2021-10-05 12:24:47 +03:00
testAjaxUnauthorizedRequest ( t , header , false )
2019-01-30 13:13:12 +03:00
}
2021-01-12 18:40:14 +03:00
func TestAjaxUnauthorizedRequestAccept1 ( t * testing . T ) {
header := make ( http . Header )
header . Add ( "Accept" , "application/json, text/plain, */*" )
2021-10-05 12:24:47 +03:00
testAjaxUnauthorizedRequest ( t , header , false )
}
func TestForceJSONErrorsUnauthorizedRequest ( t * testing . T ) {
testAjaxUnauthorizedRequest ( t , nil , true )
2021-01-12 18:40:14 +03:00
}
2019-01-30 13:13:12 +03:00
func TestAjaxForbiddendRequest ( t * testing . T ) {
2021-10-05 12:24:47 +03:00
test , err := newAjaxRequestTest ( false )
2020-07-13 22:56:05 +03:00
if err != nil {
t . Fatal ( err )
}
2019-01-31 18:22:30 +03:00
endpoint := "/test"
2019-01-30 13:13:12 +03:00
header := make ( http . Header )
2021-10-05 12:24:47 +03:00
code , rh , _ , err := test . getEndpoint ( endpoint , header )
2019-01-30 13:13:12 +03:00
assert . NoError ( t , err )
assert . Equal ( t , http . StatusForbidden , code )
mime := rh . Get ( "Content-Type" )
2019-01-31 18:22:30 +03:00
assert . NotEqual ( t , applicationJSON , mime )
2019-01-30 13:13:12 +03:00
}
2019-03-15 10:18:37 +03:00
func TestClearSplitCookie ( t * testing . T ) {
2020-05-25 16:00:49 +03:00
opts := baseTestOptions ( )
opts . Cookie . Secret = base64CookieSecret
2020-04-12 16:00:59 +03:00
opts . Cookie . Name = "oauth2"
opts . Cookie . Domains = [ ] string { "abc" }
2020-07-13 22:56:05 +03:00
err := validation . Validate ( opts )
assert . NoError ( t , err )
store , err := sessionscookie . NewCookieSessionStore ( & opts . Session , & opts . Cookie )
if err != nil {
t . Fatal ( err )
}
2021-04-21 12:33:27 +03:00
p := OAuthProxy { CookieOptions : & opts . Cookie , sessionStore : store }
2019-03-15 10:18:37 +03:00
var rw = httptest . NewRecorder ( )
req := httptest . NewRequest ( "get" , "/" , nil )
req . AddCookie ( & http . Cookie {
Name : "test1" ,
Value : "test1" ,
} )
req . AddCookie ( & http . Cookie {
Name : "oauth2_0" ,
Value : "oauth2_0" ,
} )
req . AddCookie ( & http . Cookie {
Name : "oauth2_1" ,
Value : "oauth2_1" ,
} )
2020-07-13 22:56:05 +03:00
err = p . ClearSessionCookie ( rw , req )
assert . NoError ( t , err )
2019-03-15 10:18:37 +03:00
header := rw . Header ( )
assert . Equal ( t , 2 , len ( header [ "Set-Cookie" ] ) , "should have 3 set-cookie header entries" )
}
func TestClearSingleCookie ( t * testing . T ) {
2020-05-25 16:00:49 +03:00
opts := baseTestOptions ( )
2020-04-12 16:00:59 +03:00
opts . Cookie . Name = "oauth2"
opts . Cookie . Domains = [ ] string { "abc" }
2020-07-13 22:56:05 +03:00
store , err := sessionscookie . NewCookieSessionStore ( & opts . Session , & opts . Cookie )
if err != nil {
t . Fatal ( err )
}
2021-04-21 12:33:27 +03:00
p := OAuthProxy { CookieOptions : & opts . Cookie , sessionStore : store }
2019-03-15 10:18:37 +03:00
var rw = httptest . NewRecorder ( )
req := httptest . NewRequest ( "get" , "/" , nil )
req . AddCookie ( & http . Cookie {
Name : "test1" ,
Value : "test1" ,
} )
req . AddCookie ( & http . Cookie {
Name : "oauth2" ,
Value : "oauth2" ,
} )
2020-07-13 22:56:05 +03:00
err = p . ClearSessionCookie ( rw , req )
assert . NoError ( t , err )
2019-03-15 10:18:37 +03:00
header := rw . Header ( )
assert . Equal ( t , 1 , len ( header [ "Set-Cookie" ] ) , "should have 1 set-cookie header entries" )
}
2019-04-24 18:25:29 +03:00
2019-04-27 05:16:45 +03:00
type NoOpKeySet struct {
}
2020-09-27 21:46:29 +03:00
func ( NoOpKeySet ) VerifySignature ( _ context . Context , jwt string ) ( payload [ ] byte , err error ) {
2019-04-27 05:16:45 +03:00
splitStrings := strings . Split ( jwt , "." )
payloadString := splitStrings [ 1 ]
2020-04-14 11:36:44 +03:00
return base64 . RawURLEncoding . DecodeString ( payloadString )
2019-04-27 05:16:45 +03:00
}
func TestGetJwtSession ( t * testing . T ) {
/ * token payload :
{
"sub" : "1234567890" ,
"aud" : "https://test.myapp.com" ,
"name" : "John Doe" ,
"email" : "john@example.com" ,
"iss" : "https://issuer.example.com" ,
"iat" : 1553691215 ,
"exp" : 1912151821
}
* /
goodJwt := "eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCJ9." +
"eyJzdWIiOiIxMjM0NTY3ODkwIiwiYXVkIjoiaHR0cHM6Ly90ZXN0Lm15YXBwLmNvbSIsIm5hbWUiOiJKb2huIERvZSIsImVtY" +
"WlsIjoiam9obkBleGFtcGxlLmNvbSIsImlzcyI6Imh0dHBzOi8vaXNzdWVyLmV4YW1wbGUuY29tIiwiaWF0IjoxNTUzNjkxMj" +
"E1LCJleHAiOjE5MTIxNTE4MjF9." +
"rLVyzOnEldUq_pNkfa-WiV8TVJYWyZCaM2Am_uo8FGg11zD7l-qmz3x1seTvqpH6Y0Ty00fmv6dJnGnC8WMnPXQiodRTfhBSe" +
"OKZMu0HkMD2sg52zlKkbfLTO6ic5VnbVgwjjrB8am_Ta6w7kyFUaB5C1BsIrrLMldkWEhynbb8"
keyset := NoOpKeySet { }
verifier := oidc . NewVerifier ( "https://issuer.example.com" , keyset ,
2022-02-15 19:12:22 +03:00
& oidc . Config { ClientID : "https://test.myapp.com" , SkipExpiryCheck : true ,
SkipClientIDCheck : true } )
2022-02-16 18:55:44 +03:00
verificationOptions := internaloidc . IDTokenVerificationOptions {
2022-02-15 19:12:22 +03:00
AudienceClaims : [ ] string { "aud" } ,
ClientID : "https://test.myapp.com" ,
ExtraAudiences : [ ] string { } ,
}
internalVerifier := internaloidc . NewVerifier ( verifier , verificationOptions )
2019-04-27 05:16:45 +03:00
2020-10-19 04:14:32 +03:00
test , err := NewAuthOnlyEndpointTest ( "" , func ( opts * options . Options ) {
2020-07-29 22:10:14 +03:00
opts . InjectRequestHeaders = [ ] options . Header {
{
Name : "Authorization" ,
Values : [ ] options . HeaderValue {
{
ClaimSource : & options . ClaimSource {
Claim : "id_token" ,
Prefix : "Bearer " ,
} ,
} ,
} ,
} ,
{
Name : "X-Forwarded-User" ,
Values : [ ] options . HeaderValue {
{
ClaimSource : & options . ClaimSource {
Claim : "user" ,
} ,
} ,
} ,
} ,
{
Name : "X-Forwarded-Email" ,
Values : [ ] options . HeaderValue {
{
ClaimSource : & options . ClaimSource {
Claim : "email" ,
} ,
} ,
} ,
} ,
}
opts . InjectResponseHeaders = [ ] options . Header {
{
Name : "Authorization" ,
Values : [ ] options . HeaderValue {
{
ClaimSource : & options . ClaimSource {
Claim : "id_token" ,
Prefix : "Bearer " ,
} ,
} ,
} ,
} ,
{
Name : "X-Auth-Request-User" ,
Values : [ ] options . HeaderValue {
{
ClaimSource : & options . ClaimSource {
Claim : "user" ,
} ,
} ,
} ,
} ,
{
Name : "X-Auth-Request-Email" ,
Values : [ ] options . HeaderValue {
{
ClaimSource : & options . ClaimSource {
Claim : "email" ,
} ,
} ,
} ,
} ,
}
2019-06-21 02:57:20 +03:00
opts . SkipJwtBearerTokens = true
2022-02-15 19:12:22 +03:00
opts . SetJWTBearerVerifiers ( append ( opts . GetJWTBearerVerifiers ( ) , internalVerifier ) )
2019-06-21 02:57:20 +03:00
} )
2020-07-13 22:56:05 +03:00
if err != nil {
t . Fatal ( err )
}
2019-06-21 02:57:20 +03:00
tp , _ := test . proxy . provider . ( * TestProvider )
tp . GroupValidator = func ( s string ) bool {
return true
}
2019-05-01 00:06:11 +03:00
authHeader := fmt . Sprintf ( "Bearer %s" , goodJwt )
2019-06-21 02:57:20 +03:00
test . req . Header = map [ string ] [ ] string {
2019-05-01 00:06:11 +03:00
"Authorization" : { authHeader } ,
2019-04-27 05:16:45 +03:00
}
2019-05-01 00:06:11 +03:00
2019-06-21 02:57:20 +03:00
test . proxy . ServeHTTP ( test . rw , test . req )
if test . rw . Code >= 400 {
t . Fatalf ( "expected 3xx got %d" , test . rw . Code )
}
2019-05-01 00:06:11 +03:00
2019-06-21 02:57:20 +03:00
// Check PassAuthorization, should overwrite Basic header
assert . Equal ( t , test . req . Header . Get ( "Authorization" ) , authHeader )
2020-06-01 18:56:50 +03:00
assert . Equal ( t , test . req . Header . Get ( "X-Forwarded-User" ) , "1234567890" )
2019-06-21 02:57:20 +03:00
assert . Equal ( t , test . req . Header . Get ( "X-Forwarded-Email" ) , "john@example.com" )
2019-05-01 00:06:11 +03:00
2019-06-21 02:57:20 +03:00
// SetAuthorization and SetXAuthRequest
assert . Equal ( t , test . rw . Header ( ) . Get ( "Authorization" ) , authHeader )
2020-06-01 18:56:50 +03:00
assert . Equal ( t , test . rw . Header ( ) . Get ( "X-Auth-Request-User" ) , "1234567890" )
2019-06-21 02:57:20 +03:00
assert . Equal ( t , test . rw . Header ( ) . Get ( "X-Auth-Request-Email" ) , "john@example.com" )
}
2019-05-01 00:06:11 +03:00
2020-04-09 17:39:07 +03:00
func Test_prepareNoCache ( t * testing . T ) {
handler := http . HandlerFunc ( func ( w http . ResponseWriter , r * http . Request ) {
prepareNoCache ( w )
} )
mux := http . NewServeMux ( )
mux . Handle ( "/" , handler )
rec := httptest . NewRecorder ( )
req := httptest . NewRequest ( http . MethodGet , "/" , nil )
mux . ServeHTTP ( rec , req )
for k , v := range noCacheHeaders {
assert . Equal ( t , rec . Header ( ) . Get ( k ) , v )
}
}
2020-07-06 13:04:31 +03:00
func Test_noCacheHeaders ( t * testing . T ) {
2020-09-24 06:16:05 +03:00
upstreamServer := httptest . NewServer ( http . HandlerFunc ( func ( w http . ResponseWriter , r * http . Request ) {
2020-07-13 22:56:05 +03:00
_ , err := w . Write ( [ ] byte ( "upstream" ) )
if err != nil {
t . Error ( err )
}
2020-04-09 17:39:07 +03:00
} ) )
2020-09-24 06:16:05 +03:00
t . Cleanup ( upstreamServer . Close )
2020-04-09 17:39:07 +03:00
2020-05-25 16:00:49 +03:00
opts := baseTestOptions ( )
2021-09-17 14:08:18 +03:00
opts . UpstreamServers = options . UpstreamConfig {
Upstreams : [ ] options . Upstream {
2021-08-09 16:32:15 +03:00
{
ID : upstreamServer . URL ,
Path : "/" ,
URI : upstreamServer . URL ,
} ,
2020-05-26 22:06:27 +03:00
} ,
}
2020-04-09 17:39:07 +03:00
opts . SkipAuthRegex = [ ] string { ".*" }
2020-07-13 22:56:05 +03:00
err := validation . Validate ( opts )
2020-05-25 16:00:49 +03:00
assert . NoError ( t , err )
2020-07-13 22:56:05 +03:00
proxy , err := NewOAuthProxy ( opts , func ( _ string ) bool { return true } )
if err != nil {
t . Fatal ( err )
}
2020-04-09 17:39:07 +03:00
2020-07-06 13:04:31 +03:00
t . Run ( "not exist in response from upstream" , func ( t * testing . T ) {
rec := httptest . NewRecorder ( )
req := httptest . NewRequest ( http . MethodGet , "/upstream" , nil )
proxy . ServeHTTP ( rec , req )
2020-04-09 17:39:07 +03:00
2020-07-06 13:04:31 +03:00
assert . Equal ( t , http . StatusOK , rec . Code )
assert . Equal ( t , "upstream" , rec . Body . String ( ) )
2020-04-09 17:39:07 +03:00
2020-07-06 13:04:31 +03:00
// checking noCacheHeaders does not exists in response headers from upstream
for k := range noCacheHeaders {
assert . Equal ( t , "" , rec . Header ( ) . Get ( k ) )
}
} )
t . Run ( "has no-cache" , func ( t * testing . T ) {
tests := [ ] struct {
path string
hasNoCache bool
} {
{
path : "/oauth2/sign_in" ,
hasNoCache : true ,
} ,
{
path : "/oauth2/sign_out" ,
hasNoCache : true ,
} ,
{
path : "/oauth2/start" ,
hasNoCache : true ,
} ,
{
path : "/oauth2/callback" ,
hasNoCache : true ,
} ,
{
path : "/oauth2/auth" ,
hasNoCache : false ,
} ,
{
path : "/oauth2/userinfo" ,
hasNoCache : true ,
} ,
{
path : "/upstream" ,
hasNoCache : false ,
} ,
}
for _ , tt := range tests {
t . Run ( tt . path , func ( t * testing . T ) {
rec := httptest . NewRecorder ( )
req := httptest . NewRequest ( http . MethodGet , tt . path , nil )
proxy . ServeHTTP ( rec , req )
cacheControl := rec . Result ( ) . Header . Get ( "Cache-Control" )
if tt . hasNoCache != ( strings . Contains ( cacheControl , "no-cache" ) ) {
t . Errorf ( ` unexpected "Cache-Control" header: %s ` , cacheControl )
}
} )
}
} )
2020-04-09 17:39:07 +03:00
}
2020-05-25 16:00:49 +03:00
func baseTestOptions ( ) * options . Options {
opts := options . NewOptions ( )
opts . Cookie . Secret = rawCookieSecret
2021-04-03 19:06:30 +03:00
opts . Providers [ 0 ] . ID = "providerID"
opts . Providers [ 0 ] . ClientID = clientID
opts . Providers [ 0 ] . ClientSecret = clientSecret
2020-05-25 16:00:49 +03:00
opts . EmailDomains = [ ] string { "*" }
2020-07-29 22:10:14 +03:00
// Default injected headers for legacy configuration
opts . InjectRequestHeaders = [ ] options . Header {
{
Name : "Authorization" ,
Values : [ ] options . HeaderValue {
{
ClaimSource : & options . ClaimSource {
Claim : "user" ,
BasicAuthPassword : & options . SecretSource {
Value : [ ] byte ( base64 . StdEncoding . EncodeToString ( [ ] byte ( "This is a secure password" ) ) ) ,
} ,
} ,
} ,
} ,
} ,
{
Name : "X-Forwarded-User" ,
Values : [ ] options . HeaderValue {
{
ClaimSource : & options . ClaimSource {
Claim : "user" ,
} ,
} ,
} ,
} ,
{
Name : "X-Forwarded-Email" ,
Values : [ ] options . HeaderValue {
{
ClaimSource : & options . ClaimSource {
Claim : "email" ,
} ,
} ,
} ,
} ,
}
2021-02-14 20:08:04 +03:00
2020-05-25 16:00:49 +03:00
return opts
}
2020-07-11 13:10:58 +03:00
func TestTrustedIPs ( t * testing . T ) {
tests := [ ] struct {
name string
trustedIPs [ ] string
reverseProxy bool
realClientIPHeader string
req * http . Request
expectTrusted bool
} {
// Check unconfigured behavior.
{
name : "Default" ,
trustedIPs : nil ,
reverseProxy : false ,
realClientIPHeader : "X-Real-IP" , // Default value
req : func ( ) * http . Request {
req , _ := http . NewRequest ( "GET" , "/" , nil )
return req
} ( ) ,
expectTrusted : false ,
} ,
// Check using req.RemoteAddr (Options.ReverseProxy == false).
{
name : "WithRemoteAddr" ,
trustedIPs : [ ] string { "127.0.0.1" } ,
reverseProxy : false ,
realClientIPHeader : "X-Real-IP" , // Default value
req : func ( ) * http . Request {
req , _ := http . NewRequest ( "GET" , "/" , nil )
req . RemoteAddr = "127.0.0.1:43670"
return req
} ( ) ,
expectTrusted : true ,
} ,
// Check ignores req.RemoteAddr match when behind a reverse proxy / missing header.
{
name : "IgnoresRemoteAddrInReverseProxyMode" ,
trustedIPs : [ ] string { "127.0.0.1" } ,
reverseProxy : true ,
realClientIPHeader : "X-Real-IP" , // Default value
req : func ( ) * http . Request {
req , _ := http . NewRequest ( "GET" , "/" , nil )
req . RemoteAddr = "127.0.0.1:44324"
return req
} ( ) ,
expectTrusted : false ,
} ,
// Check successful trusting of localhost in IPv4.
{
name : "TrustsLocalhostInReverseProxyMode" ,
trustedIPs : [ ] string { "127.0.0.0/8" , "::1" } ,
reverseProxy : true ,
realClientIPHeader : "X-Forwarded-For" ,
req : func ( ) * http . Request {
req , _ := http . NewRequest ( "GET" , "/" , nil )
req . Header . Add ( "X-Forwarded-For" , "127.0.0.1" )
return req
} ( ) ,
expectTrusted : true ,
} ,
// Check successful trusting of localhost in IPv6.
{
name : "TrustsIP6LocalostInReverseProxyMode" ,
trustedIPs : [ ] string { "127.0.0.0/8" , "::1" } ,
reverseProxy : true ,
realClientIPHeader : "X-Forwarded-For" ,
req : func ( ) * http . Request {
req , _ := http . NewRequest ( "GET" , "/" , nil )
req . Header . Add ( "X-Forwarded-For" , "::1" )
return req
} ( ) ,
expectTrusted : true ,
} ,
// Check does not trust random IPv4 address.
{
name : "DoesNotTrustRandomIP4Address" ,
trustedIPs : [ ] string { "127.0.0.0/8" , "::1" } ,
reverseProxy : true ,
realClientIPHeader : "X-Forwarded-For" ,
req : func ( ) * http . Request {
req , _ := http . NewRequest ( "GET" , "/" , nil )
req . Header . Add ( "X-Forwarded-For" , "12.34.56.78" )
return req
} ( ) ,
expectTrusted : false ,
} ,
// Check does not trust random IPv6 address.
{
name : "DoesNotTrustRandomIP6Address" ,
trustedIPs : [ ] string { "127.0.0.0/8" , "::1" } ,
reverseProxy : true ,
realClientIPHeader : "X-Forwarded-For" ,
req : func ( ) * http . Request {
req , _ := http . NewRequest ( "GET" , "/" , nil )
req . Header . Add ( "X-Forwarded-For" , "::2" )
return req
} ( ) ,
expectTrusted : false ,
} ,
// Check respects correct header.
{
name : "RespectsCorrectHeaderInReverseProxyMode" ,
trustedIPs : [ ] string { "127.0.0.0/8" , "::1" } ,
reverseProxy : true ,
realClientIPHeader : "X-Forwarded-For" ,
req : func ( ) * http . Request {
req , _ := http . NewRequest ( "GET" , "/" , nil )
req . Header . Add ( "X-Real-IP" , "::1" )
return req
} ( ) ,
expectTrusted : false ,
} ,
// Check doesn't trust if garbage is provided.
{
name : "DoesNotTrustGarbageInReverseProxyMode" ,
trustedIPs : [ ] string { "127.0.0.0/8" , "::1" } ,
reverseProxy : true ,
realClientIPHeader : "X-Forwarded-For" ,
req : func ( ) * http . Request {
req , _ := http . NewRequest ( "GET" , "/" , nil )
req . Header . Add ( "X-Forwarded-For" , "adsfljk29242as!!" )
return req
} ( ) ,
expectTrusted : false ,
} ,
// Check doesn't trust if garbage is provided (no reverse-proxy).
{
name : "DoesNotTrustGarbage" ,
trustedIPs : [ ] string { "127.0.0.0/8" , "::1" } ,
reverseProxy : false ,
realClientIPHeader : "X-Real-IP" ,
req : func ( ) * http . Request {
req , _ := http . NewRequest ( "GET" , "/" , nil )
req . RemoteAddr = "adsfljk29242as!!"
return req
} ( ) ,
expectTrusted : false ,
} ,
}
for _ , tt := range tests {
t . Run ( tt . name , func ( t * testing . T ) {
opts := baseTestOptions ( )
2021-09-17 14:08:18 +03:00
opts . UpstreamServers = options . UpstreamConfig {
Upstreams : [ ] options . Upstream {
2021-08-09 16:32:15 +03:00
{
ID : "static" ,
Path : "/" ,
Static : true ,
} ,
2020-05-26 22:06:27 +03:00
} ,
}
2020-07-11 13:10:58 +03:00
opts . TrustedIPs = tt . trustedIPs
opts . ReverseProxy = tt . reverseProxy
opts . RealClientIPHeader = tt . realClientIPHeader
2020-09-24 06:16:05 +03:00
err := validation . Validate ( opts )
assert . NoError ( t , err )
2020-07-11 13:10:58 +03:00
proxy , err := NewOAuthProxy ( opts , func ( string ) bool { return true } )
assert . NoError ( t , err )
rw := httptest . NewRecorder ( )
proxy . ServeHTTP ( rw , tt . req )
if tt . expectTrusted {
assert . Equal ( t , 200 , rw . Code )
} else {
assert . Equal ( t , 403 , rw . Code )
}
} )
}
}
2020-07-28 21:42:09 +03:00
2020-09-24 06:16:05 +03:00
func Test_buildRoutesAllowlist ( t * testing . T ) {
2020-10-05 22:39:44 +03:00
type expectedAllowedRoute struct {
method string
2022-08-19 13:46:25 +03:00
negate bool
2020-10-05 22:39:44 +03:00
regexString string
}
2020-09-24 06:16:05 +03:00
testCases := [ ] struct {
2020-10-05 22:39:44 +03:00
name string
skipAuthRegex [ ] string
skipAuthRoutes [ ] string
expectedRoutes [ ] expectedAllowedRoute
shouldError bool
2020-09-24 06:16:05 +03:00
} {
{
2020-10-05 22:39:44 +03:00
name : "No skip auth configured" ,
skipAuthRegex : [ ] string { } ,
skipAuthRoutes : [ ] string { } ,
expectedRoutes : [ ] expectedAllowedRoute { } ,
shouldError : false ,
2020-09-24 06:16:05 +03:00
} ,
{
name : "Only skipAuthRegex configured" ,
skipAuthRegex : [ ] string {
"^/foo/bar" ,
"^/baz/[0-9]+/thing" ,
} ,
skipAuthRoutes : [ ] string { } ,
2020-10-05 22:39:44 +03:00
expectedRoutes : [ ] expectedAllowedRoute {
{
method : "" ,
2022-08-19 13:46:25 +03:00
negate : false ,
2020-10-05 22:39:44 +03:00
regexString : "^/foo/bar" ,
} ,
{
method : "" ,
2022-08-19 13:46:25 +03:00
negate : false ,
2020-10-05 22:39:44 +03:00
regexString : "^/baz/[0-9]+/thing" ,
} ,
2020-09-24 06:16:05 +03:00
} ,
shouldError : false ,
} ,
{
name : "Only skipAuthRoutes configured" ,
skipAuthRegex : [ ] string { } ,
skipAuthRoutes : [ ] string {
"GET=^/foo/bar" ,
"POST=^/baz/[0-9]+/thing" ,
"^/all/methods$" ,
"WEIRD=^/methods/are/allowed" ,
"PATCH=/second/equals?are=handled&just=fine" ,
2022-08-19 13:46:25 +03:00
"!=^/api" ,
"METHOD!=^/api" ,
2020-09-24 06:16:05 +03:00
} ,
2020-10-05 22:39:44 +03:00
expectedRoutes : [ ] expectedAllowedRoute {
{
method : "GET" ,
2022-08-19 13:46:25 +03:00
negate : false ,
2020-10-05 22:39:44 +03:00
regexString : "^/foo/bar" ,
} ,
{
method : "POST" ,
2022-08-19 13:46:25 +03:00
negate : false ,
2020-10-05 22:39:44 +03:00
regexString : "^/baz/[0-9]+/thing" ,
} ,
{
method : "" ,
2022-08-19 13:46:25 +03:00
negate : false ,
2020-10-05 22:39:44 +03:00
regexString : "^/all/methods$" ,
} ,
{
method : "WEIRD" ,
2022-08-19 13:46:25 +03:00
negate : false ,
2020-10-05 22:39:44 +03:00
regexString : "^/methods/are/allowed" ,
} ,
{
method : "PATCH" ,
2022-08-19 13:46:25 +03:00
negate : false ,
2020-10-05 22:39:44 +03:00
regexString : "/second/equals?are=handled&just=fine" ,
} ,
2022-08-19 13:46:25 +03:00
{
method : "" ,
negate : true ,
regexString : "^/api" ,
} ,
{
method : "METHOD" ,
negate : true ,
regexString : "^/api" ,
} ,
2020-09-24 06:16:05 +03:00
} ,
shouldError : false ,
} ,
{
name : "Both skipAuthRegexes and skipAuthRoutes configured" ,
skipAuthRegex : [ ] string {
"^/foo/bar/regex" ,
"^/baz/[0-9]+/thing/regex" ,
} ,
skipAuthRoutes : [ ] string {
"GET=^/foo/bar" ,
"POST=^/baz/[0-9]+/thing" ,
"^/all/methods$" ,
} ,
2020-10-05 22:39:44 +03:00
expectedRoutes : [ ] expectedAllowedRoute {
{
method : "" ,
regexString : "^/foo/bar/regex" ,
} ,
{
method : "" ,
regexString : "^/baz/[0-9]+/thing/regex" ,
} ,
{
method : "GET" ,
regexString : "^/foo/bar" ,
} ,
{
method : "POST" ,
regexString : "^/baz/[0-9]+/thing" ,
} ,
{
method : "" ,
regexString : "^/all/methods$" ,
} ,
2020-09-24 06:16:05 +03:00
} ,
shouldError : false ,
} ,
{
name : "Invalid skipAuthRegex entry" ,
skipAuthRegex : [ ] string {
"^/foo/bar" ,
"^/baz/[0-9]+/thing" ,
"(bad[regex" ,
} ,
2020-10-05 22:39:44 +03:00
skipAuthRoutes : [ ] string { } ,
expectedRoutes : [ ] expectedAllowedRoute { } ,
shouldError : true ,
2020-09-24 06:16:05 +03:00
} ,
{
name : "Invalid skipAuthRoutes entry" ,
skipAuthRegex : [ ] string { } ,
skipAuthRoutes : [ ] string {
"GET=^/foo/bar" ,
"POST=^/baz/[0-9]+/thing" ,
"^/all/methods$" ,
"PUT=(bad[regex" ,
} ,
2020-10-05 22:39:44 +03:00
expectedRoutes : [ ] expectedAllowedRoute { } ,
shouldError : true ,
2020-09-24 06:16:05 +03:00
} ,
}
for _ , tc := range testCases {
t . Run ( tc . name , func ( t * testing . T ) {
opts := & options . Options {
SkipAuthRegex : tc . skipAuthRegex ,
SkipAuthRoutes : tc . skipAuthRoutes ,
}
routes , err := buildRoutesAllowlist ( opts )
if tc . shouldError {
assert . Error ( t , err )
return
}
2020-09-26 22:38:01 +03:00
assert . NoError ( t , err )
2020-09-24 06:16:05 +03:00
for i , route := range routes {
2020-10-05 22:39:44 +03:00
assert . Greater ( t , len ( tc . expectedRoutes ) , i )
assert . Equal ( t , route . method , tc . expectedRoutes [ i ] . method )
2022-08-19 13:46:25 +03:00
assert . Equal ( t , route . negate , tc . expectedRoutes [ i ] . negate )
2020-10-05 22:39:44 +03:00
assert . Equal ( t , route . pathRegex . String ( ) , tc . expectedRoutes [ i ] . regexString )
2020-09-24 06:16:05 +03:00
}
} )
}
}
2022-09-11 18:09:32 +03:00
func TestApiRoutes ( t * testing . T ) {
ajaxAPIServer := httptest . NewServer ( http . HandlerFunc ( func ( w http . ResponseWriter , r * http . Request ) {
w . WriteHeader ( 200 )
_ , err := w . Write ( [ ] byte ( "AJAX API Request" ) )
if err != nil {
t . Fatal ( err )
}
} ) )
t . Cleanup ( ajaxAPIServer . Close )
apiServer := httptest . NewServer ( http . HandlerFunc ( func ( w http . ResponseWriter , r * http . Request ) {
w . WriteHeader ( 200 )
_ , err := w . Write ( [ ] byte ( "API Request" ) )
if err != nil {
t . Fatal ( err )
}
} ) )
t . Cleanup ( apiServer . Close )
uiServer := httptest . NewServer ( http . HandlerFunc ( func ( w http . ResponseWriter , r * http . Request ) {
w . WriteHeader ( 200 )
_ , err := w . Write ( [ ] byte ( "API Request" ) )
if err != nil {
t . Fatal ( err )
}
} ) )
t . Cleanup ( uiServer . Close )
opts := baseTestOptions ( )
opts . UpstreamServers = options . UpstreamConfig {
Upstreams : [ ] options . Upstream {
{
ID : apiServer . URL ,
Path : "/api" ,
URI : apiServer . URL ,
} ,
{
ID : ajaxAPIServer . URL ,
Path : "/ajaxapi" ,
URI : ajaxAPIServer . URL ,
} ,
{
ID : uiServer . URL ,
Path : "/ui" ,
URI : uiServer . URL ,
} ,
} ,
}
opts . APIRoutes = [ ] string {
"^/api" ,
}
opts . SkipProviderButton = true
err := validation . Validate ( opts )
assert . NoError ( t , err )
proxy , err := NewOAuthProxy ( opts , func ( _ string ) bool { return true } )
if err != nil {
t . Fatal ( err )
}
testCases := [ ] struct {
name string
contentType string
url string
shouldRedirect bool
} {
{
name : "AJAX request matching API regex" ,
contentType : "application/json" ,
url : "/api/v1/UserInfo" ,
shouldRedirect : false ,
} ,
{
name : "AJAX request not matching API regex" ,
contentType : "application/json" ,
url : "/ajaxapi/v1/UserInfo" ,
shouldRedirect : false ,
} ,
{
name : "Other Request matching API regex" ,
contentType : "application/grpcwebtext" ,
url : "/api/v1/UserInfo" ,
shouldRedirect : false ,
} ,
{
name : "UI request" ,
contentType : "html" ,
url : "/ui/index.html" ,
shouldRedirect : true ,
} ,
}
for _ , tc := range testCases {
t . Run ( tc . name , func ( t * testing . T ) {
req , err := http . NewRequest ( "GET" , tc . url , nil )
req . Header . Set ( "Accept" , tc . contentType )
assert . NoError ( t , err )
rw := httptest . NewRecorder ( )
proxy . ServeHTTP ( rw , req )
if tc . shouldRedirect {
assert . Equal ( t , 302 , rw . Code )
} else {
assert . Equal ( t , 401 , rw . Code )
}
} )
}
}
2020-09-24 06:16:05 +03:00
func TestAllowedRequest ( t * testing . T ) {
upstreamServer := httptest . NewServer ( http . HandlerFunc ( func ( w http . ResponseWriter , r * http . Request ) {
w . WriteHeader ( 200 )
_ , err := w . Write ( [ ] byte ( "Allowed Request" ) )
if err != nil {
t . Fatal ( err )
}
} ) )
t . Cleanup ( upstreamServer . Close )
opts := baseTestOptions ( )
2021-09-17 14:08:18 +03:00
opts . UpstreamServers = options . UpstreamConfig {
Upstreams : [ ] options . Upstream {
2021-08-09 16:32:15 +03:00
{
ID : upstreamServer . URL ,
Path : "/" ,
URI : upstreamServer . URL ,
} ,
2020-09-24 06:16:05 +03:00
} ,
}
opts . SkipAuthRegex = [ ] string {
"^/skip/auth/regex$" ,
}
opts . SkipAuthRoutes = [ ] string {
"GET=^/skip/auth/routes/get" ,
}
err := validation . Validate ( opts )
assert . NoError ( t , err )
proxy , err := NewOAuthProxy ( opts , func ( _ string ) bool { return true } )
if err != nil {
t . Fatal ( err )
}
testCases := [ ] struct {
name string
method string
url string
allowed bool
} {
{
name : "Regex GET allowed" ,
method : "GET" ,
url : "/skip/auth/regex" ,
allowed : true ,
} ,
{
name : "Regex POST allowed " ,
method : "POST" ,
url : "/skip/auth/regex" ,
allowed : true ,
} ,
{
name : "Regex denied" ,
method : "GET" ,
url : "/wrong/denied" ,
allowed : false ,
} ,
{
name : "Route allowed" ,
method : "GET" ,
url : "/skip/auth/routes/get" ,
allowed : true ,
} ,
{
name : "Route denied with wrong method" ,
method : "PATCH" ,
url : "/skip/auth/routes/get" ,
allowed : false ,
} ,
{
name : "Route denied with wrong path" ,
method : "GET" ,
url : "/skip/auth/routes/wrong/path" ,
allowed : false ,
} ,
2022-08-19 13:46:25 +03:00
{
name : "Route denied with wrong path and method" ,
method : "POST" ,
url : "/skip/auth/routes/wrong/path" ,
allowed : false ,
} ,
}
for _ , tc := range testCases {
t . Run ( tc . name , func ( t * testing . T ) {
req , err := http . NewRequest ( tc . method , tc . url , nil )
assert . NoError ( t , err )
assert . Equal ( t , tc . allowed , proxy . isAllowedRoute ( req ) )
rw := httptest . NewRecorder ( )
proxy . ServeHTTP ( rw , req )
if tc . allowed {
assert . Equal ( t , 200 , rw . Code )
assert . Equal ( t , "Allowed Request" , rw . Body . String ( ) )
} else {
assert . Equal ( t , 403 , rw . Code )
}
} )
}
}
func TestAllowedRequestNegateWithoutMethod ( t * testing . T ) {
upstreamServer := httptest . NewServer ( http . HandlerFunc ( func ( w http . ResponseWriter , r * http . Request ) {
w . WriteHeader ( 200 )
_ , err := w . Write ( [ ] byte ( "Allowed Request" ) )
if err != nil {
t . Fatal ( err )
}
} ) )
t . Cleanup ( upstreamServer . Close )
opts := baseTestOptions ( )
opts . UpstreamServers = options . UpstreamConfig {
Upstreams : [ ] options . Upstream {
{
ID : upstreamServer . URL ,
Path : "/" ,
URI : upstreamServer . URL ,
} ,
} ,
}
opts . SkipAuthRoutes = [ ] string {
"!=^/api" , // any non-api routes
"POST=^/api/public-entity/?$" ,
}
err := validation . Validate ( opts )
assert . NoError ( t , err )
proxy , err := NewOAuthProxy ( opts , func ( _ string ) bool { return true } )
if err != nil {
t . Fatal ( err )
}
testCases := [ ] struct {
name string
method string
url string
allowed bool
} {
{
name : "Some static file allowed" ,
method : "GET" ,
url : "/static/file.txt" ,
allowed : true ,
} ,
{
name : "POST to contact form allowed" ,
method : "POST" ,
url : "/contact" ,
allowed : true ,
} ,
{
name : "Regex POST allowed" ,
method : "POST" ,
url : "/api/public-entity" ,
allowed : true ,
} ,
{
name : "Regex POST with trailing slash allowed" ,
method : "POST" ,
url : "/api/public-entity/" ,
allowed : true ,
} ,
{
name : "Regex GET api route denied" ,
method : "GET" ,
url : "/api/users" ,
allowed : false ,
} ,
{
name : "Regex POST api route denied" ,
method : "POST" ,
url : "/api/users" ,
allowed : false ,
} ,
{
name : "Regex DELETE api route denied" ,
method : "DELETE" ,
url : "/api/users/1" ,
allowed : false ,
} ,
}
for _ , tc := range testCases {
t . Run ( tc . name , func ( t * testing . T ) {
req , err := http . NewRequest ( tc . method , tc . url , nil )
assert . NoError ( t , err )
assert . Equal ( t , tc . allowed , proxy . isAllowedRoute ( req ) )
rw := httptest . NewRecorder ( )
proxy . ServeHTTP ( rw , req )
if tc . allowed {
assert . Equal ( t , 200 , rw . Code )
assert . Equal ( t , "Allowed Request" , rw . Body . String ( ) )
} else {
assert . Equal ( t , 403 , rw . Code )
}
} )
}
}
func TestAllowedRequestNegateWithMethod ( t * testing . T ) {
upstreamServer := httptest . NewServer ( http . HandlerFunc ( func ( w http . ResponseWriter , r * http . Request ) {
w . WriteHeader ( 200 )
_ , err := w . Write ( [ ] byte ( "Allowed Request" ) )
if err != nil {
t . Fatal ( err )
}
} ) )
t . Cleanup ( upstreamServer . Close )
opts := baseTestOptions ( )
opts . UpstreamServers = options . UpstreamConfig {
Upstreams : [ ] options . Upstream {
{
ID : upstreamServer . URL ,
Path : "/" ,
URI : upstreamServer . URL ,
} ,
} ,
}
opts . SkipAuthRoutes = [ ] string {
"GET!=^/api" , // any non-api routes
"POST=^/api/public-entity/?$" ,
}
err := validation . Validate ( opts )
assert . NoError ( t , err )
proxy , err := NewOAuthProxy ( opts , func ( _ string ) bool { return true } )
if err != nil {
t . Fatal ( err )
}
testCases := [ ] struct {
name string
method string
url string
allowed bool
} {
{
name : "Some static file allowed" ,
method : "GET" ,
url : "/static/file.txt" ,
allowed : true ,
} ,
{
name : "POST to contact form not allowed" ,
method : "POST" ,
url : "/contact" ,
allowed : false ,
} ,
{
name : "Regex POST allowed" ,
method : "POST" ,
url : "/api/public-entity" ,
allowed : true ,
} ,
{
name : "Regex POST with trailing slash allowed" ,
method : "POST" ,
url : "/api/public-entity/" ,
allowed : true ,
} ,
{
name : "Regex GET api route denied" ,
method : "GET" ,
url : "/api/users" ,
allowed : false ,
} ,
{
name : "Regex POST api route denied" ,
method : "POST" ,
url : "/api/users" ,
allowed : false ,
} ,
{
name : "Regex DELETE api route denied" ,
method : "DELETE" ,
url : "/api/users/1" ,
allowed : false ,
} ,
2020-09-24 06:16:05 +03:00
}
for _ , tc := range testCases {
t . Run ( tc . name , func ( t * testing . T ) {
req , err := http . NewRequest ( tc . method , tc . url , nil )
assert . NoError ( t , err )
assert . Equal ( t , tc . allowed , proxy . isAllowedRoute ( req ) )
rw := httptest . NewRecorder ( )
proxy . ServeHTTP ( rw , req )
if tc . allowed {
assert . Equal ( t , 200 , rw . Code )
assert . Equal ( t , "Allowed Request" , rw . Body . String ( ) )
} else {
assert . Equal ( t , 403 , rw . Code )
}
} )
}
}
2020-07-28 21:42:09 +03:00
func TestProxyAllowedGroups ( t * testing . T ) {
tests := [ ] struct {
name string
allowedGroups [ ] string
groups [ ] string
expectUnauthorized bool
} {
{ "NoAllowedGroups" , [ ] string { } , [ ] string { } , false } ,
{ "NoAllowedGroupsUserHasGroups" , [ ] string { } , [ ] string { "a" , "b" } , false } ,
{ "UserInAllowedGroup" , [ ] string { "a" } , [ ] string { "a" , "b" } , false } ,
{ "UserNotInAllowedGroup" , [ ] string { "a" } , [ ] string { "c" } , true } ,
}
for _ , tt := range tests {
t . Run ( tt . name , func ( t * testing . T ) {
emailAddress := "test"
created := time . Now ( )
session := & sessions . SessionState {
Groups : tt . groups ,
Email : emailAddress ,
AccessToken : "oauth_token" ,
CreatedAt : & created ,
}
2020-09-24 06:16:05 +03:00
upstreamServer := httptest . NewServer ( http . HandlerFunc ( func ( w http . ResponseWriter , r * http . Request ) {
2020-07-28 21:42:09 +03:00
w . WriteHeader ( 200 )
} ) )
2020-09-24 06:16:05 +03:00
t . Cleanup ( upstreamServer . Close )
2020-07-28 21:42:09 +03:00
test , err := NewProcessCookieTestWithOptionsModifiers ( func ( opts * options . Options ) {
2021-04-03 19:06:30 +03:00
opts . Providers [ 0 ] . AllowedGroups = tt . allowedGroups
2021-09-17 14:08:18 +03:00
opts . UpstreamServers = options . UpstreamConfig {
Upstreams : [ ] options . Upstream {
2021-08-09 16:32:15 +03:00
{
ID : upstreamServer . URL ,
Path : "/" ,
URI : upstreamServer . URL ,
} ,
2020-07-28 21:42:09 +03:00
} ,
}
} )
if err != nil {
t . Fatal ( err )
}
test . req , _ = http . NewRequest ( "GET" , "/" , nil )
test . req . Header . Add ( "accept" , applicationJSON )
2020-09-24 06:16:05 +03:00
err = test . SaveSession ( session )
assert . NoError ( t , err )
2020-07-28 21:42:09 +03:00
test . proxy . ServeHTTP ( test . rw , test . req )
if tt . expectUnauthorized {
2021-02-10 22:34:19 +03:00
assert . Equal ( t , http . StatusForbidden , test . rw . Code )
2020-07-28 21:42:09 +03:00
} else {
assert . Equal ( t , http . StatusOK , test . rw . Code )
}
} )
}
}
func TestAuthOnlyAllowedGroups ( t * testing . T ) {
2020-10-19 04:14:32 +03:00
testCases := [ ] struct {
2020-07-28 21:42:09 +03:00
name string
allowedGroups [ ] string
groups [ ] string
2020-10-19 04:14:32 +03:00
querystring string
2020-11-18 06:03:41 +03:00
expectedStatusCode int
2020-07-28 21:42:09 +03:00
} {
2020-10-19 04:14:32 +03:00
{
name : "NoAllowedGroups" ,
allowedGroups : [ ] string { } ,
groups : [ ] string { } ,
querystring : "" ,
2020-11-18 06:03:41 +03:00
expectedStatusCode : http . StatusAccepted ,
2020-10-19 04:14:32 +03:00
} ,
{
name : "NoAllowedGroupsUserHasGroups" ,
allowedGroups : [ ] string { } ,
groups : [ ] string { "a" , "b" } ,
querystring : "" ,
2020-11-18 06:03:41 +03:00
expectedStatusCode : http . StatusAccepted ,
2020-10-19 04:14:32 +03:00
} ,
{
name : "UserInAllowedGroup" ,
allowedGroups : [ ] string { "a" } ,
groups : [ ] string { "a" , "b" } ,
querystring : "" ,
2020-11-18 06:03:41 +03:00
expectedStatusCode : http . StatusAccepted ,
2020-10-19 04:14:32 +03:00
} ,
{
name : "UserNotInAllowedGroup" ,
allowedGroups : [ ] string { "a" } ,
groups : [ ] string { "c" } ,
querystring : "" ,
2020-11-18 06:03:41 +03:00
expectedStatusCode : http . StatusUnauthorized ,
2020-10-19 04:14:32 +03:00
} ,
{
name : "UserInQuerystringGroup" ,
allowedGroups : [ ] string { "a" , "b" } ,
groups : [ ] string { "a" , "c" } ,
2020-11-27 20:07:21 +03:00
querystring : "?allowed_groups=a" ,
expectedStatusCode : http . StatusAccepted ,
} ,
{
name : "UserInMultiParamQuerystringGroup" ,
allowedGroups : [ ] string { "a" , "b" } ,
groups : [ ] string { "b" } ,
querystring : "?allowed_groups=a&allowed_groups=b,d" ,
2020-11-18 06:03:41 +03:00
expectedStatusCode : http . StatusAccepted ,
2020-10-19 04:14:32 +03:00
} ,
{
name : "UserInOnlyQuerystringGroup" ,
allowedGroups : [ ] string { } ,
groups : [ ] string { "a" , "c" } ,
querystring : "?allowed_groups=a,b" ,
2020-11-18 06:03:41 +03:00
expectedStatusCode : http . StatusAccepted ,
2020-10-19 04:14:32 +03:00
} ,
{
name : "UserInDelimitedQuerystringGroup" ,
allowedGroups : [ ] string { "a" , "b" , "c" } ,
groups : [ ] string { "c" } ,
querystring : "?allowed_groups=a,c" ,
2020-11-18 06:03:41 +03:00
expectedStatusCode : http . StatusAccepted ,
2020-10-19 04:14:32 +03:00
} ,
{
name : "UserNotInQuerystringGroup" ,
allowedGroups : [ ] string { } ,
groups : [ ] string { "c" } ,
2020-11-27 20:07:21 +03:00
querystring : "?allowed_groups=a,b" ,
2020-11-18 06:03:41 +03:00
expectedStatusCode : http . StatusForbidden ,
2020-10-19 04:14:32 +03:00
} ,
{
name : "UserInConfigGroupNotInQuerystringGroup" ,
allowedGroups : [ ] string { "a" , "b" , "c" } ,
groups : [ ] string { "c" } ,
2020-11-27 20:07:21 +03:00
querystring : "?allowed_groups=a,b" ,
2020-11-18 06:03:41 +03:00
expectedStatusCode : http . StatusForbidden ,
2020-10-19 04:14:32 +03:00
} ,
{
name : "UserInQuerystringGroupNotInConfigGroup" ,
allowedGroups : [ ] string { "a" , "b" } ,
groups : [ ] string { "c" } ,
querystring : "?allowed_groups=b,c" ,
2020-11-18 06:03:41 +03:00
expectedStatusCode : http . StatusUnauthorized ,
2020-10-19 04:14:32 +03:00
} ,
2020-07-28 21:42:09 +03:00
}
2020-10-19 04:14:32 +03:00
for _ , tc := range testCases {
t . Run ( tc . name , func ( t * testing . T ) {
2020-07-28 21:42:09 +03:00
emailAddress := "test"
created := time . Now ( )
session := & sessions . SessionState {
2020-10-19 04:14:32 +03:00
Groups : tc . groups ,
2020-07-28 21:42:09 +03:00
Email : emailAddress ,
AccessToken : "oauth_token" ,
CreatedAt : & created ,
}
2020-10-19 04:14:32 +03:00
test , err := NewAuthOnlyEndpointTest ( tc . querystring , func ( opts * options . Options ) {
2021-04-03 19:06:30 +03:00
opts . Providers [ 0 ] . AllowedGroups = tc . allowedGroups
2020-07-28 21:42:09 +03:00
} )
if err != nil {
t . Fatal ( err )
}
err = test . SaveSession ( session )
assert . NoError ( t , err )
test . proxy . ServeHTTP ( test . rw , test . req )
2020-11-18 06:03:41 +03:00
assert . Equal ( t , tc . expectedStatusCode , test . rw . Code )
2020-07-28 21:42:09 +03:00
} )
}
}
2022-02-08 23:40:40 +03:00
func TestAuthOnlyAllowedGroupsWithSkipMethods ( t * testing . T ) {
testCases := [ ] struct {
name string
groups [ ] string
method string
ip string
withSession bool
expectedStatusCode int
} {
{
name : "UserWithGroupSkipAuthPreflight" ,
groups : [ ] string { "a" , "c" } ,
method : "OPTIONS" ,
ip : "1.2.3.5:43670" ,
withSession : true ,
expectedStatusCode : http . StatusAccepted ,
} ,
{
name : "UserWithGroupTrustedIp" ,
groups : [ ] string { "a" , "c" } ,
method : "GET" ,
ip : "1.2.3.4:43670" ,
withSession : true ,
expectedStatusCode : http . StatusAccepted ,
} ,
{
name : "UserWithoutGroupSkipAuthPreflight" ,
groups : [ ] string { "c" } ,
method : "OPTIONS" ,
ip : "1.2.3.5:43670" ,
withSession : true ,
expectedStatusCode : http . StatusForbidden ,
} ,
{
name : "UserWithoutGroupTrustedIp" ,
groups : [ ] string { "c" } ,
method : "GET" ,
ip : "1.2.3.4:43670" ,
withSession : true ,
expectedStatusCode : http . StatusForbidden ,
} ,
{
name : "UserWithoutSessionSkipAuthPreflight" ,
method : "OPTIONS" ,
ip : "1.2.3.5:43670" ,
withSession : false ,
expectedStatusCode : http . StatusAccepted ,
} ,
{
name : "UserWithoutSessionTrustedIp" ,
method : "GET" ,
ip : "1.2.3.4:43670" ,
withSession : false ,
expectedStatusCode : http . StatusAccepted ,
} ,
}
for _ , tc := range testCases {
t . Run ( tc . name , func ( t * testing . T ) {
test , err := NewAuthOnlyEndpointTest ( "?allowed_groups=a,b" , func ( opts * options . Options ) {
opts . SkipAuthPreflight = true
opts . TrustedIPs = [ ] string { "1.2.3.4" }
} )
if err != nil {
t . Fatal ( err )
}
test . req . Method = tc . method
test . req . RemoteAddr = tc . ip
if tc . withSession {
created := time . Now ( )
session := & sessions . SessionState {
Groups : tc . groups ,
Email : "test" ,
AccessToken : "oauth_token" ,
CreatedAt : & created ,
}
err = test . SaveSession ( session )
}
assert . NoError ( t , err )
test . proxy . ServeHTTP ( test . rw , test . req )
assert . Equal ( t , tc . expectedStatusCode , test . rw . Code )
} )
}
}
2021-07-28 11:12:00 +03:00
func TestAuthOnlyAllowedEmailDomains ( t * testing . T ) {
testCases := [ ] struct {
name string
email string
querystring string
expectedStatusCode int
} {
{
name : "NotEmailRestriction" ,
email : "toto@example.com" ,
querystring : "" ,
expectedStatusCode : http . StatusAccepted ,
} ,
{
name : "UserInAllowedEmailDomain" ,
email : "toto@example.com" ,
querystring : "?allowed_email_domains=example.com" ,
expectedStatusCode : http . StatusAccepted ,
} ,
{
name : "UserNotInAllowedEmailDomain" ,
email : "toto@example.com" ,
querystring : "?allowed_email_domains=a.example.com" ,
expectedStatusCode : http . StatusForbidden ,
} ,
{
2022-04-24 04:11:38 +03:00
name : "UserNotInAllowedEmailDomains" ,
2021-07-28 11:12:00 +03:00
email : "toto@example.com" ,
querystring : "?allowed_email_domains=a.example.com,b.example.com" ,
expectedStatusCode : http . StatusForbidden ,
} ,
{
name : "UserInAllowedEmailDomains" ,
email : "toto@example.com" ,
querystring : "?allowed_email_domains=a.example.com,example.com" ,
expectedStatusCode : http . StatusAccepted ,
} ,
{
name : "UserInAllowedEmailDomainWildcard" ,
email : "toto@foo.example.com" ,
querystring : "?allowed_email_domains=*.example.com" ,
expectedStatusCode : http . StatusAccepted ,
} ,
{
name : "UserNotInAllowedEmailDomainWildcard" ,
email : "toto@example.com" ,
querystring : "?allowed_email_domains=*.a.example.com" ,
expectedStatusCode : http . StatusForbidden ,
} ,
{
name : "UserInAllowedEmailDomainsWildcard" ,
email : "toto@example.com" ,
querystring : "?allowed_email_domains=*.a.example.com,*.b.example.com" ,
expectedStatusCode : http . StatusForbidden ,
} ,
{
name : "UserInAllowedEmailDomainsWildcard" ,
email : "toto@c.example.com" ,
querystring : "?allowed_email_domains=a.b.c.example.com,*.c.example.com" ,
expectedStatusCode : http . StatusAccepted ,
} ,
}
for _ , tc := range testCases {
t . Run ( tc . name , func ( t * testing . T ) {
groups := [ ] string { }
created := time . Now ( )
session := & sessions . SessionState {
Groups : groups ,
Email : tc . email ,
AccessToken : "oauth_token" ,
CreatedAt : & created ,
}
test , err := NewAuthOnlyEndpointTest ( tc . querystring , func ( opts * options . Options ) { } )
if err != nil {
t . Fatal ( err )
}
err = test . SaveSession ( session )
assert . NoError ( t , err )
test . proxy . ServeHTTP ( test . rw , test . req )
assert . Equal ( t , tc . expectedStatusCode , test . rw . Code )
} )
}
}
2022-04-24 04:11:38 +03:00
func TestAuthOnlyAllowedEmails ( t * testing . T ) {
testCases := [ ] struct {
name string
email string
querystring string
expectedStatusCode int
} {
{
name : "NotEmailRestriction" ,
email : "toto@example.com" ,
querystring : "" ,
expectedStatusCode : http . StatusAccepted ,
} ,
{
name : "UserInAllowedEmail" ,
email : "toto@example.com" ,
querystring : "?allowed_emails=toto@example.com" ,
expectedStatusCode : http . StatusAccepted ,
} ,
{
name : "UserNotInAllowedEmail" ,
email : "toto@example.com" ,
querystring : "?allowed_emails=tete@example.com" ,
expectedStatusCode : http . StatusForbidden ,
} ,
{
name : "UserNotInAllowedEmails" ,
email : "toto@example.com" ,
querystring : "?allowed_emails=tete@example.com,tutu@example.com" ,
expectedStatusCode : http . StatusForbidden ,
} ,
{
name : "UserInAllowedEmails" ,
email : "toto@example.com" ,
querystring : "?allowed_emails=tete@example.com,toto@example.com" ,
expectedStatusCode : http . StatusAccepted ,
} ,
}
for _ , tc := range testCases {
t . Run ( tc . name , func ( t * testing . T ) {
groups := [ ] string { }
created := time . Now ( )
session := & sessions . SessionState {
Groups : groups ,
Email : tc . email ,
AccessToken : "oauth_token" ,
CreatedAt : & created ,
}
test , err := NewAuthOnlyEndpointTest ( tc . querystring , func ( opts * options . Options ) { } )
if err != nil {
t . Fatal ( err )
}
err = test . SaveSession ( session )
assert . NoError ( t , err )
test . proxy . ServeHTTP ( test . rw , test . req )
assert . Equal ( t , tc . expectedStatusCode , test . rw . Code )
} )
}
}