2018-11-14 10:18:03 +01:00
package auth
import (
"context"
"fmt"
2021-03-04 20:08:03 +01:00
"io"
2018-11-14 10:18:03 +01:00
"net/http"
"net/http/httptest"
"os"
"testing"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
2023-02-03 15:24:05 +01:00
"github.com/traefik/traefik/v3/pkg/config/dynamic"
"github.com/traefik/traefik/v3/pkg/testhelpers"
2018-11-14 10:18:03 +01:00
)
func TestBasicAuthFail ( t * testing . T ) {
next := http . HandlerFunc ( func ( w http . ResponseWriter , r * http . Request ) {
fmt . Fprintln ( w , "traefik" )
} )
2019-07-10 09:26:04 +02:00
auth := dynamic . BasicAuth {
2018-11-14 10:18:03 +01:00
Users : [ ] string { "test" } ,
}
_ , err := NewBasic ( context . Background ( ) , next , auth , "authName" )
require . Error ( t , err )
2019-07-10 09:26:04 +02:00
auth2 := dynamic . BasicAuth {
2018-11-14 10:18:03 +01:00
Users : [ ] string { "test:test" } ,
}
authMiddleware , err := NewBasic ( context . Background ( ) , next , auth2 , "authTest" )
require . NoError ( t , err )
ts := httptest . NewServer ( authMiddleware )
defer ts . Close ( )
req := testhelpers . MustNewRequest ( http . MethodGet , ts . URL , nil )
req . SetBasicAuth ( "test" , "test" )
res , err := http . DefaultClient . Do ( req )
require . NoError ( t , err )
assert . Equal ( t , http . StatusUnauthorized , res . StatusCode , "they should be equal" )
}
func TestBasicAuthSuccess ( t * testing . T ) {
next := http . HandlerFunc ( func ( w http . ResponseWriter , r * http . Request ) {
fmt . Fprintln ( w , "traefik" )
} )
2019-07-10 09:26:04 +02:00
auth := dynamic . BasicAuth {
2018-11-14 10:18:03 +01:00
Users : [ ] string { "test:$apr1$H6uskkkW$IgXLP6ewTrSuBkTrqE8wj/" } ,
}
authMiddleware , err := NewBasic ( context . Background ( ) , next , auth , "authName" )
require . NoError ( t , err )
ts := httptest . NewServer ( authMiddleware )
defer ts . Close ( )
req := testhelpers . MustNewRequest ( http . MethodGet , ts . URL , nil )
req . SetBasicAuth ( "test" , "test" )
res , err := http . DefaultClient . Do ( req )
require . NoError ( t , err )
assert . Equal ( t , http . StatusOK , res . StatusCode , "they should be equal" )
2021-03-04 20:08:03 +01:00
body , err := io . ReadAll ( res . Body )
2018-11-14 10:18:03 +01:00
require . NoError ( t , err )
defer res . Body . Close ( )
assert . Equal ( t , "traefik\n" , string ( body ) , "they should be equal" )
}
func TestBasicAuthUserHeader ( t * testing . T ) {
next := http . HandlerFunc ( func ( w http . ResponseWriter , r * http . Request ) {
assert . Equal ( t , "test" , r . Header [ "X-Webauth-User" ] [ 0 ] , "auth user should be set" )
fmt . Fprintln ( w , "traefik" )
} )
2019-07-10 09:26:04 +02:00
auth := dynamic . BasicAuth {
2018-11-14 10:18:03 +01:00
Users : [ ] string { "test:$apr1$H6uskkkW$IgXLP6ewTrSuBkTrqE8wj/" } ,
HeaderField : "X-Webauth-User" ,
}
middleware , err := NewBasic ( context . Background ( ) , next , auth , "authName" )
require . NoError ( t , err )
ts := httptest . NewServer ( middleware )
defer ts . Close ( )
req := testhelpers . MustNewRequest ( http . MethodGet , ts . URL , nil )
req . SetBasicAuth ( "test" , "test" )
res , err := http . DefaultClient . Do ( req )
require . NoError ( t , err )
assert . Equal ( t , http . StatusOK , res . StatusCode )
2021-03-04 20:08:03 +01:00
body , err := io . ReadAll ( res . Body )
2018-11-14 10:18:03 +01:00
require . NoError ( t , err )
defer res . Body . Close ( )
assert . Equal ( t , "traefik\n" , string ( body ) )
}
func TestBasicAuthHeaderRemoved ( t * testing . T ) {
next := http . HandlerFunc ( func ( w http . ResponseWriter , r * http . Request ) {
assert . Empty ( t , r . Header . Get ( authorizationHeader ) )
fmt . Fprintln ( w , "traefik" )
} )
2019-07-10 09:26:04 +02:00
auth := dynamic . BasicAuth {
2018-11-14 10:18:03 +01:00
RemoveHeader : true ,
Users : [ ] string { "test:$apr1$H6uskkkW$IgXLP6ewTrSuBkTrqE8wj/" } ,
}
middleware , err := NewBasic ( context . Background ( ) , next , auth , "authName" )
require . NoError ( t , err )
ts := httptest . NewServer ( middleware )
defer ts . Close ( )
req := testhelpers . MustNewRequest ( http . MethodGet , ts . URL , nil )
req . SetBasicAuth ( "test" , "test" )
res , err := http . DefaultClient . Do ( req )
require . NoError ( t , err )
assert . Equal ( t , http . StatusOK , res . StatusCode )
2021-03-04 20:08:03 +01:00
body , err := io . ReadAll ( res . Body )
2018-11-14 10:18:03 +01:00
require . NoError ( t , err )
err = res . Body . Close ( )
require . NoError ( t , err )
assert . Equal ( t , "traefik\n" , string ( body ) )
}
func TestBasicAuthHeaderPresent ( t * testing . T ) {
next := http . HandlerFunc ( func ( w http . ResponseWriter , r * http . Request ) {
assert . NotEmpty ( t , r . Header . Get ( authorizationHeader ) )
fmt . Fprintln ( w , "traefik" )
} )
2019-07-10 09:26:04 +02:00
auth := dynamic . BasicAuth {
2018-11-14 10:18:03 +01:00
Users : [ ] string { "test:$apr1$H6uskkkW$IgXLP6ewTrSuBkTrqE8wj/" } ,
}
middleware , err := NewBasic ( context . Background ( ) , next , auth , "authName" )
require . NoError ( t , err )
ts := httptest . NewServer ( middleware )
defer ts . Close ( )
req := testhelpers . MustNewRequest ( http . MethodGet , ts . URL , nil )
req . SetBasicAuth ( "test" , "test" )
res , err := http . DefaultClient . Do ( req )
require . NoError ( t , err )
assert . Equal ( t , http . StatusOK , res . StatusCode )
2021-03-04 20:08:03 +01:00
body , err := io . ReadAll ( res . Body )
2018-11-14 10:18:03 +01:00
require . NoError ( t , err )
err = res . Body . Close ( )
require . NoError ( t , err )
assert . Equal ( t , "traefik\n" , string ( body ) )
}
func TestBasicAuthUsersFromFile ( t * testing . T ) {
testCases := [ ] struct {
desc string
userFileContent string
expectedUsers map [ string ] string
givenUsers [ ] string
realm string
} {
{
desc : "Finds the users in the file" ,
userFileContent : "test:$apr1$H6uskkkW$IgXLP6ewTrSuBkTrqE8wj/\ntest2:$apr1$d9hr9HBB$4HxwgUir3HP4EsggP/QNo0\n" ,
givenUsers : [ ] string { } ,
expectedUsers : map [ string ] string { "test" : "test" , "test2" : "test2" } ,
} ,
{
desc : "Merges given users with users from the file" ,
userFileContent : "test:$apr1$H6uskkkW$IgXLP6ewTrSuBkTrqE8wj/\n" ,
givenUsers : [ ] string { "test2:$apr1$d9hr9HBB$4HxwgUir3HP4EsggP/QNo0" , "test3:$apr1$3rJbDP0q$RfzJiorTk78jQ1EcKqWso0" } ,
expectedUsers : map [ string ] string { "test" : "test" , "test2" : "test2" , "test3" : "test3" } ,
} ,
{
desc : "Given users have priority over users in the file" ,
userFileContent : "test:$apr1$H6uskkkW$IgXLP6ewTrSuBkTrqE8wj/\ntest2:$apr1$d9hr9HBB$4HxwgUir3HP4EsggP/QNo0\n" ,
givenUsers : [ ] string { "test2:$apr1$mK.GtItK$ncnLYvNLek0weXdxo68690" } ,
expectedUsers : map [ string ] string { "test" : "test" , "test2" : "overridden" } ,
} ,
{
desc : "Should authenticate the correct user based on the realm" ,
userFileContent : "test:$apr1$H6uskkkW$IgXLP6ewTrSuBkTrqE8wj/\ntest2:$apr1$d9hr9HBB$4HxwgUir3HP4EsggP/QNo0\n" ,
givenUsers : [ ] string { "test2:$apr1$mK.GtItK$ncnLYvNLek0weXdxo68690" } ,
expectedUsers : map [ string ] string { "test" : "test" , "test2" : "overridden" } ,
2018-11-19 16:40:03 +01:00
realm : "traefik" ,
} ,
{
desc : "Should skip comments" ,
userFileContent : "#Comment\ntest:$apr1$H6uskkkW$IgXLP6ewTrSuBkTrqE8wj/\ntest2:$apr1$d9hr9HBB$4HxwgUir3HP4EsggP/QNo0\n" ,
givenUsers : [ ] string { } ,
expectedUsers : map [ string ] string { "test" : "test" , "test2" : "test2" } ,
realm : "traefiker" ,
2018-11-14 10:18:03 +01:00
} ,
}
for _ , test := range testCases {
t . Run ( test . desc , func ( t * testing . T ) {
t . Parallel ( )
// Creates the temporary configuration file with the users
2021-03-04 20:08:03 +01:00
usersFile , err := os . CreateTemp ( t . TempDir ( ) , "auth-users" )
2018-11-14 10:18:03 +01:00
require . NoError ( t , err )
2023-06-05 10:24:06 +02:00
_ , err = usersFile . WriteString ( test . userFileContent )
2018-11-14 10:18:03 +01:00
require . NoError ( t , err )
// Creates the configuration for our Authenticator
2019-07-10 09:26:04 +02:00
authenticatorConfiguration := dynamic . BasicAuth {
2018-11-14 10:18:03 +01:00
Users : test . givenUsers ,
UsersFile : usersFile . Name ( ) ,
Realm : test . realm ,
}
next := http . HandlerFunc ( func ( w http . ResponseWriter , r * http . Request ) {
fmt . Fprintln ( w , "traefik" )
} )
authenticator , err := NewBasic ( context . Background ( ) , next , authenticatorConfiguration , "authName" )
require . NoError ( t , err )
ts := httptest . NewServer ( authenticator )
defer ts . Close ( )
for userName , userPwd := range test . expectedUsers {
req := testhelpers . MustNewRequest ( http . MethodGet , ts . URL , nil )
req . SetBasicAuth ( userName , userPwd )
var res * http . Response
res , err = http . DefaultClient . Do ( req )
require . NoError ( t , err )
require . Equal ( t , http . StatusOK , res . StatusCode , "Cannot authenticate user " + userName )
var body [ ] byte
2021-03-04 20:08:03 +01:00
body , err = io . ReadAll ( res . Body )
2018-11-14 10:18:03 +01:00
require . NoError ( t , err )
err = res . Body . Close ( )
require . NoError ( t , err )
require . Equal ( t , "traefik\n" , string ( body ) )
}
// Checks that user foo doesn't work
req := testhelpers . MustNewRequest ( http . MethodGet , ts . URL , nil )
req . SetBasicAuth ( "foo" , "foo" )
res , err := http . DefaultClient . Do ( req )
require . NoError ( t , err )
require . Equal ( t , http . StatusUnauthorized , res . StatusCode )
if len ( test . realm ) > 0 {
require . Equal ( t , ` Basic realm=" ` + test . realm + ` " ` , res . Header . Get ( "WWW-Authenticate" ) )
}
2021-03-04 20:08:03 +01:00
body , err := io . ReadAll ( res . Body )
2018-11-14 10:18:03 +01:00
require . NoError ( t , err )
err = res . Body . Close ( )
require . NoError ( t , err )
require . NotContains ( t , "traefik" , string ( body ) )
} )
}
}