2019-11-02 21:32:58 +07:00
package suites
import (
2019-11-25 03:27:59 +07:00
"context"
"fmt"
2021-12-01 20:14:15 +07:00
"io"
2019-11-25 03:27:59 +07:00
"log"
"net/http"
2020-01-18 21:57:42 +07:00
"net/url"
2019-11-02 21:32:58 +07:00
"testing"
2019-11-25 03:27:59 +07:00
"time"
2019-11-02 21:32:58 +07:00
2019-12-07 23:40:42 +07:00
"github.com/stretchr/testify/require"
2019-11-02 21:32:58 +07:00
"github.com/stretchr/testify/suite"
2020-04-05 19:37:21 +07:00
2021-08-11 08:04:35 +07:00
"github.com/authelia/authelia/v4/internal/storage"
"github.com/authelia/authelia/v4/internal/utils"
2019-11-02 21:32:58 +07:00
)
2019-11-25 03:27:59 +07:00
type StandaloneWebDriverSuite struct {
2021-11-05 20:14:42 +07:00
* RodSuite
2019-11-02 21:32:58 +07:00
}
2019-11-25 03:27:59 +07:00
func NewStandaloneWebDriverSuite ( ) * StandaloneWebDriverSuite {
2021-11-05 20:14:42 +07:00
return & StandaloneWebDriverSuite { RodSuite : new ( RodSuite ) }
2019-11-25 03:27:59 +07:00
}
func ( s * StandaloneWebDriverSuite ) SetupSuite ( ) {
2021-11-05 20:14:42 +07:00
browser , err := StartRod ( )
2019-11-25 03:27:59 +07:00
if err != nil {
log . Fatal ( err )
}
2021-11-05 20:14:42 +07:00
s . RodSession = browser
2019-11-25 03:27:59 +07:00
}
func ( s * StandaloneWebDriverSuite ) TearDownSuite ( ) {
2021-11-05 20:14:42 +07:00
err := s . RodSession . Stop ( )
2019-11-25 03:27:59 +07:00
if err != nil {
log . Fatal ( err )
}
}
func ( s * StandaloneWebDriverSuite ) SetupTest ( ) {
2021-11-05 20:14:42 +07:00
s . Page = s . doCreateTab ( s . T ( ) , HomeBaseURL )
s . verifyIsHome ( s . T ( ) , s . Page )
}
2019-11-25 03:27:59 +07:00
2021-11-05 20:14:42 +07:00
func ( s * StandaloneWebDriverSuite ) TearDownTest ( ) {
s . collectCoverage ( s . Page )
s . MustClose ( )
2019-11-25 03:27:59 +07:00
}
func ( s * StandaloneWebDriverSuite ) TestShouldLetUserKnowHeIsAlreadyAuthenticated ( ) {
ctx , cancel := context . WithTimeout ( context . Background ( ) , 20 * time . Second )
2021-11-05 20:14:42 +07:00
defer func ( ) {
cancel ( )
s . collectScreenshot ( ctx . Err ( ) , s . Page )
} ( )
2019-11-25 03:27:59 +07:00
2021-11-05 20:14:42 +07:00
_ = s . doRegisterAndLogin2FA ( s . T ( ) , s . Context ( ctx ) , "john" , "password" , false , "" )
2019-11-25 03:27:59 +07:00
2020-05-02 12:06:39 +07:00
// Visit home page to change context.
2021-11-05 20:14:42 +07:00
s . doVisit ( s . T ( ) , s . Context ( ctx ) , HomeBaseURL )
s . verifyIsHome ( s . T ( ) , s . Context ( ctx ) )
2019-11-25 03:27:59 +07:00
2020-05-02 12:06:39 +07:00
// Visit the login page and wait for redirection to 2FA page with success icon displayed.
2021-11-05 20:14:42 +07:00
s . doVisit ( s . T ( ) , s . Context ( ctx ) , GetLoginBaseURL ( ) )
s . verifyIsAuthenticatedPage ( s . T ( ) , s . Context ( ctx ) )
2019-11-25 03:27:59 +07:00
}
2022-06-19 19:43:19 +07:00
func ( s * StandaloneWebDriverSuite ) TestShouldRedirectAfterOneFactorOnAnotherTab ( ) {
ctx , cancel := context . WithTimeout ( context . Background ( ) , 10 * time . Second )
targetURL := fmt . Sprintf ( "%s/secret.html" , SingleFactorBaseURL )
page2 := s . Browser ( ) . MustPage ( targetURL )
defer func ( ) {
cancel ( )
s . collectScreenshot ( ctx . Err ( ) , s . Page )
s . collectScreenshot ( ctx . Err ( ) , page2 )
page2 . MustClose ( )
} ( )
// Open second tab with secret page.
page2 . MustWaitLoad ( )
// Switch to first, visit the login page and wait for redirection to secret page with secret displayed.
s . Page . MustActivate ( )
s . doLoginOneFactor ( s . T ( ) , s . Context ( ctx ) , "john" , "password" , false , targetURL )
s . verifySecretAuthorized ( s . T ( ) , s . Page )
// Switch to second tab and wait for redirection to secret page with secret displayed.
page2 . MustActivate ( )
s . verifySecretAuthorized ( s . T ( ) , page2 . Context ( ctx ) )
}
2021-08-02 13:15:38 +07:00
func ( s * StandaloneWebDriverSuite ) TestShouldRedirectAlreadyAuthenticatedUser ( ) {
ctx , cancel := context . WithTimeout ( context . Background ( ) , 20 * time . Second )
2021-11-05 20:14:42 +07:00
defer func ( ) {
cancel ( )
s . collectScreenshot ( ctx . Err ( ) , s . Page )
} ( )
2021-08-02 13:15:38 +07:00
2021-11-05 20:14:42 +07:00
_ = s . doRegisterAndLogin2FA ( s . T ( ) , s . Context ( ctx ) , "john" , "password" , false , "" )
2021-08-02 13:15:38 +07:00
// Visit home page to change context.
2021-11-05 20:14:42 +07:00
s . doVisit ( s . T ( ) , s . Context ( ctx ) , HomeBaseURL )
s . verifyIsHome ( s . T ( ) , s . Context ( ctx ) )
2021-08-02 13:15:38 +07:00
// Visit the login page and wait for redirection to 2FA page with success icon displayed.
2021-11-05 20:14:42 +07:00
s . doVisit ( s . T ( ) , s . Context ( ctx ) , fmt . Sprintf ( "%s?rd=https://secure.example.com:8080" , GetLoginBaseURL ( ) ) )
_ , err := s . Page . ElementR ( "h1" , "Public resource" )
require . NoError ( s . T ( ) , err )
s . verifyURLIs ( s . T ( ) , s . Context ( ctx ) , "https://secure.example.com:8080/" )
2021-08-02 13:15:38 +07:00
}
func ( s * StandaloneWebDriverSuite ) TestShouldNotRedirectAlreadyAuthenticatedUserToUnsafeURL ( ) {
ctx , cancel := context . WithTimeout ( context . Background ( ) , 20 * time . Second )
2021-11-05 20:14:42 +07:00
defer func ( ) {
cancel ( )
s . collectScreenshot ( ctx . Err ( ) , s . Page )
} ( )
2021-08-02 13:15:38 +07:00
2021-11-05 20:14:42 +07:00
_ = s . doRegisterAndLogin2FA ( s . T ( ) , s . Context ( ctx ) , "john" , "password" , false , "" )
2021-08-02 13:15:38 +07:00
// Visit home page to change context.
2021-11-05 20:14:42 +07:00
s . doVisit ( s . T ( ) , s . Context ( ctx ) , HomeBaseURL )
s . verifyIsHome ( s . T ( ) , s . Context ( ctx ) )
2021-08-02 13:15:38 +07:00
// Visit the login page and wait for redirection to 2FA page with success icon displayed.
2021-11-05 20:14:42 +07:00
s . doVisit ( s . T ( ) , s . Context ( ctx ) , fmt . Sprintf ( "%s?rd=https://secure.example.local:8080" , GetLoginBaseURL ( ) ) )
s . verifyNotificationDisplayed ( s . T ( ) , s . Context ( ctx ) , "Redirection was determined to be unsafe and aborted. Ensure the redirection URL is correct." )
2021-08-02 13:15:38 +07:00
}
2019-12-07 23:40:42 +07:00
func ( s * StandaloneWebDriverSuite ) TestShouldCheckUserIsAskedToRegisterDevice ( ) {
2021-11-05 20:14:42 +07:00
ctx , cancel := context . WithTimeout ( context . Background ( ) , 30 * time . Second )
defer func ( ) {
cancel ( )
s . collectScreenshot ( ctx . Err ( ) , s . Page )
} ( )
2019-12-07 23:40:42 +07:00
username := "john"
password := "password"
2020-05-02 12:06:39 +07:00
// Clean up any TOTP secret already in DB.
2021-12-01 19:11:29 +07:00
provider := storage . NewSQLiteProvider ( & storageLocalTmpConfig )
2021-11-23 16:45:38 +07:00
require . NoError ( s . T ( ) , provider . DeleteTOTPConfiguration ( ctx , username ) )
2019-12-07 23:40:42 +07:00
2020-05-02 12:06:39 +07:00
// Login one factor.
2021-11-05 20:14:42 +07:00
s . doLoginOneFactor ( s . T ( ) , s . Context ( ctx ) , username , password , false , "" )
2019-12-07 23:40:42 +07:00
2020-05-02 12:06:39 +07:00
// Check the user is asked to register a new device.
2021-11-05 20:14:42 +07:00
s . WaitElementLocatedByClassName ( s . T ( ) , s . Context ( ctx ) , "state-not-registered" )
2019-12-07 23:40:42 +07:00
2020-05-02 12:06:39 +07:00
// Then register the TOTP factor.
2021-11-05 20:14:42 +07:00
s . doRegisterTOTP ( s . T ( ) , s . Context ( ctx ) )
2020-05-02 12:06:39 +07:00
// And logout.
2021-11-05 20:14:42 +07:00
s . doLogout ( s . T ( ) , s . Context ( ctx ) )
2019-12-07 23:40:42 +07:00
2020-05-02 12:06:39 +07:00
// Login one factor again.
2021-11-05 20:14:42 +07:00
s . doLoginOneFactor ( s . T ( ) , s . Context ( ctx ) , username , password , false , "" )
2019-12-07 23:40:42 +07:00
2022-01-31 12:25:15 +07:00
// now the user should be asked to perform 2FA.
2021-11-05 20:14:42 +07:00
s . WaitElementLocatedByClassName ( s . T ( ) , s . Context ( ctx ) , "state-method" )
2019-12-07 23:40:42 +07:00
}
2019-11-25 03:27:59 +07:00
type StandaloneSuite struct {
suite . Suite
}
2019-11-02 21:32:58 +07:00
func NewStandaloneSuite ( ) * StandaloneSuite {
2019-11-25 03:27:59 +07:00
return & StandaloneSuite { }
}
2021-03-05 11:18:31 +07:00
func ( s * StandaloneSuite ) TestShouldRespectMethodsACL ( ) {
req , err := http . NewRequest ( "GET" , fmt . Sprintf ( "%s/api/verify?rd=%s" , AutheliaBaseURL , GetLoginBaseURL ( ) ) , nil )
s . Assert ( ) . NoError ( err )
req . Header . Set ( "X-Forwarded-Method" , "GET" )
req . Header . Set ( "X-Forwarded-Proto" , "https" )
req . Header . Set ( "X-Forwarded-Host" , fmt . Sprintf ( "secure.%s" , BaseDomain ) )
req . Header . Set ( "X-Forwarded-URI" , "/" )
2021-07-22 10:52:37 +07:00
req . Header . Set ( "Accept" , "text/html; charset=utf8" )
2021-03-05 11:18:31 +07:00
client := NewHTTPClient ( )
res , err := client . Do ( req )
s . Assert ( ) . NoError ( err )
s . Assert ( ) . Equal ( res . StatusCode , 302 )
2021-12-01 20:14:15 +07:00
body , err := io . ReadAll ( res . Body )
2021-03-05 11:18:31 +07:00
s . Assert ( ) . NoError ( err )
urlEncodedAdminURL := url . QueryEscape ( SecureBaseURL + "/" )
2022-07-08 19:18:52 +07:00
s . Assert ( ) . Equal ( fmt . Sprintf ( "<a href=\"%s\">302 Found</a>" , utils . StringHTMLEscape ( fmt . Sprintf ( "%s/?rd=%s&rm=GET" , GetLoginBaseURL ( ) , urlEncodedAdminURL ) ) ) , string ( body ) )
2021-03-05 11:18:31 +07:00
req . Header . Set ( "X-Forwarded-Method" , "OPTIONS" )
res , err = client . Do ( req )
s . Assert ( ) . NoError ( err )
s . Assert ( ) . Equal ( res . StatusCode , 200 )
}
2021-07-16 10:43:48 +07:00
func ( s * StandaloneSuite ) TestShouldRespondWithCorrectStatusCode ( ) {
req , err := http . NewRequest ( "GET" , fmt . Sprintf ( "%s/api/verify?rd=%s" , AutheliaBaseURL , GetLoginBaseURL ( ) ) , nil )
s . Assert ( ) . NoError ( err )
req . Header . Set ( "X-Forwarded-Method" , "GET" )
req . Header . Set ( "X-Forwarded-Proto" , "https" )
req . Header . Set ( "X-Forwarded-Host" , fmt . Sprintf ( "secure.%s" , BaseDomain ) )
req . Header . Set ( "X-Forwarded-URI" , "/" )
2021-07-22 10:52:37 +07:00
req . Header . Set ( "Accept" , "text/html; charset=utf8" )
2021-07-16 10:43:48 +07:00
client := NewHTTPClient ( )
res , err := client . Do ( req )
s . Assert ( ) . NoError ( err )
s . Assert ( ) . Equal ( res . StatusCode , 302 )
2021-12-01 20:14:15 +07:00
body , err := io . ReadAll ( res . Body )
2021-07-16 10:43:48 +07:00
s . Assert ( ) . NoError ( err )
urlEncodedAdminURL := url . QueryEscape ( SecureBaseURL + "/" )
2022-07-08 19:18:52 +07:00
s . Assert ( ) . Equal ( fmt . Sprintf ( "<a href=\"%s\">302 Found</a>" , utils . StringHTMLEscape ( fmt . Sprintf ( "%s/?rd=%s&rm=GET" , GetLoginBaseURL ( ) , urlEncodedAdminURL ) ) ) , string ( body ) )
2021-07-16 10:43:48 +07:00
req . Header . Set ( "X-Forwarded-Method" , "POST" )
res , err = client . Do ( req )
s . Assert ( ) . NoError ( err )
s . Assert ( ) . Equal ( res . StatusCode , 303 )
2021-12-01 20:14:15 +07:00
body , err = io . ReadAll ( res . Body )
2021-07-16 10:43:48 +07:00
s . Assert ( ) . NoError ( err )
urlEncodedAdminURL = url . QueryEscape ( SecureBaseURL + "/" )
2022-07-08 19:18:52 +07:00
s . Assert ( ) . Equal ( fmt . Sprintf ( "<a href=\"%s\">303 See Other</a>" , utils . StringHTMLEscape ( fmt . Sprintf ( "%s/?rd=%s&rm=POST" , GetLoginBaseURL ( ) , urlEncodedAdminURL ) ) ) , string ( body ) )
2021-07-16 10:43:48 +07:00
}
2020-05-02 12:06:39 +07:00
// Standard case using nginx.
2021-07-22 10:52:37 +07:00
func ( s * StandaloneSuite ) TestShouldVerifyAPIVerifyUnauthorized ( ) {
2019-11-25 03:27:59 +07:00
req , err := http . NewRequest ( "GET" , fmt . Sprintf ( "%s/api/verify" , AutheliaBaseURL ) , nil )
s . Assert ( ) . NoError ( err )
req . Header . Set ( "X-Forwarded-Proto" , "https" )
req . Header . Set ( "X-Original-URL" , AdminBaseURL )
2021-07-22 10:52:37 +07:00
req . Header . Set ( "Accept" , "text/html; charset=utf8" )
2019-11-25 03:27:59 +07:00
client := NewHTTPClient ( )
res , err := client . Do ( req )
s . Assert ( ) . NoError ( err )
s . Assert ( ) . Equal ( res . StatusCode , 401 )
2021-12-01 20:14:15 +07:00
body , err := io . ReadAll ( res . Body )
2019-11-25 03:27:59 +07:00
s . Assert ( ) . NoError ( err )
2022-07-08 19:18:52 +07:00
s . Assert ( ) . Equal ( "401 Unauthorized" , string ( body ) )
2019-11-25 03:27:59 +07:00
}
2020-05-02 12:06:39 +07:00
// Standard case using Kubernetes.
2019-11-25 03:27:59 +07:00
func ( s * StandaloneSuite ) TestShouldVerifyAPIVerifyRedirectFromXOriginalURL ( ) {
2020-05-27 18:55:44 +07:00
req , err := http . NewRequest ( "GET" , fmt . Sprintf ( "%s/api/verify?rd=%s" , AutheliaBaseURL , GetLoginBaseURL ( ) ) , nil )
2019-11-25 03:27:59 +07:00
s . Assert ( ) . NoError ( err )
req . Header . Set ( "X-Forwarded-Proto" , "https" )
req . Header . Set ( "X-Original-URL" , AdminBaseURL )
2021-07-22 10:52:37 +07:00
req . Header . Set ( "Accept" , "text/html; charset=utf8" )
2019-11-25 03:27:59 +07:00
client := NewHTTPClient ( )
res , err := client . Do ( req )
s . Assert ( ) . NoError ( err )
s . Assert ( ) . Equal ( res . StatusCode , 302 )
2021-12-01 20:14:15 +07:00
body , err := io . ReadAll ( res . Body )
2019-11-25 03:27:59 +07:00
s . Assert ( ) . NoError ( err )
2020-01-18 21:57:42 +07:00
urlEncodedAdminURL := url . QueryEscape ( AdminBaseURL )
2022-07-08 19:18:52 +07:00
s . Assert ( ) . Equal ( fmt . Sprintf ( "<a href=\"%s\">302 Found</a>" , utils . StringHTMLEscape ( fmt . Sprintf ( "%s/?rd=%s" , GetLoginBaseURL ( ) , urlEncodedAdminURL ) ) ) , string ( body ) )
2019-11-25 03:27:59 +07:00
}
func ( s * StandaloneSuite ) TestShouldVerifyAPIVerifyRedirectFromXOriginalHostURI ( ) {
2020-05-27 18:55:44 +07:00
req , err := http . NewRequest ( "GET" , fmt . Sprintf ( "%s/api/verify?rd=%s" , AutheliaBaseURL , GetLoginBaseURL ( ) ) , nil )
2019-11-25 03:27:59 +07:00
s . Assert ( ) . NoError ( err )
req . Header . Set ( "X-Forwarded-Proto" , "https" )
req . Header . Set ( "X-Forwarded-Host" , "secure.example.com:8080" )
req . Header . Set ( "X-Forwarded-URI" , "/" )
2021-07-22 10:52:37 +07:00
req . Header . Set ( "Accept" , "text/html; charset=utf8" )
2019-11-25 03:27:59 +07:00
client := NewHTTPClient ( )
res , err := client . Do ( req )
s . Assert ( ) . NoError ( err )
s . Assert ( ) . Equal ( res . StatusCode , 302 )
2021-12-01 20:14:15 +07:00
body , err := io . ReadAll ( res . Body )
2019-11-25 03:27:59 +07:00
s . Assert ( ) . NoError ( err )
2020-01-18 21:57:42 +07:00
urlEncodedAdminURL := url . QueryEscape ( SecureBaseURL + "/" )
2022-07-08 19:18:52 +07:00
s . Assert ( ) . Equal ( fmt . Sprintf ( "<a href=\"%s\">302 Found</a>" , utils . StringHTMLEscape ( fmt . Sprintf ( "%s/?rd=%s" , GetLoginBaseURL ( ) , urlEncodedAdminURL ) ) ) , string ( body ) )
2019-11-25 03:27:59 +07:00
}
2022-06-14 14:20:13 +07:00
func ( s * StandaloneSuite ) TestShouldRecordMetrics ( ) {
client := NewHTTPClient ( )
req , err := http . NewRequest ( "GET" , fmt . Sprintf ( "%s/api/health" , LoginBaseURL ) , nil )
s . Require ( ) . NoError ( err )
res , err := client . Do ( req )
s . Require ( ) . NoError ( err )
s . Assert ( ) . Equal ( res . StatusCode , 200 )
req , err = http . NewRequest ( "GET" , fmt . Sprintf ( "%s/metrics" , LoginBaseURL ) , nil )
s . Require ( ) . NoError ( err )
res , err = client . Do ( req )
s . Require ( ) . NoError ( err )
s . Assert ( ) . Equal ( res . StatusCode , 200 )
body , err := io . ReadAll ( res . Body )
s . Require ( ) . NoError ( err )
metrics := string ( body )
2022-06-14 18:51:33 +07:00
s . Assert ( ) . Contains ( metrics , "authelia_request_duration_bucket{" )
s . Assert ( ) . Contains ( metrics , "authelia_request_duration_sum{" )
2022-06-14 14:20:13 +07:00
}
2019-12-06 04:35:03 +07:00
func ( s * StandaloneSuite ) TestStandaloneWebDriverScenario ( ) {
suite . Run ( s . T ( ) , NewStandaloneWebDriverSuite ( ) )
}
2022-05-04 08:01:36 +07:00
func ( s * StandaloneSuite ) Test1FAScenario ( ) {
suite . Run ( s . T ( ) , New1FAScenario ( ) )
2019-12-06 04:35:03 +07:00
}
2022-05-04 08:01:36 +07:00
func ( s * StandaloneSuite ) Test2FAScenario ( ) {
suite . Run ( s . T ( ) , New2FAScenario ( ) )
2019-12-06 04:35:03 +07:00
}
func ( s * StandaloneSuite ) TestBypassPolicyScenario ( ) {
suite . Run ( s . T ( ) , NewBypassPolicyScenario ( ) )
}
func ( s * StandaloneSuite ) TestBackendProtectionScenario ( ) {
suite . Run ( s . T ( ) , NewBackendProtectionScenario ( ) )
}
func ( s * StandaloneSuite ) TestResetPasswordScenario ( ) {
suite . Run ( s . T ( ) , NewResetPasswordScenario ( ) )
}
func ( s * StandaloneSuite ) TestAvailableMethodsScenario ( ) {
2022-03-03 18:20:43 +07:00
suite . Run ( s . T ( ) , NewAvailableMethodsScenario ( [ ] string { "TIME-BASED ONE-TIME PASSWORD" , "SECURITY KEY - WEBAUTHN" } ) )
2019-11-02 21:32:58 +07:00
}
2020-01-18 21:57:42 +07:00
func ( s * StandaloneSuite ) TestRedirectionURLScenario ( ) {
suite . Run ( s . T ( ) , NewRedirectionURLScenario ( ) )
}
2021-04-13 15:38:12 +07:00
func ( s * StandaloneSuite ) TestRedirectionCheckScenario ( ) {
suite . Run ( s . T ( ) , NewRedirectionCheckScenario ( ) )
}
2019-11-02 21:32:58 +07:00
func TestStandaloneSuite ( t * testing . T ) {
2021-03-14 14:08:26 +07:00
if testing . Short ( ) {
t . Skip ( "skipping suite test in short mode" )
}
2019-11-25 03:27:59 +07:00
suite . Run ( t , NewStandaloneSuite ( ) )
2019-11-02 21:32:58 +07:00
}