refactor(suites): replace selenium with go-rod (#2534)
* refactor(suites): replace selenium with go-rod This change replaces [tebeka/selenium](https://github.com/tebeka/selenium) with [go-rod](https://github.com/go-rod/rod). We no longer have a chromedriver/external driver dependency to utilise Selenium as we instead utilise the Chrome Dev Protocol to communicate with the browser. Rod [documents](https://go-rod.github.io/#/why-rod) benefits of choosing the library as opposed to the available alternatives.pull/2569/head
parent
0e8ff3bde9
commit
83488d52a6
2
go.mod
2
go.mod
|
@ -13,6 +13,7 @@ require (
|
|||
github.com/fasthttp/router v1.4.4
|
||||
github.com/fasthttp/session/v2 v2.4.4
|
||||
github.com/go-ldap/ldap/v3 v3.4.1
|
||||
github.com/go-rod/rod v0.101.8
|
||||
github.com/go-sql-driver/mysql v1.6.0
|
||||
github.com/golang-jwt/jwt/v4 v4.1.0
|
||||
github.com/golang/mock v1.6.0
|
||||
|
@ -30,7 +31,6 @@ require (
|
|||
github.com/sirupsen/logrus v1.8.1
|
||||
github.com/spf13/cobra v1.2.1
|
||||
github.com/stretchr/testify v1.7.0
|
||||
github.com/tebeka/selenium v0.9.9
|
||||
github.com/tstranex/u2f v1.0.0
|
||||
github.com/valyala/fasthttp v1.31.0
|
||||
golang.org/x/sys v0.0.0-20210902050250-f475640dd07b // indirect
|
||||
|
|
23
go.sum
23
go.sum
|
@ -43,10 +43,7 @@ github.com/Azure/go-ansiterm v0.0.0-20170929234023-d6e3b3328b78/go.mod h1:LmzpDX
|
|||
github.com/Azure/go-ntlmssp v0.0.0-20200615164410-66371956d46c h1:/IBSNwUN8+eKzUzbJPqhK839ygXJ82sde8x3ogr6R28=
|
||||
github.com/Azure/go-ntlmssp v0.0.0-20200615164410-66371956d46c/go.mod h1:chxPXzSsl7ZWRAuOIE23GDNzjWuZquvFlgA8xmpunjU=
|
||||
github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU=
|
||||
github.com/BurntSushi/xgb v0.0.0-20160522181843-27f122750802 h1:1BDTz0u9nC3//pOCMdNH+CiXJVYJh5UQNCOBG7jbELc=
|
||||
github.com/BurntSushi/xgb v0.0.0-20160522181843-27f122750802/go.mod h1:IVnqGOEym/WlBOVXweHU+Q+/VP0lqqI8lqeDx9IjBqo=
|
||||
github.com/BurntSushi/xgbutil v0.0.0-20160919175755-f7c97cef3b4e h1:4ZrkT/RzpnROylmoQL57iVUL57wGKTR5O6KpVnbm2tA=
|
||||
github.com/BurntSushi/xgbutil v0.0.0-20160919175755-f7c97cef3b4e/go.mod h1:uw9h2sd4WWHOPdJ13MQpwK5qYWKYDumDqxWWIknEQ+k=
|
||||
github.com/DATA-DOG/go-sqlmock v1.3.3/go.mod h1:f/Ixk793poVmq4qj/V1dPUg2JEAKC73Q5eFN3EC/SaM=
|
||||
github.com/DATA-DOG/go-sqlmock v1.5.0 h1:Shsta01QNfFxHCfpW6YH2STWB0MudeXXEWMr20OEh60=
|
||||
github.com/DATA-DOG/go-sqlmock v1.5.0/go.mod h1:f/Ixk793poVmq4qj/V1dPUg2JEAKC73Q5eFN3EC/SaM=
|
||||
|
@ -89,8 +86,6 @@ github.com/armon/consul-api v0.0.0-20180202201655-eb2c6b5be1b6/go.mod h1:grANhF5
|
|||
github.com/armon/go-metrics v0.0.0-20180917152333-f0300d1749da/go.mod h1:Q73ZrmVTwzkszR9V5SSuryQ31EELlFMUz1kKyl939pY=
|
||||
github.com/armon/go-radix v0.0.0-20180808171621-7fddfc383310/go.mod h1:ufUuZ+zHj4x4TnLV4JWEpy2hxWSpsRywHrMgIH9cCH8=
|
||||
github.com/armon/go-radix v1.0.0/go.mod h1:ufUuZ+zHj4x4TnLV4JWEpy2hxWSpsRywHrMgIH9cCH8=
|
||||
github.com/armon/go-socks5 v0.0.0-20160902184237-e75332964ef5 h1:0CwZNZbxp69SHPdPJAN/hZIm0C4OItdklCFmMRWYpio=
|
||||
github.com/armon/go-socks5 v0.0.0-20160902184237-e75332964ef5/go.mod h1:wHh0iHkYZB8zMSxRWpUBQtwG5a7fFgvEO+odwuTv2gs=
|
||||
github.com/aryann/difflib v0.0.0-20170710044230-e206f873d14a/go.mod h1:DAHtR1m6lCRdSC2Tm3DSWRPvIPr6xNKyeHdqDQSQT+A=
|
||||
github.com/asaskevich/govalidator v0.0.0-20180720115003-f9ffefc3facf/go.mod h1:lB+ZfQJz7igIIfQNfa7Ml4HSf2uFQQRzpGGRXenZAgY=
|
||||
github.com/asaskevich/govalidator v0.0.0-20190424111038-f61b66f89f4a/go.mod h1:lB+ZfQJz7igIIfQNfa7Ml4HSf2uFQQRzpGGRXenZAgY=
|
||||
|
@ -119,8 +114,6 @@ github.com/beorn7/perks v1.0.1/go.mod h1:G2ZrVWU2WbWT9wwq4/hrbKbnv/1ERSJQ0ibhJ6r
|
|||
github.com/bgentry/speakeasy v0.1.0/go.mod h1:+zsyZBPWlz7T6j88CTgSN5bM796AkVf0kBD4zp0CCIs=
|
||||
github.com/bketelsen/crypt v0.0.3-0.20200106085610-5cbc8cc4026c/go.mod h1:MKsuJmJgSg28kpZDP6UIiPt0e0Oz0kqKNGyRaWEPv84=
|
||||
github.com/bketelsen/crypt v0.0.4/go.mod h1:aI6NrJ0pMGgvZKL1iVgXLnfIFJtfV+bKCoqOes/6LfM=
|
||||
github.com/blang/semver v3.5.1+incompatible h1:cQNTCjp13qL8KC3Nbxr/y2Bqb63oX6wdnnjpJbkM4JQ=
|
||||
github.com/blang/semver v3.5.1+incompatible/go.mod h1:kRBLl5iJ+tD4TcOOxsy/0fnwebNt5EWlYSAyrTnjyyk=
|
||||
github.com/bmatcuk/doublestar/v2 v2.0.3/go.mod h1:QMmcs3H2AUQICWhfzLXz+IYln8lRQmTZRptLie8RgRw=
|
||||
github.com/bmizerany/assert v0.0.0-20160611221934-b7ed37b82869/go.mod h1:Ekp36dRnpXw/yCqJaO+ZrUyxD+3VXMFFr56k5XYrpB4=
|
||||
github.com/boombuler/barcode v1.0.1-0.20190219062509-6c824513bacc h1:biVzkmvwrH8WK8raXaxBx6fRVTlJILwEwQGL1I/ByEI=
|
||||
|
@ -315,6 +308,8 @@ github.com/go-openapi/validate v0.19.3/go.mod h1:90Vh6jjkTn+OT1Eefm0ZixWNFjhtOH7
|
|||
github.com/go-openapi/validate v0.19.10/go.mod h1:RKEZTUWDkxKQxN2jDT7ZnZi2bhZlbNMAuKvKB+IaGx8=
|
||||
github.com/go-redis/redis/v8 v8.11.4 h1:kHoYkfZP6+pe04aFTnhDH6GDROa5yJdHJVNxV3F46Tg=
|
||||
github.com/go-redis/redis/v8 v8.11.4/go.mod h1:2Z2wHZXdQpCDXEGzqMockDpNyYvi2l4Pxt6RJr792+w=
|
||||
github.com/go-rod/rod v0.101.8 h1:oV0O97uwjkCVyAP0hD6K6bBE8FUMIjs0dtF7l6kEBsU=
|
||||
github.com/go-rod/rod v0.101.8/go.mod h1:N/zlT53CfSpq74nb6rOR0K8UF0SPUPBmzBnArrms+mY=
|
||||
github.com/go-sql-driver/mysql v1.4.0/go.mod h1:zAC/RDZ24gD3HViQzih4MyKcchzm+sOG5ZlKdlhCg5w=
|
||||
github.com/go-sql-driver/mysql v1.4.1/go.mod h1:zAC/RDZ24gD3HViQzih4MyKcchzm+sOG5ZlKdlhCg5w=
|
||||
github.com/go-sql-driver/mysql v1.5.0/go.mod h1:DCzpHaOWr8IXmIStZouvnhqoel9Qv2LBy8hT2VhHyBg=
|
||||
|
@ -646,10 +641,8 @@ github.com/google/go-cmp v0.5.4/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/
|
|||
github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
|
||||
github.com/google/go-cmp v0.5.6 h1:BKbKCqvP6I+rmFHt06ZmyQtvB8xAkWdhFyr0ZUNZcxQ=
|
||||
github.com/google/go-cmp v0.5.6/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
|
||||
github.com/google/go-github/v27 v27.0.4/go.mod h1:/0Gr8pJ55COkmv+S/yPKCczSkUPIM/LnFyubufRNIS0=
|
||||
github.com/google/go-jsonnet v0.16.0/go.mod h1:sOcuej3UW1vpPTZOr8L7RQimqai1a57bt5j22LzGZCw=
|
||||
github.com/google/go-jsonnet v0.17.0/go.mod h1:sOcuej3UW1vpPTZOr8L7RQimqai1a57bt5j22LzGZCw=
|
||||
github.com/google/go-querystring v1.0.0/go.mod h1:odCYkC5MyYFN7vkCjXpyrEuKhc/BUO6wN/zVPAxq5ck=
|
||||
github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg=
|
||||
github.com/google/martian v2.1.0+incompatible/go.mod h1:9I4somxYTbIHy5NJKHRl3wXiIaQGbYVAs8BPL6v8lEs=
|
||||
github.com/google/martian/v3 v3.0.0/go.mod h1:y5Zk1BBys9G+gd6Jrk0W3cC1+ELVxBWuIGO+w/tUAp0=
|
||||
|
@ -1297,8 +1290,6 @@ github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/
|
|||
github.com/subosito/gotenv v1.1.1/go.mod h1:N0PQaV/YGNqwC0u51sEeR/aUtSLEXKX9iv69rRypqCw=
|
||||
github.com/subosito/gotenv v1.2.0 h1:Slr1R9HxAlEKefgq5jn9U+DnETlIUa6HfgEzj0g5d7s=
|
||||
github.com/subosito/gotenv v1.2.0/go.mod h1:N0PQaV/YGNqwC0u51sEeR/aUtSLEXKX9iv69rRypqCw=
|
||||
github.com/tebeka/selenium v0.9.9 h1:cNziB+etNgyH/7KlNI7RMC1ua5aH1+5wUlFQyzeMh+w=
|
||||
github.com/tebeka/selenium v0.9.9/go.mod h1:5Fr8+pUvU6B1OiPfkdCKdXZyr5znvVkxuPd0NOdZCQc=
|
||||
github.com/tidwall/gjson v1.11.0 h1:C16pk7tQNiH6VlCrtIXL1w8GaOsi1X3W8KDkE1BuYd4=
|
||||
github.com/tidwall/gjson v1.11.0/go.mod h1:/wbyibRr2FHMks5tjHJ5F8dMZh3AcwJEMf5vlfC0lxk=
|
||||
github.com/tidwall/match v1.1.1 h1:+Ho715JplO36QYgwN9PGYNhgZvoUSc9X2c80KVTi+GA=
|
||||
|
@ -1342,6 +1333,16 @@ github.com/xeipuuv/gojsonpointer v0.0.0-20180127040702-4e3ac2762d5f/go.mod h1:N2
|
|||
github.com/xiang90/probing v0.0.0-20190116061207-43a291ad63a2/go.mod h1:UETIi67q53MR2AWcXfiuqkDkRtnGDLqkBTpCHuJHxtU=
|
||||
github.com/xordataexchange/crypt v0.0.3-0.20170626215501-b2862e3d0a77/go.mod h1:aYKd//L2LvnjZzWKhF00oedf4jCCReLcmhLdhm1A27Q=
|
||||
github.com/xtgo/uuid v0.0.0-20140804021211-a0b114877d4c/go.mod h1:UrdRz5enIKZ63MEE3IF9l2/ebyx59GyGgPi+tICQdmM=
|
||||
github.com/ysmood/goob v0.3.0 h1:XZ51cZJ4W3WCoCiUktixzMIQF86W7G5VFL4QQ/Q2uS0=
|
||||
github.com/ysmood/goob v0.3.0/go.mod h1:S3lq113Y91y1UBf1wj1pFOxeahvfKkCk6mTWTWbDdWs=
|
||||
github.com/ysmood/got v0.15.1 h1:X5jAbMyBf5yeezuFMp9HaMGXZWMSqIQcUlAHI+kJmUs=
|
||||
github.com/ysmood/got v0.15.1/go.mod h1:pE1l4LOwOBhQg6A/8IAatkGp7uZjnalzrZolnlhhMgY=
|
||||
github.com/ysmood/gotrace v0.2.2 h1:006KHGRThSRf8lwh4EyhNmuuq/l+Ygs+JqojkhEG1/E=
|
||||
github.com/ysmood/gotrace v0.2.2/go.mod h1:TzhIG7nHDry5//eYZDYcTzuJLYQIkykJzCRIo4/dzQM=
|
||||
github.com/ysmood/gson v0.6.4 h1:Yb6tosv6bk59HqjZu2/7o4BFherpYEMkDkXmlhgryZ4=
|
||||
github.com/ysmood/gson v0.6.4/go.mod h1:3Kzs5zDl21g5F/BlLTNcuAGAYLKt2lV5G8D1zF3RNmg=
|
||||
github.com/ysmood/leakless v0.7.0 h1:XCGdaPExyoreoQd+H5qgxM3ReNbSPFsEXpSKwbXbwQw=
|
||||
github.com/ysmood/leakless v0.7.0/go.mod h1:R8iAXPRaG97QJwqxs74RdwzcRHT1SWCGTNqY8q0JvMQ=
|
||||
github.com/yuin/goldmark v1.1.25/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
|
||||
github.com/yuin/goldmark v1.1.27/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
|
||||
github.com/yuin/goldmark v1.1.32/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
|
||||
|
|
|
@ -1,17 +1,17 @@
|
|||
package suites
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"testing"
|
||||
|
||||
"github.com/go-rod/rod"
|
||||
"github.com/stretchr/testify/require"
|
||||
)
|
||||
|
||||
func (wds *WebDriverSession) doChangeMethod(ctx context.Context, t *testing.T, method string) {
|
||||
err := wds.WaitElementLocatedByID(ctx, t, "methods-button").Click()
|
||||
func (rs *RodSession) doChangeMethod(t *testing.T, page *rod.Page, method string) {
|
||||
err := rs.WaitElementLocatedByCSSSelector(t, page, "methods-button").Click("left")
|
||||
require.NoError(t, err)
|
||||
wds.WaitElementLocatedByID(ctx, t, "methods-dialog")
|
||||
err = wds.WaitElementLocatedByID(ctx, t, fmt.Sprintf("%s-option", method)).Click()
|
||||
rs.WaitElementLocatedByCSSSelector(t, page, "methods-dialog")
|
||||
err = rs.WaitElementLocatedByCSSSelector(t, page, fmt.Sprintf("%s-option", method)).Click("left")
|
||||
require.NoError(t, err)
|
||||
}
|
||||
|
|
|
@ -1,44 +1,44 @@
|
|||
package suites
|
||||
|
||||
import (
|
||||
"context"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"github.com/go-rod/rod"
|
||||
"github.com/stretchr/testify/require"
|
||||
)
|
||||
|
||||
func (wds *WebDriverSession) doFillLoginPageAndClick(ctx context.Context, t *testing.T, username, password string, keepMeLoggedIn bool) {
|
||||
usernameElement := wds.WaitElementLocatedByID(ctx, t, "username-textfield")
|
||||
err := usernameElement.SendKeys(username)
|
||||
func (rs *RodSession) doFillLoginPageAndClick(t *testing.T, page *rod.Page, username, password string, keepMeLoggedIn bool) {
|
||||
usernameElement := rs.WaitElementLocatedByCSSSelector(t, page, "username-textfield")
|
||||
err := usernameElement.Input(username)
|
||||
require.NoError(t, err)
|
||||
|
||||
passwordElement := wds.WaitElementLocatedByID(ctx, t, "password-textfield")
|
||||
err = passwordElement.SendKeys(password)
|
||||
passwordElement := rs.WaitElementLocatedByCSSSelector(t, page, "password-textfield")
|
||||
err = passwordElement.Input(password)
|
||||
require.NoError(t, err)
|
||||
|
||||
if keepMeLoggedIn {
|
||||
keepMeLoggedInElement := wds.WaitElementLocatedByID(ctx, t, "remember-checkbox")
|
||||
err = keepMeLoggedInElement.Click()
|
||||
keepMeLoggedInElement := rs.WaitElementLocatedByCSSSelector(t, page, "remember-checkbox")
|
||||
err = keepMeLoggedInElement.Click("left")
|
||||
require.NoError(t, err)
|
||||
}
|
||||
|
||||
buttonElement := wds.WaitElementLocatedByID(ctx, t, "sign-in-button")
|
||||
err = buttonElement.Click()
|
||||
buttonElement := rs.WaitElementLocatedByCSSSelector(t, page, "sign-in-button")
|
||||
err = buttonElement.Click("left")
|
||||
require.NoError(t, err)
|
||||
}
|
||||
|
||||
// Login 1FA.
|
||||
func (wds *WebDriverSession) doLoginOneFactor(ctx context.Context, t *testing.T, username, password string, keepMeLoggedIn bool, targetURL string) {
|
||||
wds.doVisitLoginPage(ctx, t, targetURL)
|
||||
wds.doFillLoginPageAndClick(ctx, t, username, password, keepMeLoggedIn)
|
||||
func (rs *RodSession) doLoginOneFactor(t *testing.T, page *rod.Page, username, password string, keepMeLoggedIn bool, targetURL string) {
|
||||
rs.doVisitLoginPage(t, page, targetURL)
|
||||
rs.doFillLoginPageAndClick(t, page, username, password, keepMeLoggedIn)
|
||||
}
|
||||
|
||||
// Login 1FA and 2FA subsequently (must already be registered).
|
||||
func (wds *WebDriverSession) doLoginTwoFactor(ctx context.Context, t *testing.T, username, password string, keepMeLoggedIn bool, otpSecret, targetURL string) {
|
||||
wds.doLoginOneFactor(ctx, t, username, password, keepMeLoggedIn, targetURL)
|
||||
wds.verifyIsSecondFactorPage(ctx, t)
|
||||
wds.doValidateTOTP(ctx, t, otpSecret)
|
||||
func (rs *RodSession) doLoginTwoFactor(t *testing.T, page *rod.Page, username, password string, keepMeLoggedIn bool, otpSecret, targetURL string) {
|
||||
rs.doLoginOneFactor(t, page, username, password, keepMeLoggedIn, targetURL)
|
||||
rs.verifyIsSecondFactorPage(t, page)
|
||||
rs.doValidateTOTP(t, page, otpSecret)
|
||||
// timeout when targetURL is not defined to prevent a show stopping redirect when visiting a protected domain
|
||||
if targetURL == "" {
|
||||
time.Sleep(1 * time.Second)
|
||||
|
@ -46,20 +46,20 @@ func (wds *WebDriverSession) doLoginTwoFactor(ctx context.Context, t *testing.T,
|
|||
}
|
||||
|
||||
// Login 1FA and register 2FA.
|
||||
func (wds *WebDriverSession) doLoginAndRegisterTOTP(ctx context.Context, t *testing.T, username, password string, keepMeLoggedIn bool) string {
|
||||
wds.doLoginOneFactor(ctx, t, username, password, keepMeLoggedIn, "")
|
||||
secret := wds.doRegisterTOTP(ctx, t)
|
||||
wds.doVisit(t, GetLoginBaseURL())
|
||||
wds.verifyIsSecondFactorPage(ctx, t)
|
||||
func (rs *RodSession) doLoginAndRegisterTOTP(t *testing.T, page *rod.Page, username, password string, keepMeLoggedIn bool) string {
|
||||
rs.doLoginOneFactor(t, page, username, password, keepMeLoggedIn, "")
|
||||
secret := rs.doRegisterTOTP(t, page)
|
||||
rs.doVisit(t, page, GetLoginBaseURL())
|
||||
rs.verifyIsSecondFactorPage(t, page)
|
||||
|
||||
return secret
|
||||
}
|
||||
|
||||
// Register a user with TOTP, logout and then authenticate until TOTP-2FA.
|
||||
func (wds *WebDriverSession) doRegisterAndLogin2FA(ctx context.Context, t *testing.T, username, password string, keepMeLoggedIn bool, targetURL string) string { //nolint:unparam
|
||||
func (rs *RodSession) doRegisterAndLogin2FA(t *testing.T, page *rod.Page, username, password string, keepMeLoggedIn bool, targetURL string) string { //nolint:unparam
|
||||
// Register TOTP secret and logout.
|
||||
secret := wds.doRegisterThenLogout(ctx, t, username, password)
|
||||
wds.doLoginTwoFactor(ctx, t, username, password, keepMeLoggedIn, secret, targetURL)
|
||||
secret := rs.doRegisterThenLogout(t, page, username, password)
|
||||
rs.doLoginTwoFactor(t, page, username, password, keepMeLoggedIn, secret, targetURL)
|
||||
|
||||
return secret
|
||||
}
|
||||
|
|
|
@ -1,25 +1,28 @@
|
|||
package suites
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"net/url"
|
||||
"testing"
|
||||
|
||||
"github.com/go-rod/rod"
|
||||
)
|
||||
|
||||
func (wds *WebDriverSession) doLogout(ctx context.Context, t *testing.T) {
|
||||
wds.doVisit(t, fmt.Sprintf("%s%s", GetLoginBaseURL(), "/logout"))
|
||||
wds.verifyIsFirstFactorPage(ctx, t)
|
||||
func (rs *RodSession) doLogout(t *testing.T, page *rod.Page) {
|
||||
rs.doVisit(t, page, fmt.Sprintf("%s%s", GetLoginBaseURL(), "/logout"))
|
||||
rs.verifyIsFirstFactorPage(t, page)
|
||||
}
|
||||
|
||||
func (wds *WebDriverSession) doLogoutWithRedirect(ctx context.Context, t *testing.T, targetURL string, firstFactor bool) {
|
||||
wds.doVisit(t, fmt.Sprintf("%s%s%s", GetLoginBaseURL(), "/logout?rd=", url.QueryEscape(targetURL)))
|
||||
func (rs *RodSession) doLogoutWithRedirect(t *testing.T, page *rod.Page, targetURL string, firstFactor bool) {
|
||||
rs.doVisit(t, page, fmt.Sprintf("%s%s%s", GetLoginBaseURL(), "/logout?rd=", url.QueryEscape(targetURL)))
|
||||
|
||||
if firstFactor {
|
||||
wds.verifyIsFirstFactorPage(ctx, t)
|
||||
rs.verifyIsFirstFactorPage(t, page)
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
wds.verifyURLIs(ctx, t, targetURL)
|
||||
page.MustElementR("h1", "Public resource")
|
||||
|
||||
rs.verifyURLIs(t, page, targetURL)
|
||||
}
|
||||
|
|
|
@ -1,13 +1,14 @@
|
|||
package suites
|
||||
|
||||
import (
|
||||
"context"
|
||||
"testing"
|
||||
|
||||
"github.com/go-rod/rod"
|
||||
)
|
||||
|
||||
func (wds *WebDriverSession) doRegisterThenLogout(ctx context.Context, t *testing.T, username, password string) string {
|
||||
secret := wds.doLoginAndRegisterTOTP(ctx, t, username, password, false)
|
||||
wds.doLogout(ctx, t)
|
||||
func (rs *RodSession) doRegisterThenLogout(t *testing.T, page *rod.Page, username, password string) string {
|
||||
secret := rs.doLoginAndRegisterTOTP(t, page, username, password, false)
|
||||
rs.doLogout(t, page)
|
||||
|
||||
return secret
|
||||
}
|
||||
|
|
|
@ -1,52 +1,60 @@
|
|||
package suites
|
||||
|
||||
import (
|
||||
"context"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"github.com/go-rod/rod"
|
||||
"github.com/stretchr/testify/require"
|
||||
)
|
||||
|
||||
func (wds *WebDriverSession) doInitiatePasswordReset(ctx context.Context, t *testing.T, username string) {
|
||||
err := wds.WaitElementLocatedByID(ctx, t, "reset-password-button").Click()
|
||||
func (rs *RodSession) doInitiatePasswordReset(t *testing.T, page *rod.Page, username string) {
|
||||
err := rs.WaitElementLocatedByCSSSelector(t, page, "reset-password-button").Click("left")
|
||||
require.NoError(t, err)
|
||||
// Fill in username
|
||||
err = wds.WaitElementLocatedByID(ctx, t, "username-textfield").SendKeys(username)
|
||||
err = rs.WaitElementLocatedByCSSSelector(t, page, "username-textfield").Input(username)
|
||||
require.NoError(t, err)
|
||||
// And click on the reset button
|
||||
err = wds.WaitElementLocatedByID(ctx, t, "reset-button").Click()
|
||||
err = rs.WaitElementLocatedByCSSSelector(t, page, "reset-button").Click("left")
|
||||
require.NoError(t, err)
|
||||
}
|
||||
|
||||
func (wds *WebDriverSession) doCompletePasswordReset(ctx context.Context, t *testing.T, newPassword1, newPassword2 string) {
|
||||
func (rs *RodSession) doCompletePasswordReset(t *testing.T, page *rod.Page, newPassword1, newPassword2 string) {
|
||||
link := doGetLinkFromLastMail(t)
|
||||
wds.doVisit(t, link)
|
||||
rs.doVisit(t, page, link)
|
||||
|
||||
err := wds.WaitElementLocatedByID(ctx, t, "password1-textfield").SendKeys(newPassword1)
|
||||
time.Sleep(1 * time.Second)
|
||||
|
||||
err := rs.WaitElementLocatedByCSSSelector(t, page, "password1-textfield").Input(newPassword1)
|
||||
require.NoError(t, err)
|
||||
err = wds.WaitElementLocatedByID(ctx, t, "password2-textfield").SendKeys(newPassword2)
|
||||
|
||||
time.Sleep(1 * time.Second)
|
||||
|
||||
err = rs.WaitElementLocatedByCSSSelector(t, page, "password2-textfield").Input(newPassword2)
|
||||
require.NoError(t, err)
|
||||
err = wds.WaitElementLocatedByID(ctx, t, "reset-button").Click()
|
||||
|
||||
err = rs.WaitElementLocatedByCSSSelector(t, page, "reset-button").Click("left")
|
||||
require.NoError(t, err)
|
||||
}
|
||||
|
||||
func (wds *WebDriverSession) doSuccessfullyCompletePasswordReset(ctx context.Context, t *testing.T, newPassword1, newPassword2 string) {
|
||||
wds.doCompletePasswordReset(ctx, t, newPassword1, newPassword2)
|
||||
wds.verifyIsFirstFactorPage(ctx, t)
|
||||
func (rs *RodSession) doSuccessfullyCompletePasswordReset(t *testing.T, page *rod.Page, newPassword1, newPassword2 string) {
|
||||
rs.doCompletePasswordReset(t, page, newPassword1, newPassword2)
|
||||
rs.verifyIsFirstFactorPage(t, page)
|
||||
}
|
||||
|
||||
func (wds *WebDriverSession) doUnsuccessfulPasswordReset(ctx context.Context, t *testing.T, newPassword1, newPassword2 string) {
|
||||
wds.doCompletePasswordReset(ctx, t, newPassword1, newPassword2)
|
||||
func (rs *RodSession) doUnsuccessfulPasswordReset(t *testing.T, page *rod.Page, newPassword1, newPassword2 string) {
|
||||
rs.doCompletePasswordReset(t, page, newPassword1, newPassword2)
|
||||
rs.verifyNotificationDisplayed(t, page, "Your supplied password does not meet the password policy requirements.")
|
||||
}
|
||||
|
||||
func (wds *WebDriverSession) doResetPassword(ctx context.Context, t *testing.T, username, newPassword1, newPassword2 string, unsuccessful bool) {
|
||||
wds.doInitiatePasswordReset(ctx, t, username)
|
||||
func (rs *RodSession) doResetPassword(t *testing.T, page *rod.Page, username, newPassword1, newPassword2 string, unsuccessful bool) {
|
||||
rs.doInitiatePasswordReset(t, page, username)
|
||||
// then wait for the "email sent notification"
|
||||
wds.verifyMailNotificationDisplayed(ctx, t)
|
||||
rs.verifyMailNotificationDisplayed(t, page)
|
||||
|
||||
if unsuccessful {
|
||||
wds.doUnsuccessfulPasswordReset(ctx, t, newPassword1, newPassword2)
|
||||
rs.doUnsuccessfulPasswordReset(t, page, newPassword1, newPassword2)
|
||||
} else {
|
||||
wds.doSuccessfullyCompletePasswordReset(ctx, t, newPassword1, newPassword2)
|
||||
rs.doSuccessfullyCompletePasswordReset(t, page, newPassword1, newPassword2)
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,43 +1,42 @@
|
|||
package suites
|
||||
|
||||
import (
|
||||
"context"
|
||||
"strings"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"github.com/go-rod/rod"
|
||||
"github.com/pquerna/otp/totp"
|
||||
"github.com/stretchr/testify/assert"
|
||||
"github.com/stretchr/testify/require"
|
||||
)
|
||||
|
||||
func (wds *WebDriverSession) doRegisterTOTP(ctx context.Context, t *testing.T) string {
|
||||
err := wds.WaitElementLocatedByID(ctx, t, "register-link").Click()
|
||||
func (rs *RodSession) doRegisterTOTP(t *testing.T, page *rod.Page) string {
|
||||
err := rs.WaitElementLocatedByCSSSelector(t, page, "register-link").Click("left")
|
||||
require.NoError(t, err)
|
||||
wds.verifyMailNotificationDisplayed(ctx, t)
|
||||
rs.verifyMailNotificationDisplayed(t, page)
|
||||
link := doGetLinkFromLastMail(t)
|
||||
wds.doVisit(t, link)
|
||||
secretURL, err := wds.WaitElementLocatedByID(ctx, t, "secret-url").GetAttribute("value")
|
||||
rs.doVisit(t, page, link)
|
||||
secretURL, err := page.MustElement("#secret-url").Attribute("value")
|
||||
assert.NoError(t, err)
|
||||
|
||||
secret := secretURL[strings.LastIndex(secretURL, "=")+1:]
|
||||
secret := (*secretURL)[strings.LastIndex(*secretURL, "=")+1:]
|
||||
assert.NotEqual(t, "", secret)
|
||||
assert.NotNil(t, secret)
|
||||
|
||||
return secret
|
||||
}
|
||||
|
||||
func (wds *WebDriverSession) doEnterOTP(ctx context.Context, t *testing.T, code string) {
|
||||
inputs := wds.WaitElementsLocatedByCSSSelector(ctx, t, "#otp-input input")
|
||||
func (rs *RodSession) doEnterOTP(t *testing.T, page *rod.Page, code string) {
|
||||
inputs := rs.WaitElementsLocatedByCSSSelector(t, page, "otp-input input")
|
||||
|
||||
for i := 0; i < 6; i++ {
|
||||
err := inputs[i].SendKeys(string(code[i]))
|
||||
require.NoError(t, err)
|
||||
_ = inputs[i].Input(string(code[i]))
|
||||
}
|
||||
}
|
||||
|
||||
func (wds *WebDriverSession) doValidateTOTP(ctx context.Context, t *testing.T, secret string) {
|
||||
func (rs *RodSession) doValidateTOTP(t *testing.T, page *rod.Page, secret string) {
|
||||
code, err := totp.GenerateCode(secret, time.Now())
|
||||
assert.NoError(t, err)
|
||||
wds.doEnterOTP(ctx, t, code)
|
||||
rs.doEnterOTP(t, page, code)
|
||||
}
|
||||
|
|
|
@ -1,28 +1,36 @@
|
|||
package suites
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"testing"
|
||||
|
||||
"github.com/go-rod/rod"
|
||||
"github.com/go-rod/rod/lib/proto"
|
||||
"github.com/stretchr/testify/assert"
|
||||
)
|
||||
|
||||
func (wds *WebDriverSession) doVisit(t *testing.T, url string) {
|
||||
err := wds.WebDriver.Get(url)
|
||||
func (rs *RodSession) doCreateTab(t *testing.T, url string) *rod.Page {
|
||||
p, err := rs.WebDriver.MustIncognito().Page(proto.TargetCreateTarget{URL: url})
|
||||
assert.NoError(t, err)
|
||||
|
||||
return p
|
||||
}
|
||||
|
||||
func (rs *RodSession) doVisit(t *testing.T, page *rod.Page, url string) {
|
||||
err := page.Navigate(url)
|
||||
assert.NoError(t, err)
|
||||
}
|
||||
|
||||
func (wds *WebDriverSession) doVisitAndVerifyOneFactorStep(ctx context.Context, t *testing.T, url string) {
|
||||
wds.doVisit(t, url)
|
||||
wds.verifyIsFirstFactorPage(ctx, t)
|
||||
func (rs *RodSession) doVisitAndVerifyOneFactorStep(t *testing.T, page *rod.Page, url string) {
|
||||
rs.doVisit(t, page, url)
|
||||
rs.verifyIsFirstFactorPage(t, page)
|
||||
}
|
||||
|
||||
func (wds *WebDriverSession) doVisitLoginPage(ctx context.Context, t *testing.T, targetURL string) {
|
||||
func (rs *RodSession) doVisitLoginPage(t *testing.T, page *rod.Page, targetURL string) {
|
||||
suffix := ""
|
||||
if targetURL != "" {
|
||||
suffix = fmt.Sprintf("?rd=%s", targetURL)
|
||||
}
|
||||
|
||||
wds.doVisitAndVerifyOneFactorStep(ctx, t, fmt.Sprintf("%s/%s", GetLoginBaseURL(), suffix))
|
||||
rs.doVisitAndVerifyOneFactorStep(t, page, fmt.Sprintf("%s/%s", GetLoginBaseURL(), suffix))
|
||||
}
|
||||
|
|
|
@ -51,7 +51,6 @@ var DuoBaseURL = "https://duo.example.com"
|
|||
var AutheliaBaseURL = "https://authelia.example.com:9091"
|
||||
|
||||
const stringTrue = "true"
|
||||
const defaultChromeDriverPort = "4444"
|
||||
|
||||
const testUsername = "john"
|
||||
const testPassword = "password"
|
||||
|
|
|
@ -1 +1 @@
|
|||
-R '^web/' -r '(\.go$|go\.mod|\.sh|\.yml)' -s /resources/run-backend-dev.sh
|
||||
-R '^web/' -R 'users.yml' -r '(\.go$|go\.mod|\.sh|\.yml)' -s /resources/run-backend-dev.sh
|
|
@ -4,11 +4,15 @@ global
|
|||
log stdout format raw local0 debug
|
||||
|
||||
defaults
|
||||
default-server init-addr none
|
||||
mode http
|
||||
log global
|
||||
option httplog
|
||||
option forwardfor
|
||||
|
||||
resolvers docker
|
||||
nameserver ip 127.0.0.11:53
|
||||
|
||||
frontend fe_api
|
||||
bind *:8081 ssl crt /usr/local/etc/haproxy/haproxy.pem
|
||||
|
||||
|
@ -63,13 +67,13 @@ backend be_auth_request
|
|||
listen be_auth_request_proxy
|
||||
mode http
|
||||
bind 127.0.0.1:8085
|
||||
server authelia-backend authelia-backend:9091 ssl verify none
|
||||
server authelia-backend authelia-backend:9091 resolvers docker ssl verify none
|
||||
|
||||
backend be_authelia
|
||||
server authelia-backend authelia-backend:9091 ssl verify none
|
||||
server authelia-backend authelia-backend:9091 resolvers docker ssl verify none
|
||||
|
||||
backend fe_authelia
|
||||
server authelia-frontend authelia-frontend:3000
|
||||
server authelia-frontend authelia-frontend:3000 resolvers docker
|
||||
|
||||
backend be_httpbin
|
||||
acl remote_user_exist var(req.auth_response_header.remote_user) -m found
|
||||
|
@ -81,10 +85,10 @@ backend be_httpbin
|
|||
http-request set-header Remote-Name %[var(req.auth_response_header.remote_name)] if remote_name_exist
|
||||
http-request set-header Remote-Email %[var(req.auth_response_header.remote_email)] if remote_email_exist
|
||||
|
||||
server httpbin-backend httpbin:8000
|
||||
server httpbin-backend httpbin:8000 resolvers docker
|
||||
|
||||
backend be_mail
|
||||
server smtp-backend smtp:1080
|
||||
server smtp-backend smtp:1080 resolvers docker
|
||||
|
||||
backend be_protected
|
||||
server nginx-backend nginx-backend:80
|
||||
server nginx-backend nginx-backend:80 resolvers docker
|
||||
|
|
|
@ -5,36 +5,34 @@ import (
|
|||
"log"
|
||||
"time"
|
||||
|
||||
"github.com/tebeka/selenium"
|
||||
|
||||
"github.com/authelia/authelia/v4/internal/utils"
|
||||
)
|
||||
|
||||
type AvailableMethodsScenario struct {
|
||||
*SeleniumSuite
|
||||
*RodSuite
|
||||
|
||||
methods []string
|
||||
}
|
||||
|
||||
func NewAvailableMethodsScenario(methods []string) *AvailableMethodsScenario {
|
||||
return &AvailableMethodsScenario{
|
||||
SeleniumSuite: new(SeleniumSuite),
|
||||
RodSuite: new(RodSuite),
|
||||
methods: methods,
|
||||
}
|
||||
}
|
||||
|
||||
func (s *AvailableMethodsScenario) SetupSuite() {
|
||||
wds, err := StartWebDriver()
|
||||
browser, err := StartRod()
|
||||
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
|
||||
s.SeleniumSuite.WebDriverSession = wds
|
||||
s.RodSession = browser
|
||||
}
|
||||
|
||||
func (s *AvailableMethodsScenario) TearDownSuite() {
|
||||
err := s.WebDriverSession.Stop()
|
||||
err := s.RodSession.Stop()
|
||||
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
|
@ -42,26 +40,30 @@ func (s *AvailableMethodsScenario) TearDownSuite() {
|
|||
}
|
||||
|
||||
func (s *AvailableMethodsScenario) SetupTest() {
|
||||
ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second)
|
||||
defer cancel()
|
||||
s.Page = s.doCreateTab(s.T(), HomeBaseURL)
|
||||
s.verifyIsHome(s.T(), s.Page)
|
||||
}
|
||||
|
||||
s.doLogout(ctx, s.T())
|
||||
s.doVisit(s.T(), HomeBaseURL)
|
||||
s.verifyIsHome(ctx, s.T())
|
||||
func (s *AvailableMethodsScenario) TearDownTest() {
|
||||
s.collectCoverage(s.Page)
|
||||
s.MustClose()
|
||||
}
|
||||
|
||||
func (s *AvailableMethodsScenario) TestShouldCheckAvailableMethods() {
|
||||
ctx, cancel := context.WithTimeout(context.Background(), 20*time.Second)
|
||||
defer cancel()
|
||||
defer func() {
|
||||
cancel()
|
||||
s.collectScreenshot(ctx.Err(), s.Page)
|
||||
}()
|
||||
|
||||
s.doLoginOneFactor(ctx, s.T(), "john", "password", false, "")
|
||||
s.doLoginOneFactor(s.T(), s.Context(ctx), "john", "password", false, "")
|
||||
|
||||
methodsButton := s.WaitElementLocatedByID(ctx, s.T(), "methods-button")
|
||||
err := methodsButton.Click()
|
||||
methodsButton := s.WaitElementLocatedByCSSSelector(s.T(), s.Context(ctx), "methods-button")
|
||||
err := methodsButton.Click("left")
|
||||
s.Assert().NoError(err)
|
||||
|
||||
methodsDialog := s.WaitElementLocatedByID(ctx, s.T(), "methods-dialog")
|
||||
options, err := methodsDialog.FindElements(selenium.ByClassName, "method-option")
|
||||
methodsDialog := s.WaitElementLocatedByCSSSelector(s.T(), s.Context(ctx), "methods-dialog")
|
||||
options, err := methodsDialog.Elements(".method-option")
|
||||
s.Assert().NoError(err)
|
||||
s.Assert().Len(options, len(s.methods))
|
||||
|
||||
|
|
|
@ -11,27 +11,27 @@ import (
|
|||
)
|
||||
|
||||
type BypassPolicyScenario struct {
|
||||
*SeleniumSuite
|
||||
*RodSuite
|
||||
}
|
||||
|
||||
func NewBypassPolicyScenario() *BypassPolicyScenario {
|
||||
return &BypassPolicyScenario{
|
||||
SeleniumSuite: new(SeleniumSuite),
|
||||
RodSuite: new(RodSuite),
|
||||
}
|
||||
}
|
||||
|
||||
func (s *BypassPolicyScenario) SetupSuite() {
|
||||
wds, err := StartWebDriver()
|
||||
browser, err := StartRod()
|
||||
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
|
||||
s.WebDriverSession = wds
|
||||
s.RodSession = browser
|
||||
}
|
||||
|
||||
func (s *BypassPolicyScenario) TearDownSuite() {
|
||||
err := s.WebDriverSession.Stop()
|
||||
err := s.RodSession.Stop()
|
||||
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
|
@ -39,23 +39,27 @@ func (s *BypassPolicyScenario) TearDownSuite() {
|
|||
}
|
||||
|
||||
func (s *BypassPolicyScenario) SetupTest() {
|
||||
ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second)
|
||||
defer cancel()
|
||||
s.Page = s.doCreateTab(s.T(), HomeBaseURL)
|
||||
s.verifyIsHome(s.T(), s.Page)
|
||||
}
|
||||
|
||||
s.doLogout(ctx, s.T())
|
||||
s.doVisit(s.T(), HomeBaseURL)
|
||||
s.verifyIsHome(ctx, s.T())
|
||||
func (s *BypassPolicyScenario) TearDownTest() {
|
||||
s.collectCoverage(s.Page)
|
||||
s.MustClose()
|
||||
}
|
||||
|
||||
func (s *BypassPolicyScenario) TestShouldAccessPublicResource() {
|
||||
ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second)
|
||||
defer cancel()
|
||||
defer func() {
|
||||
cancel()
|
||||
s.collectScreenshot(ctx.Err(), s.Page)
|
||||
}()
|
||||
|
||||
s.doVisit(s.T(), AdminBaseURL)
|
||||
s.verifyIsFirstFactorPage(ctx, s.T())
|
||||
s.doVisit(s.T(), s.Context(ctx), AdminBaseURL)
|
||||
s.verifyIsFirstFactorPage(s.T(), s.Context(ctx))
|
||||
|
||||
s.doVisit(s.T(), fmt.Sprintf("%s/secret.html", PublicBaseURL))
|
||||
s.verifySecretAuthorized(ctx, s.T())
|
||||
s.doVisit(s.T(), s.Context(ctx), fmt.Sprintf("%s/secret.html", PublicBaseURL))
|
||||
s.verifySecretAuthorized(s.T(), s.Context(ctx))
|
||||
}
|
||||
|
||||
func TestBypassPolicyScenario(t *testing.T) {
|
||||
|
|
|
@ -10,33 +10,31 @@ import (
|
|||
"time"
|
||||
|
||||
mapset "github.com/deckarep/golang-set"
|
||||
"github.com/stretchr/testify/require"
|
||||
"github.com/stretchr/testify/suite"
|
||||
"github.com/tebeka/selenium"
|
||||
)
|
||||
|
||||
type CustomHeadersScenario struct {
|
||||
*SeleniumSuite
|
||||
*RodSuite
|
||||
}
|
||||
|
||||
func NewCustomHeadersScenario() *CustomHeadersScenario {
|
||||
return &CustomHeadersScenario{
|
||||
SeleniumSuite: new(SeleniumSuite),
|
||||
RodSuite: new(RodSuite),
|
||||
}
|
||||
}
|
||||
|
||||
func (s *CustomHeadersScenario) SetupSuite() {
|
||||
wds, err := StartWebDriver()
|
||||
browser, err := StartRod()
|
||||
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
|
||||
s.WebDriverSession = wds
|
||||
s.RodSession = browser
|
||||
}
|
||||
|
||||
func (s *CustomHeadersScenario) TearDownSuite() {
|
||||
err := s.WebDriverSession.Stop()
|
||||
err := s.RodSession.Stop()
|
||||
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
|
@ -44,23 +42,26 @@ func (s *CustomHeadersScenario) TearDownSuite() {
|
|||
}
|
||||
|
||||
func (s *CustomHeadersScenario) SetupTest() {
|
||||
ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second)
|
||||
defer cancel()
|
||||
s.Page = s.doCreateTab(s.T(), HomeBaseURL)
|
||||
s.verifyIsHome(s.T(), s.Page)
|
||||
}
|
||||
|
||||
s.doLogout(ctx, s.T())
|
||||
s.doVisit(s.T(), HomeBaseURL)
|
||||
s.verifyIsHome(ctx, s.T())
|
||||
func (s *CustomHeadersScenario) TearDownTest() {
|
||||
s.collectCoverage(s.Page)
|
||||
s.MustClose()
|
||||
}
|
||||
|
||||
func (s *CustomHeadersScenario) TestShouldNotForwardCustomHeaderForUnauthenticatedUser() {
|
||||
ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second)
|
||||
defer cancel()
|
||||
defer func() {
|
||||
cancel()
|
||||
s.collectScreenshot(ctx.Err(), s.Page)
|
||||
}()
|
||||
|
||||
s.doVisit(s.T(), fmt.Sprintf("%s/headers", PublicBaseURL))
|
||||
s.doVisit(s.T(), s.Context(ctx), fmt.Sprintf("%s/headers", PublicBaseURL))
|
||||
|
||||
body, err := s.WebDriver().FindElement(selenium.ByTagName, "body")
|
||||
body, err := s.Context(ctx).Element("body")
|
||||
s.Assert().NoError(err)
|
||||
s.WaitElementTextContains(ctx, s.T(), body, "\"Host\"")
|
||||
|
||||
b, err := body.Text()
|
||||
s.Assert().NoError(err)
|
||||
|
@ -82,46 +83,46 @@ type HeadersPayload struct {
|
|||
}
|
||||
|
||||
func (s *CustomHeadersScenario) TestShouldForwardCustomHeaderForAuthenticatedUser() {
|
||||
ctx, cancel := context.WithTimeout(context.Background(), 20*time.Second)
|
||||
defer cancel()
|
||||
ctx, cancel := context.WithTimeout(context.Background(), 30*time.Second)
|
||||
defer func() {
|
||||
cancel()
|
||||
s.collectScreenshot(ctx.Err(), s.Page)
|
||||
}()
|
||||
|
||||
expectedGroups := mapset.NewSetWith("dev", "admins")
|
||||
|
||||
targetURL := fmt.Sprintf("%s/headers", PublicBaseURL)
|
||||
s.doLoginOneFactor(ctx, s.T(), "john", "password", false, targetURL)
|
||||
s.verifyURLIs(ctx, s.T(), targetURL)
|
||||
s.doLoginOneFactor(s.T(), s.Context(ctx), "john", "password", false, targetURL)
|
||||
s.verifyIsPublic(s.T(), s.Context(ctx))
|
||||
|
||||
err := s.Wait(ctx, func(d selenium.WebDriver) (bool, error) {
|
||||
body, err := s.WebDriver().FindElement(selenium.ByTagName, "body")
|
||||
if err != nil {
|
||||
return false, err
|
||||
}
|
||||
|
||||
if body == nil {
|
||||
return false, nil
|
||||
}
|
||||
body, err := s.Context(ctx).Element("body")
|
||||
s.Assert().NoError(err)
|
||||
s.Assert().NotNil(body)
|
||||
|
||||
content, err := body.Text()
|
||||
if err != nil {
|
||||
return false, err
|
||||
}
|
||||
s.Assert().NoError(err)
|
||||
s.Assert().NotNil(content)
|
||||
|
||||
payload := HeadersPayload{}
|
||||
if err := json.Unmarshal([]byte(content), &payload); err != nil {
|
||||
return false, err
|
||||
log.Panic(err)
|
||||
}
|
||||
|
||||
groups := strings.Split(payload.Headers.ForwardedGroups, ",")
|
||||
actualGroups := mapset.NewSet()
|
||||
|
||||
for _, group := range groups {
|
||||
actualGroups.Add(group)
|
||||
}
|
||||
|
||||
return strings.Contains(payload.Headers.ForwardedUser, "john") && expectedGroups.Equal(actualGroups) &&
|
||||
strings.Contains(payload.Headers.ForwardedName, "John Doe") && strings.Contains(payload.Headers.ForwardedEmail, "john.doe@authelia.com"), nil
|
||||
})
|
||||
if strings.Contains(payload.Headers.ForwardedUser, "john") && expectedGroups.Equal(actualGroups) &&
|
||||
strings.Contains(payload.Headers.ForwardedName, "John Doe") && strings.Contains(payload.Headers.ForwardedEmail, "john.doe@authelia.com") {
|
||||
err = nil
|
||||
} else {
|
||||
err = fmt.Errorf("headers do not include user information")
|
||||
}
|
||||
|
||||
require.NoError(s.T(), err)
|
||||
s.Require().NoError(err)
|
||||
}
|
||||
|
||||
func TestCustomHeadersScenario(t *testing.T) {
|
||||
|
|
|
@ -11,57 +11,62 @@ import (
|
|||
)
|
||||
|
||||
type DefaultRedirectionURLScenario struct {
|
||||
*SeleniumSuite
|
||||
*RodSuite
|
||||
|
||||
secret string
|
||||
}
|
||||
|
||||
func NewDefaultRedirectionURLScenario() *DefaultRedirectionURLScenario {
|
||||
return &DefaultRedirectionURLScenario{
|
||||
SeleniumSuite: new(SeleniumSuite),
|
||||
RodSuite: new(RodSuite),
|
||||
}
|
||||
}
|
||||
|
||||
func (drus *DefaultRedirectionURLScenario) SetupSuite() {
|
||||
wds, err := StartWebDriver()
|
||||
func (s *DefaultRedirectionURLScenario) SetupSuite() {
|
||||
browser, err := StartRod()
|
||||
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
|
||||
drus.WebDriverSession = wds
|
||||
s.RodSession = browser
|
||||
}
|
||||
|
||||
func (s *DefaultRedirectionURLScenario) TearDownSuite() {
|
||||
err := s.RodSession.Stop()
|
||||
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
}
|
||||
|
||||
func (s *DefaultRedirectionURLScenario) SetupTest() {
|
||||
s.Page = s.doCreateTab(s.T(), HomeBaseURL)
|
||||
s.verifyIsHome(s.T(), s.Page)
|
||||
}
|
||||
|
||||
func (s *DefaultRedirectionURLScenario) TearDownTest() {
|
||||
s.collectCoverage(s.Page)
|
||||
s.MustClose()
|
||||
}
|
||||
|
||||
func (s *DefaultRedirectionURLScenario) TestUserIsRedirectedToDefaultURL() {
|
||||
ctx, cancel := context.WithTimeout(context.Background(), 20*time.Second)
|
||||
defer cancel()
|
||||
defer func() {
|
||||
cancel()
|
||||
s.collectScreenshot(ctx.Err(), s.Page)
|
||||
}()
|
||||
|
||||
targetURL := fmt.Sprintf("%s/secret.html", AdminBaseURL)
|
||||
drus.secret = drus.doRegisterAndLogin2FA(ctx, drus.T(), "john", "password", false, targetURL)
|
||||
drus.verifySecretAuthorized(ctx, drus.T())
|
||||
}
|
||||
|
||||
func (drus *DefaultRedirectionURLScenario) TearDownSuite() {
|
||||
err := drus.WebDriverSession.Stop()
|
||||
s.doVisit(s.T(), s.Context(ctx), HomeBaseURL)
|
||||
s.verifyIsHome(s.T(), s.Page)
|
||||
s.secret = s.doRegisterAndLogin2FA(s.T(), s.Context(ctx), "john", "password", false, targetURL)
|
||||
s.verifySecretAuthorized(s.T(), s.Context(ctx))
|
||||
s.doLogout(s.T(), s.Context(ctx))
|
||||
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
}
|
||||
|
||||
func (drus *DefaultRedirectionURLScenario) SetupTest() {
|
||||
ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second)
|
||||
defer cancel()
|
||||
|
||||
drus.doLogout(ctx, drus.T())
|
||||
drus.doVisit(drus.T(), HomeBaseURL)
|
||||
drus.verifyIsHome(ctx, drus.T())
|
||||
}
|
||||
|
||||
func (drus *DefaultRedirectionURLScenario) TestUserIsRedirectedToDefaultURL() {
|
||||
ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second)
|
||||
defer cancel()
|
||||
|
||||
drus.doLoginTwoFactor(ctx, drus.T(), "john", "password", false, drus.secret, "")
|
||||
drus.verifyURLIs(ctx, drus.T(), HomeBaseURL+"/")
|
||||
s.doLoginTwoFactor(s.T(), s.Context(ctx), "john", "password", false, s.secret, "")
|
||||
s.verifyIsHome(s.T(), s.Page)
|
||||
}
|
||||
|
||||
func TestShouldRunDefaultRedirectionURLScenario(t *testing.T) {
|
||||
|
|
|
@ -11,35 +11,42 @@ import (
|
|||
)
|
||||
|
||||
type InactivityScenario struct {
|
||||
*SeleniumSuite
|
||||
*RodSuite
|
||||
secret string
|
||||
}
|
||||
|
||||
func NewInactivityScenario() *InactivityScenario {
|
||||
return &InactivityScenario{
|
||||
SeleniumSuite: new(SeleniumSuite),
|
||||
RodSuite: new(RodSuite),
|
||||
}
|
||||
}
|
||||
|
||||
func (s *InactivityScenario) SetupSuite() {
|
||||
wds, err := StartWebDriver()
|
||||
browser, err := StartRod()
|
||||
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
|
||||
s.WebDriverSession = wds
|
||||
s.RodSession = browser
|
||||
|
||||
ctx, cancel := context.WithTimeout(context.Background(), 20*time.Second)
|
||||
defer cancel()
|
||||
defer func() {
|
||||
cancel()
|
||||
s.collectScreenshot(ctx.Err(), s.Page)
|
||||
|
||||
s.collectCoverage(s.Page)
|
||||
s.MustClose()
|
||||
}()
|
||||
|
||||
s.Page = s.doCreateTab(s.T(), HomeBaseURL)
|
||||
targetURL := fmt.Sprintf("%s/secret.html", AdminBaseURL)
|
||||
s.secret = s.doRegisterAndLogin2FA(ctx, s.T(), "john", "password", false, targetURL)
|
||||
s.verifySecretAuthorized(ctx, s.T())
|
||||
s.secret = s.doRegisterAndLogin2FA(s.T(), s.Context(ctx), "john", "password", false, targetURL)
|
||||
s.verifySecretAuthorized(s.T(), s.Context(ctx))
|
||||
}
|
||||
|
||||
func (s *InactivityScenario) TearDownSuite() {
|
||||
err := s.WebDriverSession.Stop()
|
||||
err := s.RodSession.Stop()
|
||||
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
|
@ -47,71 +54,78 @@ func (s *InactivityScenario) TearDownSuite() {
|
|||
}
|
||||
|
||||
func (s *InactivityScenario) SetupTest() {
|
||||
ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second)
|
||||
defer cancel()
|
||||
s.Page = s.doCreateTab(s.T(), HomeBaseURL)
|
||||
s.verifyIsHome(s.T(), s.Page)
|
||||
}
|
||||
|
||||
s.doLogout(ctx, s.T())
|
||||
s.doVisit(s.T(), HomeBaseURL)
|
||||
s.verifyIsHome(ctx, s.T())
|
||||
func (s *InactivityScenario) TearDownTest() {
|
||||
s.collectCoverage(s.Page)
|
||||
s.MustClose()
|
||||
}
|
||||
|
||||
func (s *InactivityScenario) TestShouldRequireReauthenticationAfterInactivityPeriod() {
|
||||
ctx, cancel := context.WithTimeout(context.Background(), 20*time.Second)
|
||||
defer cancel()
|
||||
defer func() {
|
||||
cancel()
|
||||
s.collectScreenshot(ctx.Err(), s.Page)
|
||||
}()
|
||||
|
||||
targetURL := fmt.Sprintf("%s/secret.html", AdminBaseURL)
|
||||
|
||||
s.doLoginTwoFactor(ctx, s.T(), "john", "password", false, s.secret, "")
|
||||
s.doVisit(s.T(), HomeBaseURL)
|
||||
s.verifyIsHome(ctx, s.T())
|
||||
s.doLoginTwoFactor(s.T(), s.Context(ctx), "john", "password", false, s.secret, "")
|
||||
s.doVisit(s.T(), s.Context(ctx), HomeBaseURL)
|
||||
s.verifyIsHome(s.T(), s.Context(ctx))
|
||||
|
||||
time.Sleep(6 * time.Second)
|
||||
|
||||
s.doVisit(s.T(), targetURL)
|
||||
s.verifyIsFirstFactorPage(ctx, s.T())
|
||||
s.doVisit(s.T(), s.Context(ctx), targetURL)
|
||||
s.verifyIsFirstFactorPage(s.T(), s.Context(ctx))
|
||||
}
|
||||
|
||||
func (s *InactivityScenario) TestShouldRequireReauthenticationAfterCookieExpiration() {
|
||||
ctx, cancel := context.WithTimeout(context.Background(), 20*time.Second)
|
||||
defer cancel()
|
||||
defer func() {
|
||||
cancel()
|
||||
s.collectScreenshot(ctx.Err(), s.Page)
|
||||
}()
|
||||
|
||||
targetURL := fmt.Sprintf("%s/secret.html", AdminBaseURL)
|
||||
|
||||
s.doLoginTwoFactor(ctx, s.T(), "john", "password", false, s.secret, "")
|
||||
s.doLoginTwoFactor(s.T(), s.Context(ctx), "john", "password", false, s.secret, "")
|
||||
|
||||
for i := 0; i < 3; i++ {
|
||||
s.doVisit(s.T(), HomeBaseURL)
|
||||
s.verifyIsHome(ctx, s.T())
|
||||
s.doVisit(s.T(), s.Context(ctx), HomeBaseURL)
|
||||
s.verifyIsHome(s.T(), s.Context(ctx))
|
||||
|
||||
time.Sleep(2 * time.Second)
|
||||
|
||||
s.doVisit(s.T(), targetURL)
|
||||
s.verifySecretAuthorized(ctx, s.T())
|
||||
s.doVisit(s.T(), s.Context(ctx), targetURL)
|
||||
s.verifySecretAuthorized(s.T(), s.Context(ctx))
|
||||
}
|
||||
|
||||
s.doVisit(s.T(), HomeBaseURL)
|
||||
s.verifyIsHome(ctx, s.T())
|
||||
|
||||
time.Sleep(2 * time.Second)
|
||||
|
||||
s.doVisit(s.T(), targetURL)
|
||||
s.verifyIsFirstFactorPage(ctx, s.T())
|
||||
s.doVisit(s.T(), s.Context(ctx), targetURL)
|
||||
s.verifyIsFirstFactorPage(s.T(), s.Context(ctx))
|
||||
}
|
||||
|
||||
func (s *InactivityScenario) TestShouldDisableCookieExpirationAndInactivity() {
|
||||
ctx, cancel := context.WithTimeout(context.Background(), 20*time.Second)
|
||||
defer cancel()
|
||||
ctx, cancel := context.WithTimeout(context.Background(), 30*time.Second)
|
||||
defer func() {
|
||||
cancel()
|
||||
s.collectScreenshot(ctx.Err(), s.Page)
|
||||
}()
|
||||
|
||||
targetURL := fmt.Sprintf("%s/secret.html", AdminBaseURL)
|
||||
|
||||
s.doLoginTwoFactor(ctx, s.T(), "john", "password", true, s.secret, "")
|
||||
s.doVisit(s.T(), HomeBaseURL)
|
||||
s.verifyIsHome(ctx, s.T())
|
||||
s.doLoginTwoFactor(s.T(), s.Context(ctx), "john", "password", true, s.secret, "")
|
||||
s.doVisit(s.T(), s.Context(ctx), HomeBaseURL)
|
||||
s.verifyIsHome(s.T(), s.Context(ctx))
|
||||
|
||||
time.Sleep(10 * time.Second)
|
||||
|
||||
s.doVisit(s.T(), targetURL)
|
||||
s.verifySecretAuthorized(ctx, s.T())
|
||||
s.doVisit(s.T(), s.Context(ctx), targetURL)
|
||||
s.verifySecretAuthorized(s.T(), s.Context(ctx))
|
||||
}
|
||||
|
||||
func TestInactivityScenario(t *testing.T) {
|
||||
|
|
|
@ -12,33 +12,40 @@ import (
|
|||
)
|
||||
|
||||
type OIDCScenario struct {
|
||||
*SeleniumSuite
|
||||
*RodSuite
|
||||
secret string
|
||||
}
|
||||
|
||||
func NewOIDCScenario() *OIDCScenario {
|
||||
return &OIDCScenario{
|
||||
SeleniumSuite: new(SeleniumSuite),
|
||||
RodSuite: new(RodSuite),
|
||||
}
|
||||
}
|
||||
|
||||
func (s *OIDCScenario) SetupSuite() {
|
||||
wds, err := StartWebDriver()
|
||||
browser, err := StartRod()
|
||||
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
|
||||
s.WebDriverSession = wds
|
||||
s.RodSession = browser
|
||||
|
||||
ctx, cancel := context.WithTimeout(context.Background(), 20*time.Second)
|
||||
defer cancel()
|
||||
ctx, cancel := context.WithTimeout(context.Background(), 30*time.Second)
|
||||
defer func() {
|
||||
cancel()
|
||||
s.collectScreenshot(ctx.Err(), s.Page)
|
||||
|
||||
s.secret = s.doRegisterAndLogin2FA(ctx, s.T(), "john", "password", false, AdminBaseURL)
|
||||
s.collectCoverage(s.Page)
|
||||
s.MustClose()
|
||||
}()
|
||||
|
||||
s.Page = s.doCreateTab(s.T(), HomeBaseURL)
|
||||
s.secret = s.doRegisterAndLogin2FA(s.T(), s.Context(ctx), "john", "password", false, AdminBaseURL)
|
||||
}
|
||||
|
||||
func (s *OIDCScenario) TearDownSuite() {
|
||||
err := s.WebDriverSession.Stop()
|
||||
err := s.RodSession.Stop()
|
||||
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
|
@ -47,65 +54,73 @@ func (s *OIDCScenario) TearDownSuite() {
|
|||
|
||||
func (s *OIDCScenario) SetupTest() {
|
||||
ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second)
|
||||
defer cancel()
|
||||
defer func() {
|
||||
cancel()
|
||||
s.collectScreenshot(ctx.Err(), s.Page)
|
||||
}()
|
||||
|
||||
s.doVisit(s.T(), fmt.Sprintf("%s/logout", OIDCBaseURL))
|
||||
s.doLogout(ctx, s.T())
|
||||
s.doVisit(s.T(), HomeBaseURL)
|
||||
s.verifyIsHome(ctx, s.T())
|
||||
s.Page = s.doCreateTab(s.T(), fmt.Sprintf("%s/logout", OIDCBaseURL))
|
||||
s.doVisit(s.T(), s.Context(ctx), HomeBaseURL)
|
||||
s.verifyIsHome(s.T(), s.Context(ctx))
|
||||
}
|
||||
|
||||
func (s *OIDCScenario) TearDownTest() {
|
||||
s.collectCoverage(s.Page)
|
||||
s.MustClose()
|
||||
}
|
||||
|
||||
func (s *OIDCScenario) TestShouldAuthorizeAccessToOIDCApp() {
|
||||
ctx, cancel := context.WithTimeout(context.Background(), 30*time.Second)
|
||||
defer cancel()
|
||||
defer func() {
|
||||
cancel()
|
||||
s.collectScreenshot(ctx.Err(), s.Page)
|
||||
}()
|
||||
|
||||
s.doVisit(s.T(), OIDCBaseURL)
|
||||
s.verifyIsFirstFactorPage(ctx, s.T())
|
||||
s.doFillLoginPageAndClick(ctx, s.T(), "john", "password", false)
|
||||
s.verifyIsSecondFactorPage(ctx, s.T())
|
||||
s.doValidateTOTP(ctx, s.T(), s.secret)
|
||||
time.Sleep(2 * time.Second)
|
||||
s.doVisit(s.T(), s.Context(ctx), OIDCBaseURL)
|
||||
s.verifyIsFirstFactorPage(s.T(), s.Context(ctx))
|
||||
s.doFillLoginPageAndClick(s.T(), s.Context(ctx), "john", "password", false)
|
||||
s.verifyIsSecondFactorPage(s.T(), s.Context(ctx))
|
||||
s.doValidateTOTP(s.T(), s.Context(ctx), s.secret)
|
||||
|
||||
s.waitBodyContains(ctx, s.T(), "Not logged yet...")
|
||||
s.waitBodyContains(s.T(), s.Context(ctx), "Not logged yet...")
|
||||
|
||||
// this href represents the 'login' link
|
||||
err := s.WaitElementLocatedByTagName(ctx, s.T(), "a").Click()
|
||||
// Search for the 'login' link
|
||||
err := s.Page.MustSearch("Log in").Click("left")
|
||||
assert.NoError(s.T(), err)
|
||||
|
||||
s.verifyIsConsentPage(ctx, s.T())
|
||||
|
||||
err = s.WaitElementLocatedByID(ctx, s.T(), "accept-button").Click()
|
||||
s.verifyIsConsentPage(s.T(), s.Context(ctx))
|
||||
err = s.WaitElementLocatedByCSSSelector(s.T(), s.Context(ctx), "accept-button").Click("left")
|
||||
assert.NoError(s.T(), err)
|
||||
|
||||
// Verify that the app is showing the info related to the user stored in the JWT token
|
||||
time.Sleep(2 * time.Second)
|
||||
s.waitBodyContains(ctx, s.T(), "Logged in as john!")
|
||||
s.waitBodyContains(s.T(), s.Context(ctx), "Logged in as john!")
|
||||
}
|
||||
|
||||
func (s *OIDCScenario) TestShouldDenyConsent() {
|
||||
ctx, cancel := context.WithTimeout(context.Background(), 30*time.Second)
|
||||
defer cancel()
|
||||
defer func() {
|
||||
cancel()
|
||||
s.collectScreenshot(ctx.Err(), s.Page)
|
||||
}()
|
||||
|
||||
s.doVisit(s.T(), OIDCBaseURL)
|
||||
s.verifyIsFirstFactorPage(ctx, s.T())
|
||||
s.doFillLoginPageAndClick(ctx, s.T(), "john", "password", false)
|
||||
s.verifyIsSecondFactorPage(ctx, s.T())
|
||||
s.doValidateTOTP(ctx, s.T(), s.secret)
|
||||
time.Sleep(1 * time.Second)
|
||||
s.doVisit(s.T(), s.Context(ctx), OIDCBaseURL)
|
||||
s.verifyIsFirstFactorPage(s.T(), s.Context(ctx))
|
||||
s.doFillLoginPageAndClick(s.T(), s.Context(ctx), "john", "password", false)
|
||||
s.verifyIsSecondFactorPage(s.T(), s.Context(ctx))
|
||||
s.doValidateTOTP(s.T(), s.Context(ctx), s.secret)
|
||||
|
||||
s.waitBodyContains(ctx, s.T(), "Not logged yet...")
|
||||
s.waitBodyContains(s.T(), s.Context(ctx), "Not logged yet...")
|
||||
|
||||
// this href represents the 'login' link
|
||||
err := s.WaitElementLocatedByTagName(ctx, s.T(), "a").Click()
|
||||
// Search for the 'login' link
|
||||
err := s.Page.MustSearch("Log in").Click("left")
|
||||
assert.NoError(s.T(), err)
|
||||
|
||||
s.verifyIsConsentPage(ctx, s.T())
|
||||
s.verifyIsConsentPage(s.T(), s.Context(ctx))
|
||||
|
||||
err = s.WaitElementLocatedByID(ctx, s.T(), "deny-button").Click()
|
||||
err = s.WaitElementLocatedByCSSSelector(s.T(), s.Context(ctx), "deny-button").Click("left")
|
||||
assert.NoError(s.T(), err)
|
||||
|
||||
time.Sleep(1 * time.Second)
|
||||
s.verifyURLIs(ctx, s.T(), "https://oidc.example.com:8080/oauth2/callback?error=access_denied&error_description=User%20has%20rejected%20the%20scopes")
|
||||
s.verifyIsOIDC(s.T(), s.Context(ctx), "oauth2:", "https://oidc.example.com:8080/oauth2/callback?error=access_denied&error_description=User%20has%20rejected%20the%20scopes")
|
||||
}
|
||||
|
||||
func TestRunOIDCScenario(t *testing.T) {
|
||||
|
|
|
@ -11,27 +11,27 @@ import (
|
|||
)
|
||||
|
||||
type OneFactorSuite struct {
|
||||
*SeleniumSuite
|
||||
*RodSuite
|
||||
}
|
||||
|
||||
func NewOneFactorScenario() *OneFactorSuite {
|
||||
return &OneFactorSuite{
|
||||
SeleniumSuite: new(SeleniumSuite),
|
||||
RodSuite: new(RodSuite),
|
||||
}
|
||||
}
|
||||
|
||||
func (s *OneFactorSuite) SetupSuite() {
|
||||
wds, err := StartWebDriver()
|
||||
browser, err := StartRod()
|
||||
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
|
||||
s.WebDriverSession = wds
|
||||
s.RodSession = browser
|
||||
}
|
||||
|
||||
func (s *OneFactorSuite) TearDownSuite() {
|
||||
err := s.WebDriverSession.Stop()
|
||||
err := s.RodSession.Stop()
|
||||
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
|
@ -39,40 +39,50 @@ func (s *OneFactorSuite) TearDownSuite() {
|
|||
}
|
||||
|
||||
func (s *OneFactorSuite) SetupTest() {
|
||||
ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second)
|
||||
defer cancel()
|
||||
s.Page = s.doCreateTab(s.T(), HomeBaseURL)
|
||||
s.verifyIsHome(s.T(), s.Page)
|
||||
}
|
||||
|
||||
s.doLogout(ctx, s.T())
|
||||
s.doVisit(s.T(), HomeBaseURL)
|
||||
s.verifyIsHome(ctx, s.T())
|
||||
func (s *OneFactorSuite) TearDownTest() {
|
||||
s.collectCoverage(s.Page)
|
||||
s.MustClose()
|
||||
}
|
||||
|
||||
func (s *OneFactorSuite) TestShouldAuthorizeSecretAfterOneFactor() {
|
||||
ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second)
|
||||
defer cancel()
|
||||
defer func() {
|
||||
cancel()
|
||||
s.collectScreenshot(ctx.Err(), s.Page)
|
||||
}()
|
||||
|
||||
targetURL := fmt.Sprintf("%s/secret.html", SingleFactorBaseURL)
|
||||
s.doLoginOneFactor(ctx, s.T(), "john", "password", false, targetURL)
|
||||
s.verifySecretAuthorized(ctx, s.T())
|
||||
s.doLoginOneFactor(s.T(), s.Context(ctx), "john", "password", false, targetURL)
|
||||
s.verifySecretAuthorized(s.T(), s.Page)
|
||||
}
|
||||
|
||||
func (s *OneFactorSuite) TestShouldRedirectToSecondFactor() {
|
||||
ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second)
|
||||
defer cancel()
|
||||
defer func() {
|
||||
cancel()
|
||||
s.collectScreenshot(ctx.Err(), s.Page)
|
||||
}()
|
||||
|
||||
targetURL := fmt.Sprintf("%s/secret.html", AdminBaseURL)
|
||||
s.doLoginOneFactor(ctx, s.T(), "john", "password", false, targetURL)
|
||||
s.verifyIsSecondFactorPage(ctx, s.T())
|
||||
s.doLoginOneFactor(s.T(), s.Context(ctx), "john", "password", false, targetURL)
|
||||
s.verifyIsSecondFactorPage(s.T(), s.Context(ctx))
|
||||
}
|
||||
|
||||
func (s *OneFactorSuite) TestShouldDenyAccessOnBadPassword() {
|
||||
ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second)
|
||||
defer cancel()
|
||||
defer func() {
|
||||
cancel()
|
||||
s.collectScreenshot(ctx.Err(), s.Page)
|
||||
}()
|
||||
|
||||
targetURL := fmt.Sprintf("%s/secret.html", AdminBaseURL)
|
||||
s.doLoginOneFactor(ctx, s.T(), "john", "bad-password", false, targetURL)
|
||||
s.verifyIsFirstFactorPage(ctx, s.T())
|
||||
s.verifyNotificationDisplayed(ctx, s.T(), "Incorrect username or password.")
|
||||
s.doLoginOneFactor(s.T(), s.Context(ctx), "john", "bad-password", false, targetURL)
|
||||
s.verifyIsFirstFactorPage(s.T(), s.Context(ctx))
|
||||
s.verifyNotificationDisplayed(s.T(), s.Context(ctx), "Incorrect username or password.")
|
||||
}
|
||||
|
||||
func TestRunOneFactor(t *testing.T) {
|
||||
|
|
|
@ -10,25 +10,25 @@ import (
|
|||
)
|
||||
|
||||
type PasswordComplexityScenario struct {
|
||||
*SeleniumSuite
|
||||
*RodSuite
|
||||
}
|
||||
|
||||
func NewPasswordComplexityScenario() *PasswordComplexityScenario {
|
||||
return &PasswordComplexityScenario{SeleniumSuite: new(SeleniumSuite)}
|
||||
return &PasswordComplexityScenario{RodSuite: new(RodSuite)}
|
||||
}
|
||||
|
||||
func (s *PasswordComplexityScenario) SetupSuite() {
|
||||
wds, err := StartWebDriver()
|
||||
browser, err := StartRod()
|
||||
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
|
||||
s.WebDriverSession = wds
|
||||
s.RodSession = browser
|
||||
}
|
||||
|
||||
func (s *PasswordComplexityScenario) TearDownSuite() {
|
||||
err := s.WebDriverSession.Stop()
|
||||
err := s.RodSession.Stop()
|
||||
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
|
@ -36,24 +36,28 @@ func (s *PasswordComplexityScenario) TearDownSuite() {
|
|||
}
|
||||
|
||||
func (s *PasswordComplexityScenario) SetupTest() {
|
||||
ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second)
|
||||
defer cancel()
|
||||
s.Page = s.doCreateTab(s.T(), HomeBaseURL)
|
||||
s.verifyIsHome(s.T(), s.Page)
|
||||
}
|
||||
|
||||
s.doLogout(ctx, s.T())
|
||||
s.doVisit(s.T(), HomeBaseURL)
|
||||
s.verifyIsHome(ctx, s.T())
|
||||
func (s *PasswordComplexityScenario) TearDownTest() {
|
||||
s.collectCoverage(s.Page)
|
||||
s.MustClose()
|
||||
}
|
||||
|
||||
func (s *PasswordComplexityScenario) TestShouldRejectPasswordReset() {
|
||||
ctx, cancel := context.WithTimeout(context.Background(), 20*time.Second)
|
||||
defer cancel()
|
||||
ctx, cancel := context.WithTimeout(context.Background(), 30*time.Second)
|
||||
defer func() {
|
||||
cancel()
|
||||
s.collectScreenshot(ctx.Err(), s.Page)
|
||||
}()
|
||||
|
||||
s.doVisit(s.T(), GetLoginBaseURL())
|
||||
s.verifyIsFirstFactorPage(ctx, s.T())
|
||||
s.doVisit(s.T(), s.Context(ctx), GetLoginBaseURL())
|
||||
s.verifyIsFirstFactorPage(s.T(), s.Context(ctx))
|
||||
|
||||
// Attempt to reset the password to a
|
||||
s.doResetPassword(ctx, s.T(), "john", "a", "a", true)
|
||||
s.verifyNotificationDisplayed(ctx, s.T(), "Your supplied password does not meet the password policy requirements.")
|
||||
s.doResetPassword(s.T(), s.Context(ctx), "john", "a", "a", true)
|
||||
s.verifyNotificationDisplayed(s.T(), s.Context(ctx), "Your supplied password does not meet the password policy requirements.")
|
||||
}
|
||||
|
||||
func TestRunPasswordComplexityScenario(t *testing.T) {
|
||||
|
|
|
@ -10,27 +10,27 @@ import (
|
|||
)
|
||||
|
||||
type RedirectionCheckScenario struct {
|
||||
*SeleniumSuite
|
||||
*RodSuite
|
||||
}
|
||||
|
||||
func NewRedirectionCheckScenario() *RedirectionCheckScenario {
|
||||
return &RedirectionCheckScenario{
|
||||
SeleniumSuite: new(SeleniumSuite),
|
||||
RodSuite: new(RodSuite),
|
||||
}
|
||||
}
|
||||
|
||||
func (s *RedirectionCheckScenario) SetupSuite() {
|
||||
wds, err := StartWebDriver()
|
||||
browser, err := StartRod()
|
||||
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
|
||||
s.WebDriverSession = wds
|
||||
s.RodSession = browser
|
||||
}
|
||||
|
||||
func (s *RedirectionCheckScenario) TearDownSuite() {
|
||||
err := s.WebDriverSession.Stop()
|
||||
err := s.RodSession.Stop()
|
||||
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
|
@ -38,12 +38,13 @@ func (s *RedirectionCheckScenario) TearDownSuite() {
|
|||
}
|
||||
|
||||
func (s *RedirectionCheckScenario) SetupTest() {
|
||||
ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second)
|
||||
defer cancel()
|
||||
s.Page = s.doCreateTab(s.T(), HomeBaseURL)
|
||||
s.verifyIsHome(s.T(), s.Page)
|
||||
}
|
||||
|
||||
s.doLogout(ctx, s.T())
|
||||
s.doVisit(s.T(), HomeBaseURL)
|
||||
s.verifyIsHome(ctx, s.T())
|
||||
func (s *RedirectionCheckScenario) TearDownTest() {
|
||||
s.collectCoverage(s.Page)
|
||||
s.MustClose()
|
||||
}
|
||||
|
||||
var redirectionAuthorizations = map[string]bool{
|
||||
|
@ -59,21 +60,24 @@ var redirectionAuthorizations = map[string]bool{
|
|||
|
||||
func (s *RedirectionCheckScenario) TestShouldRedirectOnLoginOnlyWhenDomainIsSafe() {
|
||||
ctx, cancel := context.WithTimeout(context.Background(), 35*time.Second)
|
||||
defer cancel()
|
||||
defer func() {
|
||||
cancel()
|
||||
s.collectScreenshot(ctx.Err(), s.Page)
|
||||
}()
|
||||
|
||||
secret := s.doRegisterThenLogout(ctx, s.T(), "john", "password")
|
||||
secret := s.doRegisterThenLogout(s.T(), s.Context(ctx), "john", "password")
|
||||
|
||||
for url, redirected := range redirectionAuthorizations {
|
||||
s.T().Run(url, func(t *testing.T) {
|
||||
s.doLoginTwoFactor(ctx, t, "john", "password", false, secret, url)
|
||||
s.doLoginTwoFactor(t, s.Context(ctx), "john", "password", false, secret, url)
|
||||
|
||||
if redirected {
|
||||
s.verifySecretAuthorized(ctx, t)
|
||||
s.verifySecretAuthorized(t, s.Context(ctx))
|
||||
} else {
|
||||
s.verifyIsAuthenticatedPage(ctx, t)
|
||||
s.verifyIsAuthenticatedPage(t, s.Context(ctx))
|
||||
}
|
||||
|
||||
s.doLogout(ctx, t)
|
||||
s.doLogout(t, s.Context(ctx))
|
||||
})
|
||||
}
|
||||
}
|
||||
|
@ -91,11 +95,14 @@ var logoutRedirectionURLs = map[string]bool{
|
|||
|
||||
func (s *RedirectionCheckScenario) TestShouldRedirectOnLogoutOnlyWhenDomainIsSafe() {
|
||||
ctx, cancel := context.WithTimeout(context.Background(), 30*time.Second)
|
||||
defer cancel()
|
||||
defer func() {
|
||||
cancel()
|
||||
s.collectScreenshot(ctx.Err(), s.Page)
|
||||
}()
|
||||
|
||||
for url, success := range logoutRedirectionURLs {
|
||||
s.T().Run(url, func(t *testing.T) {
|
||||
s.doLogoutWithRedirect(ctx, t, url, !success)
|
||||
s.doLogoutWithRedirect(t, s.Context(ctx), url, !success)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
|
|
@ -11,50 +11,54 @@ import (
|
|||
)
|
||||
|
||||
type RedirectionURLScenario struct {
|
||||
*SeleniumSuite
|
||||
*RodSuite
|
||||
}
|
||||
|
||||
func NewRedirectionURLScenario() *RedirectionURLScenario {
|
||||
return &RedirectionURLScenario{
|
||||
SeleniumSuite: new(SeleniumSuite),
|
||||
RodSuite: new(RodSuite),
|
||||
}
|
||||
}
|
||||
|
||||
func (rus *RedirectionURLScenario) SetupSuite() {
|
||||
wds, err := StartWebDriver()
|
||||
func (s *RedirectionURLScenario) SetupSuite() {
|
||||
browser, err := StartRod()
|
||||
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
|
||||
rus.WebDriverSession = wds
|
||||
s.RodSession = browser
|
||||
}
|
||||
|
||||
func (rus *RedirectionURLScenario) TearDownSuite() {
|
||||
err := rus.WebDriverSession.Stop()
|
||||
func (s *RedirectionURLScenario) TearDownSuite() {
|
||||
err := s.RodSession.Stop()
|
||||
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
}
|
||||
|
||||
func (rus *RedirectionURLScenario) SetupTest() {
|
||||
ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second)
|
||||
defer cancel()
|
||||
|
||||
rus.doLogout(ctx, rus.T())
|
||||
rus.doVisit(rus.T(), HomeBaseURL)
|
||||
rus.verifyIsHome(ctx, rus.T())
|
||||
func (s *RedirectionURLScenario) SetupTest() {
|
||||
s.Page = s.doCreateTab(s.T(), HomeBaseURL)
|
||||
s.verifyIsHome(s.T(), s.Page)
|
||||
}
|
||||
|
||||
func (rus *RedirectionURLScenario) TestShouldVerifyCustomURLParametersArePropagatedAfterRedirection() {
|
||||
func (s *RedirectionURLScenario) TearDownTest() {
|
||||
s.collectCoverage(s.Page)
|
||||
s.MustClose()
|
||||
}
|
||||
|
||||
func (s *RedirectionURLScenario) TestShouldVerifyCustomURLParametersArePropagatedAfterRedirection() {
|
||||
ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second)
|
||||
defer cancel()
|
||||
defer func() {
|
||||
cancel()
|
||||
s.collectScreenshot(ctx.Err(), s.Page)
|
||||
}()
|
||||
|
||||
targetURL := fmt.Sprintf("%s/secret.html?myparam=test", SingleFactorBaseURL)
|
||||
rus.doLoginOneFactor(ctx, rus.T(), "john", "password", false, targetURL)
|
||||
rus.verifySecretAuthorized(ctx, rus.T())
|
||||
rus.verifyURLIs(ctx, rus.T(), targetURL)
|
||||
s.doLoginOneFactor(s.T(), s.Context(ctx), "john", "password", false, targetURL)
|
||||
s.verifySecretAuthorized(s.T(), s.Context(ctx))
|
||||
s.verifyURLIs(s.T(), s.Context(ctx), targetURL)
|
||||
}
|
||||
|
||||
func TestRedirectionURLScenario(t *testing.T) {
|
||||
|
|
|
@ -11,27 +11,27 @@ import (
|
|||
)
|
||||
|
||||
type RegulationScenario struct {
|
||||
*SeleniumSuite
|
||||
*RodSuite
|
||||
}
|
||||
|
||||
func NewRegulationScenario() *RegulationScenario {
|
||||
return &RegulationScenario{
|
||||
SeleniumSuite: new(SeleniumSuite),
|
||||
RodSuite: new(RodSuite),
|
||||
}
|
||||
}
|
||||
|
||||
func (s *RegulationScenario) SetupSuite() {
|
||||
wds, err := StartWebDriver()
|
||||
browser, err := StartRod()
|
||||
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
|
||||
s.WebDriverSession = wds
|
||||
s.RodSession = browser
|
||||
}
|
||||
|
||||
func (s *RegulationScenario) TearDownSuite() {
|
||||
err := s.WebDriverSession.Stop()
|
||||
err := s.RodSession.Stop()
|
||||
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
|
@ -39,47 +39,49 @@ func (s *RegulationScenario) TearDownSuite() {
|
|||
}
|
||||
|
||||
func (s *RegulationScenario) SetupTest() {
|
||||
ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second)
|
||||
defer cancel()
|
||||
s.Page = s.doCreateTab(s.T(), HomeBaseURL)
|
||||
s.verifyIsHome(s.T(), s.Page)
|
||||
}
|
||||
|
||||
s.doLogout(ctx, s.T())
|
||||
s.doVisit(s.T(), HomeBaseURL)
|
||||
s.verifyIsHome(ctx, s.T())
|
||||
func (s *RegulationScenario) TearDownTest() {
|
||||
s.collectCoverage(s.Page)
|
||||
s.MustClose()
|
||||
}
|
||||
|
||||
func (s *RegulationScenario) TestShouldBanUserAfterTooManyAttempt() {
|
||||
ctx, cancel := context.WithTimeout(context.Background(), 45*time.Second)
|
||||
defer cancel()
|
||||
defer func() {
|
||||
cancel()
|
||||
s.collectScreenshot(ctx.Err(), s.Page)
|
||||
}()
|
||||
|
||||
s.doVisitLoginPage(ctx, s.T(), "")
|
||||
s.doFillLoginPageAndClick(ctx, s.T(), "john", "bad-password", false)
|
||||
s.verifyNotificationDisplayed(ctx, s.T(), "Incorrect username or password.")
|
||||
s.doVisitLoginPage(s.T(), s.Context(ctx), "")
|
||||
s.doFillLoginPageAndClick(s.T(), s.Context(ctx), "john", "bad-password", false)
|
||||
s.verifyNotificationDisplayed(s.T(), s.Context(ctx), "Incorrect username or password.")
|
||||
|
||||
for i := 0; i < 3; i++ {
|
||||
err := s.WaitElementLocatedByID(ctx, s.T(), "password-textfield").SendKeys("bad-password")
|
||||
err := s.WaitElementLocatedByCSSSelector(s.T(), s.Context(ctx), "password-textfield").Input("bad-password")
|
||||
require.NoError(s.T(), err)
|
||||
err = s.WaitElementLocatedByID(ctx, s.T(), "sign-in-button").Click()
|
||||
err = s.WaitElementLocatedByCSSSelector(s.T(), s.Context(ctx), "sign-in-button").Click("left")
|
||||
require.NoError(s.T(), err)
|
||||
time.Sleep(1 * time.Second)
|
||||
}
|
||||
|
||||
// Enter the correct password and test the regulation lock out
|
||||
err := s.WaitElementLocatedByID(ctx, s.T(), "password-textfield").SendKeys("password")
|
||||
err := s.WaitElementLocatedByCSSSelector(s.T(), s.Context(ctx), "password-textfield").Input("password")
|
||||
require.NoError(s.T(), err)
|
||||
err = s.WaitElementLocatedByID(ctx, s.T(), "sign-in-button").Click()
|
||||
err = s.WaitElementLocatedByCSSSelector(s.T(), s.Context(ctx), "sign-in-button").Click("left")
|
||||
require.NoError(s.T(), err)
|
||||
s.verifyNotificationDisplayed(ctx, s.T(), "Incorrect username or password.")
|
||||
s.verifyNotificationDisplayed(s.T(), s.Context(ctx), "Incorrect username or password.")
|
||||
|
||||
time.Sleep(1 * time.Second)
|
||||
s.verifyIsFirstFactorPage(ctx, s.T())
|
||||
time.Sleep(9 * time.Second)
|
||||
s.verifyIsFirstFactorPage(s.T(), s.Context(ctx))
|
||||
time.Sleep(10 * time.Second)
|
||||
|
||||
// Enter the correct password and test a successful login
|
||||
err = s.WaitElementLocatedByID(ctx, s.T(), "password-textfield").SendKeys("password")
|
||||
err = s.WaitElementLocatedByCSSSelector(s.T(), s.Context(ctx), "password-textfield").Input("password")
|
||||
require.NoError(s.T(), err)
|
||||
err = s.WaitElementLocatedByID(ctx, s.T(), "sign-in-button").Click()
|
||||
err = s.WaitElementLocatedByCSSSelector(s.T(), s.Context(ctx), "sign-in-button").Click("left")
|
||||
require.NoError(s.T(), err)
|
||||
s.verifyIsSecondFactorPage(ctx, s.T())
|
||||
s.verifyIsSecondFactorPage(s.T(), s.Context(ctx))
|
||||
}
|
||||
|
||||
func TestBlacklistingScenario(t *testing.T) {
|
||||
|
|
|
@ -10,25 +10,25 @@ import (
|
|||
)
|
||||
|
||||
type ResetPasswordScenario struct {
|
||||
*SeleniumSuite
|
||||
*RodSuite
|
||||
}
|
||||
|
||||
func NewResetPasswordScenario() *ResetPasswordScenario {
|
||||
return &ResetPasswordScenario{SeleniumSuite: new(SeleniumSuite)}
|
||||
return &ResetPasswordScenario{RodSuite: new(RodSuite)}
|
||||
}
|
||||
|
||||
func (s *ResetPasswordScenario) SetupSuite() {
|
||||
wds, err := StartWebDriver()
|
||||
browser, err := StartRod()
|
||||
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
|
||||
s.WebDriverSession = wds
|
||||
s.RodSession = browser
|
||||
}
|
||||
|
||||
func (s *ResetPasswordScenario) TearDownSuite() {
|
||||
err := s.WebDriverSession.Stop()
|
||||
err := s.RodSession.Stop()
|
||||
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
|
@ -36,64 +36,74 @@ func (s *ResetPasswordScenario) TearDownSuite() {
|
|||
}
|
||||
|
||||
func (s *ResetPasswordScenario) SetupTest() {
|
||||
ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second)
|
||||
defer cancel()
|
||||
s.Page = s.doCreateTab(s.T(), HomeBaseURL)
|
||||
s.verifyIsHome(s.T(), s.Page)
|
||||
}
|
||||
|
||||
s.doLogout(ctx, s.T())
|
||||
s.doVisit(s.T(), HomeBaseURL)
|
||||
s.verifyIsHome(ctx, s.T())
|
||||
func (s *ResetPasswordScenario) TearDownTest() {
|
||||
s.collectCoverage(s.Page)
|
||||
s.MustClose()
|
||||
}
|
||||
|
||||
func (s *ResetPasswordScenario) TestShouldResetPassword() {
|
||||
ctx, cancel := context.WithTimeout(context.Background(), 20*time.Second)
|
||||
defer cancel()
|
||||
ctx, cancel := context.WithTimeout(context.Background(), 45*time.Second)
|
||||
defer func() {
|
||||
cancel()
|
||||
s.collectScreenshot(ctx.Err(), s.Page)
|
||||
}()
|
||||
|
||||
s.doVisit(s.T(), GetLoginBaseURL())
|
||||
s.verifyIsFirstFactorPage(ctx, s.T())
|
||||
s.doVisit(s.T(), s.Context(ctx), GetLoginBaseURL())
|
||||
s.verifyIsFirstFactorPage(s.T(), s.Context(ctx))
|
||||
|
||||
// Reset the password to abc
|
||||
s.doResetPassword(ctx, s.T(), "john", "abc", "abc", false)
|
||||
s.doResetPassword(s.T(), s.Context(ctx), "john", "abc", "abc", false)
|
||||
|
||||
// Try to login with the old password
|
||||
s.doLoginOneFactor(ctx, s.T(), "john", "password", false, "")
|
||||
s.verifyNotificationDisplayed(ctx, s.T(), "Incorrect username or password.")
|
||||
s.doLoginOneFactor(s.T(), s.Context(ctx), "john", "password", false, "")
|
||||
s.verifyNotificationDisplayed(s.T(), s.Context(ctx), "Incorrect username or password.")
|
||||
|
||||
// Try to login with the new password
|
||||
s.doLoginOneFactor(ctx, s.T(), "john", "abc", false, "")
|
||||
s.doLoginOneFactor(s.T(), s.Context(ctx), "john", "abc", false, "")
|
||||
|
||||
// Logout
|
||||
s.doLogout(ctx, s.T())
|
||||
s.doLogout(s.T(), s.Context(ctx))
|
||||
|
||||
// Reset the original password
|
||||
s.doResetPassword(ctx, s.T(), "john", "password", "password", false)
|
||||
s.doResetPassword(s.T(), s.Context(ctx), "john", "password", "password", false)
|
||||
}
|
||||
|
||||
func (s *ResetPasswordScenario) TestShouldMakeAttackerThinkPasswordResetIsInitiated() {
|
||||
ctx, cancel := context.WithTimeout(context.Background(), 20*time.Second)
|
||||
defer cancel()
|
||||
defer func() {
|
||||
cancel()
|
||||
s.collectScreenshot(ctx.Err(), s.Page)
|
||||
}()
|
||||
|
||||
s.doVisit(s.T(), GetLoginBaseURL())
|
||||
s.verifyIsFirstFactorPage(ctx, s.T())
|
||||
s.doVisit(s.T(), s.Context(ctx), GetLoginBaseURL())
|
||||
s.verifyIsFirstFactorPage(s.T(), s.Context(ctx))
|
||||
|
||||
// Try to initiate a password reset of an nonexistent user.
|
||||
s.doInitiatePasswordReset(ctx, s.T(), "i_dont_exist")
|
||||
s.doInitiatePasswordReset(s.T(), s.Context(ctx), "i_dont_exist")
|
||||
|
||||
// Check that the notification make the attacker thinks the process is initiated
|
||||
s.verifyMailNotificationDisplayed(ctx, s.T())
|
||||
s.verifyMailNotificationDisplayed(s.T(), s.Context(ctx))
|
||||
}
|
||||
|
||||
func (s *ResetPasswordScenario) TestShouldLetUserNoticeThereIsAPasswordMismatch() {
|
||||
ctx, cancel := context.WithTimeout(context.Background(), 20*time.Second)
|
||||
defer cancel()
|
||||
ctx, cancel := context.WithTimeout(context.Background(), 30*time.Second)
|
||||
defer func() {
|
||||
cancel()
|
||||
s.collectScreenshot(ctx.Err(), s.Page)
|
||||
}()
|
||||
|
||||
s.doVisit(s.T(), GetLoginBaseURL())
|
||||
s.verifyIsFirstFactorPage(ctx, s.T())
|
||||
s.doVisit(s.T(), s.Context(ctx), GetLoginBaseURL())
|
||||
s.verifyIsFirstFactorPage(s.T(), s.Context(ctx))
|
||||
|
||||
s.doInitiatePasswordReset(ctx, s.T(), "john")
|
||||
s.verifyMailNotificationDisplayed(ctx, s.T())
|
||||
s.doInitiatePasswordReset(s.T(), s.Context(ctx), "john")
|
||||
s.verifyMailNotificationDisplayed(s.T(), s.Context(ctx))
|
||||
|
||||
s.doCompletePasswordReset(ctx, s.T(), "password", "another_password")
|
||||
s.verifyNotificationDisplayed(ctx, s.T(), "Passwords do not match.")
|
||||
s.doCompletePasswordReset(s.T(), s.Context(ctx), "password", "another_password")
|
||||
s.verifyNotificationDisplayed(s.T(), s.Context(ctx), "Passwords do not match.")
|
||||
}
|
||||
|
||||
func TestRunResetPasswordScenario(t *testing.T) {
|
||||
|
|
|
@ -13,27 +13,27 @@ import (
|
|||
)
|
||||
|
||||
type SigninEmailScenario struct {
|
||||
*SeleniumSuite
|
||||
*RodSuite
|
||||
}
|
||||
|
||||
func NewSigninEmailScenario() *SigninEmailScenario {
|
||||
return &SigninEmailScenario{
|
||||
SeleniumSuite: new(SeleniumSuite),
|
||||
RodSuite: new(RodSuite),
|
||||
}
|
||||
}
|
||||
|
||||
func (s *SigninEmailScenario) SetupSuite() {
|
||||
wds, err := StartWebDriver()
|
||||
browser, err := StartRod()
|
||||
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
|
||||
s.WebDriverSession = wds
|
||||
s.RodSession = browser
|
||||
}
|
||||
|
||||
func (s *SigninEmailScenario) TearDownSuite() {
|
||||
err := s.WebDriverSession.Stop()
|
||||
err := s.RodSession.Stop()
|
||||
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
|
@ -41,21 +41,25 @@ func (s *SigninEmailScenario) TearDownSuite() {
|
|||
}
|
||||
|
||||
func (s *SigninEmailScenario) SetupTest() {
|
||||
ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second)
|
||||
defer cancel()
|
||||
s.Page = s.doCreateTab(s.T(), HomeBaseURL)
|
||||
s.verifyIsHome(s.T(), s.Page)
|
||||
}
|
||||
|
||||
s.doLogout(ctx, s.T())
|
||||
s.doVisit(s.T(), HomeBaseURL)
|
||||
s.verifyIsHome(ctx, s.T())
|
||||
func (s *SigninEmailScenario) TearDownTest() {
|
||||
s.collectCoverage(s.Page)
|
||||
s.MustClose()
|
||||
}
|
||||
|
||||
func (s *SigninEmailScenario) TestShouldSignInWithUserEmail() {
|
||||
ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second)
|
||||
defer cancel()
|
||||
ctx, cancel := context.WithTimeout(context.Background(), 15*time.Second)
|
||||
defer func() {
|
||||
cancel()
|
||||
s.collectScreenshot(ctx.Err(), s.Page)
|
||||
}()
|
||||
|
||||
targetURL := fmt.Sprintf("%s/secret.html", SingleFactorBaseURL)
|
||||
s.doLoginOneFactor(ctx, s.T(), "john.doe@authelia.com", "password", false, targetURL)
|
||||
s.verifySecretAuthorized(ctx, s.T())
|
||||
s.doLoginOneFactor(s.T(), s.Context(ctx), "john.doe@authelia.com", "password", false, targetURL)
|
||||
s.verifySecretAuthorized(s.T(), s.Context(ctx))
|
||||
}
|
||||
|
||||
func TestSigninEmailScenario(t *testing.T) {
|
||||
|
|
|
@ -11,27 +11,27 @@ import (
|
|||
)
|
||||
|
||||
type TwoFactorSuite struct {
|
||||
*SeleniumSuite
|
||||
*RodSuite
|
||||
}
|
||||
|
||||
func NewTwoFactorScenario() *TwoFactorSuite {
|
||||
return &TwoFactorSuite{
|
||||
SeleniumSuite: new(SeleniumSuite),
|
||||
RodSuite: new(RodSuite),
|
||||
}
|
||||
}
|
||||
|
||||
func (s *TwoFactorSuite) SetupSuite() {
|
||||
wds, err := StartWebDriver()
|
||||
browser, err := StartRod()
|
||||
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
|
||||
s.WebDriverSession = wds
|
||||
s.RodSession = browser
|
||||
}
|
||||
|
||||
func (s *TwoFactorSuite) TearDownSuite() {
|
||||
err := s.WebDriverSession.Stop()
|
||||
err := s.RodSession.Stop()
|
||||
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
|
@ -39,62 +39,57 @@ func (s *TwoFactorSuite) TearDownSuite() {
|
|||
}
|
||||
|
||||
func (s *TwoFactorSuite) SetupTest() {
|
||||
ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second)
|
||||
defer cancel()
|
||||
s.Page = s.doCreateTab(s.T(), HomeBaseURL)
|
||||
s.verifyIsHome(s.T(), s.Page)
|
||||
}
|
||||
|
||||
s.doLogout(ctx, s.T())
|
||||
s.doVisit(s.T(), HomeBaseURL)
|
||||
s.verifyIsHome(ctx, s.T())
|
||||
func (s *TwoFactorSuite) TearDownTest() {
|
||||
s.collectCoverage(s.Page)
|
||||
s.MustClose()
|
||||
}
|
||||
|
||||
func (s *TwoFactorSuite) TestShouldAuthorizeSecretAfterTwoFactor() {
|
||||
ctx, cancel := context.WithTimeout(context.Background(), 15*time.Second)
|
||||
defer cancel()
|
||||
ctx, cancel := context.WithTimeout(context.Background(), 60*time.Second)
|
||||
defer func() {
|
||||
cancel()
|
||||
s.collectScreenshot(ctx.Err(), s.Page)
|
||||
}()
|
||||
|
||||
username := testUsername
|
||||
password := testPassword
|
||||
|
||||
// Login one factor
|
||||
s.doLoginOneFactor(ctx, s.T(), username, password, false, "")
|
||||
|
||||
// Check he reaches the 2FA stage
|
||||
s.verifyIsSecondFactorPage(ctx, s.T())
|
||||
|
||||
// Then register the TOTP factor
|
||||
secret := s.doRegisterTOTP(ctx, s.T())
|
||||
|
||||
// And logout
|
||||
s.doLogout(ctx, s.T())
|
||||
|
||||
// Login again with 1FA & 2FA
|
||||
// Login and register TOTP, logout and login again with 1FA & 2FA
|
||||
targetURL := fmt.Sprintf("%s/secret.html", AdminBaseURL)
|
||||
s.doLoginTwoFactor(ctx, s.T(), testUsername, testPassword, false, secret, targetURL)
|
||||
_ = s.doRegisterAndLogin2FA(s.T(), s.Context(ctx), username, password, false, targetURL)
|
||||
|
||||
// And check if the user is redirected to the secret.
|
||||
s.verifySecretAuthorized(ctx, s.T())
|
||||
s.verifySecretAuthorized(s.T(), s.Context(ctx))
|
||||
|
||||
// Leave the secret
|
||||
s.doVisit(s.T(), HomeBaseURL)
|
||||
s.verifyIsHome(ctx, s.T())
|
||||
s.doVisit(s.T(), s.Context(ctx), HomeBaseURL)
|
||||
s.verifyIsHome(s.T(), s.Context(ctx))
|
||||
|
||||
// And try to reload it again to check the session is kept
|
||||
s.doVisit(s.T(), targetURL)
|
||||
s.verifySecretAuthorized(ctx, s.T())
|
||||
s.doVisit(s.T(), s.Context(ctx), targetURL)
|
||||
s.verifySecretAuthorized(s.T(), s.Context(ctx))
|
||||
}
|
||||
|
||||
func (s *TwoFactorSuite) TestShouldFailTwoFactor() {
|
||||
ctx, cancel := context.WithTimeout(context.Background(), 15*time.Second)
|
||||
defer cancel()
|
||||
defer func() {
|
||||
cancel()
|
||||
s.collectScreenshot(ctx.Err(), s.Page)
|
||||
}()
|
||||
|
||||
// Register TOTP secret and logout.
|
||||
s.doRegisterThenLogout(ctx, s.T(), testUsername, testPassword)
|
||||
s.doRegisterThenLogout(s.T(), s.Context(ctx), testUsername, testPassword)
|
||||
|
||||
wrongPasscode := "123456"
|
||||
|
||||
s.doLoginOneFactor(ctx, s.T(), testUsername, testPassword, false, "")
|
||||
s.verifyIsSecondFactorPage(ctx, s.T())
|
||||
s.doEnterOTP(ctx, s.T(), wrongPasscode)
|
||||
s.verifyNotificationDisplayed(ctx, s.T(), "The one-time password might be wrong")
|
||||
s.doLoginOneFactor(s.T(), s.Context(ctx), testUsername, testPassword, false, "")
|
||||
s.verifyIsSecondFactorPage(s.T(), s.Context(ctx))
|
||||
s.doEnterOTP(s.T(), s.Context(ctx), wrongPasscode)
|
||||
s.verifyNotificationDisplayed(s.T(), s.Context(ctx), "The one-time password might be wrong")
|
||||
}
|
||||
|
||||
func TestRunTwoFactor(t *testing.T) {
|
||||
|
|
|
@ -10,27 +10,27 @@ import (
|
|||
)
|
||||
|
||||
type UserPreferencesScenario struct {
|
||||
*SeleniumSuite
|
||||
*RodSuite
|
||||
}
|
||||
|
||||
func NewUserPreferencesScenario() *UserPreferencesScenario {
|
||||
return &UserPreferencesScenario{
|
||||
SeleniumSuite: new(SeleniumSuite),
|
||||
RodSuite: new(RodSuite),
|
||||
}
|
||||
}
|
||||
|
||||
func (s *UserPreferencesScenario) SetupSuite() {
|
||||
wds, err := StartWebDriver()
|
||||
browser, err := StartRod()
|
||||
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
|
||||
s.WebDriverSession = wds
|
||||
s.RodSession = browser
|
||||
}
|
||||
|
||||
func (s *UserPreferencesScenario) TearDownSuite() {
|
||||
err := s.WebDriverSession.Stop()
|
||||
err := s.RodSession.Stop()
|
||||
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
|
@ -38,59 +38,63 @@ func (s *UserPreferencesScenario) TearDownSuite() {
|
|||
}
|
||||
|
||||
func (s *UserPreferencesScenario) SetupTest() {
|
||||
ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second)
|
||||
defer cancel()
|
||||
s.Page = s.doCreateTab(s.T(), HomeBaseURL)
|
||||
s.verifyIsHome(s.T(), s.Page)
|
||||
}
|
||||
|
||||
s.doLogout(ctx, s.T())
|
||||
s.doVisit(s.T(), HomeBaseURL)
|
||||
s.verifyIsHome(ctx, s.T())
|
||||
func (s *UserPreferencesScenario) TearDownTest() {
|
||||
s.collectCoverage(s.Page)
|
||||
s.MustClose()
|
||||
}
|
||||
|
||||
func (s *UserPreferencesScenario) TestShouldRememberLastUsed2FAMethod() {
|
||||
ctx, cancel := context.WithTimeout(context.Background(), 40*time.Second)
|
||||
defer cancel()
|
||||
ctx, cancel := context.WithTimeout(context.Background(), 90*time.Second)
|
||||
defer func() {
|
||||
cancel()
|
||||
s.collectScreenshot(ctx.Err(), s.Page)
|
||||
}()
|
||||
|
||||
// Authenticate
|
||||
s.doLoginOneFactor(ctx, s.T(), "john", "password", false, "")
|
||||
s.verifyIsSecondFactorPage(ctx, s.T())
|
||||
s.doLoginOneFactor(s.T(), s.Context(ctx), "john", "password", false, "")
|
||||
s.verifyIsSecondFactorPage(s.T(), s.Context(ctx))
|
||||
|
||||
// Then switch to push notification method
|
||||
s.doChangeMethod(ctx, s.T(), "push-notification")
|
||||
s.WaitElementLocatedByID(ctx, s.T(), "push-notification-method")
|
||||
s.doChangeMethod(s.T(), s.Context(ctx), "push-notification")
|
||||
s.WaitElementLocatedByCSSSelector(s.T(), s.Context(ctx), "push-notification-method")
|
||||
|
||||
// Switch context to clean up state in portal.
|
||||
s.doVisit(s.T(), HomeBaseURL)
|
||||
s.verifyIsHome(ctx, s.T())
|
||||
s.doVisit(s.T(), s.Context(ctx), HomeBaseURL)
|
||||
s.verifyIsHome(s.T(), s.Context(ctx))
|
||||
|
||||
// Then go back to portal.
|
||||
s.doVisit(s.T(), GetLoginBaseURL())
|
||||
s.verifyIsSecondFactorPage(ctx, s.T())
|
||||
s.doVisit(s.T(), s.Context(ctx), GetLoginBaseURL())
|
||||
s.verifyIsSecondFactorPage(s.T(), s.Context(ctx))
|
||||
// And check the latest method is still used.
|
||||
s.WaitElementLocatedByID(ctx, s.T(), "push-notification-method")
|
||||
s.WaitElementLocatedByCSSSelector(s.T(), s.Context(ctx), "push-notification-method")
|
||||
// Meaning the authentication is successful
|
||||
s.verifyIsHome(ctx, s.T())
|
||||
s.verifyIsHome(s.T(), s.Context(ctx))
|
||||
|
||||
// Logout the user and see what user 'harry' sees.
|
||||
s.doLogout(ctx, s.T())
|
||||
s.doLoginOneFactor(ctx, s.T(), "harry", "password", false, "")
|
||||
s.verifyIsSecondFactorPage(ctx, s.T())
|
||||
s.WaitElementLocatedByID(ctx, s.T(), "one-time-password-method")
|
||||
s.doLogout(s.T(), s.Context(ctx))
|
||||
s.doLoginOneFactor(s.T(), s.Context(ctx), "harry", "password", false, "")
|
||||
s.verifyIsSecondFactorPage(s.T(), s.Context(ctx))
|
||||
s.WaitElementLocatedByCSSSelector(s.T(), s.Context(ctx), "one-time-password-method")
|
||||
|
||||
s.doLogout(ctx, s.T())
|
||||
s.verifyIsFirstFactorPage(ctx, s.T())
|
||||
s.doLogout(s.T(), s.Context(ctx))
|
||||
s.verifyIsFirstFactorPage(s.T(), s.Context(ctx))
|
||||
|
||||
// Then log back as previous user and verify the push notification is still the default method
|
||||
s.doLoginOneFactor(ctx, s.T(), "john", "password", false, "")
|
||||
s.verifyIsSecondFactorPage(ctx, s.T())
|
||||
s.WaitElementLocatedByID(ctx, s.T(), "push-notification-method")
|
||||
s.verifyIsHome(ctx, s.T())
|
||||
s.doLoginOneFactor(s.T(), s.Context(ctx), "john", "password", false, "")
|
||||
s.verifyIsSecondFactorPage(s.T(), s.Context(ctx))
|
||||
s.WaitElementLocatedByCSSSelector(s.T(), s.Context(ctx), "push-notification-method")
|
||||
s.verifyIsHome(s.T(), s.Context(ctx))
|
||||
|
||||
s.doLogout(ctx, s.T())
|
||||
s.doLoginOneFactor(ctx, s.T(), "john", "password", false, "")
|
||||
s.doLogout(s.T(), s.Context(ctx))
|
||||
s.doLoginOneFactor(s.T(), s.Context(ctx), "john", "password", false, "")
|
||||
|
||||
// Eventually restore the default method
|
||||
s.doChangeMethod(ctx, s.T(), "one-time-password")
|
||||
s.WaitElementLocatedByID(ctx, s.T(), "one-time-password-method")
|
||||
s.doChangeMethod(s.T(), s.Context(ctx), "one-time-password")
|
||||
s.WaitElementLocatedByCSSSelector(s.T(), s.Context(ctx), "one-time-password-method")
|
||||
}
|
||||
|
||||
func TestUserPreferencesScenario(t *testing.T) {
|
||||
|
|
|
@ -7,11 +7,11 @@ import (
|
|||
)
|
||||
|
||||
type ActiveDirectorySuite struct {
|
||||
*SeleniumSuite
|
||||
*RodSuite
|
||||
}
|
||||
|
||||
func NewActiveDirectorySuite() *ActiveDirectorySuite {
|
||||
return &ActiveDirectorySuite{SeleniumSuite: new(SeleniumSuite)}
|
||||
return &ActiveDirectorySuite{RodSuite: new(RodSuite)}
|
||||
}
|
||||
|
||||
func (s *ActiveDirectorySuite) TestOneFactorScenario() {
|
||||
|
|
|
@ -11,40 +11,53 @@ import (
|
|||
)
|
||||
|
||||
type BypassAllWebDriverSuite struct {
|
||||
*SeleniumSuite
|
||||
*RodSuite
|
||||
}
|
||||
|
||||
func NewBypassAllWebDriverSuite() *BypassAllWebDriverSuite {
|
||||
return &BypassAllWebDriverSuite{SeleniumSuite: new(SeleniumSuite)}
|
||||
return &BypassAllWebDriverSuite{RodSuite: new(RodSuite)}
|
||||
}
|
||||
|
||||
func (s *BypassAllWebDriverSuite) SetupSuite() {
|
||||
wds, err := StartWebDriver()
|
||||
browser, err := StartRod()
|
||||
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
|
||||
s.WebDriverSession = wds
|
||||
s.RodSession = browser
|
||||
}
|
||||
|
||||
func (s *BypassAllWebDriverSuite) TearDownSuite() {
|
||||
err := s.WebDriverSession.Stop()
|
||||
err := s.RodSession.Stop()
|
||||
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
}
|
||||
|
||||
func (s *BypassAllWebDriverSuite) SetupTest() {
|
||||
s.Page = s.doCreateTab(s.T(), HomeBaseURL)
|
||||
s.verifyIsHome(s.T(), s.Page)
|
||||
}
|
||||
|
||||
func (s *BypassAllWebDriverSuite) TearDownTest() {
|
||||
s.collectCoverage(s.Page)
|
||||
s.MustClose()
|
||||
}
|
||||
|
||||
func (s *BypassAllWebDriverSuite) TestShouldAccessPublicResource() {
|
||||
ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second)
|
||||
defer cancel()
|
||||
defer func() {
|
||||
cancel()
|
||||
s.collectScreenshot(ctx.Err(), s.Page)
|
||||
}()
|
||||
|
||||
s.doVisit(s.T(), fmt.Sprintf("%s/secret.html", AdminBaseURL))
|
||||
s.verifySecretAuthorized(ctx, s.T())
|
||||
s.doVisit(s.T(), s.Context(ctx), fmt.Sprintf("%s/secret.html", AdminBaseURL))
|
||||
s.verifySecretAuthorized(s.T(), s.Context(ctx))
|
||||
|
||||
s.doVisit(s.T(), fmt.Sprintf("%s/secret.html", PublicBaseURL))
|
||||
s.verifySecretAuthorized(ctx, s.T())
|
||||
s.doVisit(s.T(), s.Context(ctx), fmt.Sprintf("%s/secret.html", PublicBaseURL))
|
||||
s.verifySecretAuthorized(s.T(), s.Context(ctx))
|
||||
}
|
||||
|
||||
type BypassAllSuite struct {
|
||||
|
|
|
@ -7,11 +7,11 @@ import (
|
|||
)
|
||||
|
||||
type DockerSuite struct {
|
||||
*SeleniumSuite
|
||||
*RodSuite
|
||||
}
|
||||
|
||||
func NewDockerSuite() *DockerSuite {
|
||||
return &DockerSuite{SeleniumSuite: new(SeleniumSuite)}
|
||||
return &DockerSuite{RodSuite: new(RodSuite)}
|
||||
}
|
||||
|
||||
func (s *DockerSuite) TestOneFactorScenario() {
|
||||
|
|
|
@ -10,25 +10,25 @@ import (
|
|||
)
|
||||
|
||||
type DuoPushWebDriverSuite struct {
|
||||
*SeleniumSuite
|
||||
*RodSuite
|
||||
}
|
||||
|
||||
func NewDuoPushWebDriverSuite() *DuoPushWebDriverSuite {
|
||||
return &DuoPushWebDriverSuite{SeleniumSuite: new(SeleniumSuite)}
|
||||
return &DuoPushWebDriverSuite{RodSuite: new(RodSuite)}
|
||||
}
|
||||
|
||||
func (s *DuoPushWebDriverSuite) SetupSuite() {
|
||||
wds, err := StartWebDriver()
|
||||
browser, err := StartRod()
|
||||
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
|
||||
s.WebDriverSession = wds
|
||||
s.RodSession = browser
|
||||
}
|
||||
|
||||
func (s *DuoPushWebDriverSuite) TearDownSuite() {
|
||||
err := s.WebDriverSession.Stop()
|
||||
err := s.RodSession.Stop()
|
||||
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
|
@ -36,65 +36,75 @@ func (s *DuoPushWebDriverSuite) TearDownSuite() {
|
|||
}
|
||||
|
||||
func (s *DuoPushWebDriverSuite) SetupTest() {
|
||||
ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second)
|
||||
defer cancel()
|
||||
|
||||
s.doLogout(ctx, s.T())
|
||||
s.Page = s.doCreateTab(s.T(), HomeBaseURL)
|
||||
s.verifyIsHome(s.T(), s.Page)
|
||||
}
|
||||
|
||||
func (s *DuoPushWebDriverSuite) TearDownTest() {
|
||||
ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second)
|
||||
defer cancel()
|
||||
defer func() {
|
||||
cancel()
|
||||
s.collectScreenshot(ctx.Err(), s.Page)
|
||||
|
||||
s.doLogout(ctx, s.T())
|
||||
s.doLoginOneFactor(ctx, s.T(), "john", "password", false, "")
|
||||
s.verifyIsSecondFactorPage(ctx, s.T())
|
||||
s.doChangeMethod(ctx, s.T(), "one-time-password")
|
||||
s.WaitElementLocatedByID(ctx, s.T(), "one-time-password-method")
|
||||
s.collectCoverage(s.Page)
|
||||
s.MustClose()
|
||||
}()
|
||||
|
||||
s.doLogout(s.T(), s.Context(ctx))
|
||||
s.doLoginOneFactor(s.T(), s.Context(ctx), "john", "password", false, "")
|
||||
s.verifyIsSecondFactorPage(s.T(), s.Context(ctx))
|
||||
s.doChangeMethod(s.T(), s.Context(ctx), "one-time-password")
|
||||
s.WaitElementLocatedByCSSSelector(s.T(), s.Context(ctx), "one-time-password-method")
|
||||
}
|
||||
|
||||
func (s *DuoPushWebDriverSuite) TestShouldSucceedAuthentication() {
|
||||
ctx, cancel := context.WithTimeout(context.Background(), 15*time.Second)
|
||||
defer cancel()
|
||||
defer func() {
|
||||
cancel()
|
||||
s.collectScreenshot(ctx.Err(), s.Page)
|
||||
}()
|
||||
|
||||
ConfigureDuo(s.T(), Allow)
|
||||
|
||||
s.doLoginOneFactor(ctx, s.T(), "john", "password", false, "")
|
||||
s.doChangeMethod(ctx, s.T(), "push-notification")
|
||||
s.verifyIsHome(ctx, s.T())
|
||||
s.doLoginOneFactor(s.T(), s.Context(ctx), "john", "password", false, "")
|
||||
s.doChangeMethod(s.T(), s.Context(ctx), "push-notification")
|
||||
s.verifyIsHome(s.T(), s.Context(ctx))
|
||||
}
|
||||
|
||||
func (s *DuoPushWebDriverSuite) TestShouldFailAuthentication() {
|
||||
ctx, cancel := context.WithTimeout(context.Background(), 15*time.Second)
|
||||
defer cancel()
|
||||
defer func() {
|
||||
cancel()
|
||||
s.collectScreenshot(ctx.Err(), s.Page)
|
||||
}()
|
||||
|
||||
ConfigureDuo(s.T(), Deny)
|
||||
|
||||
s.doLoginOneFactor(ctx, s.T(), "john", "password", false, "")
|
||||
s.doChangeMethod(ctx, s.T(), "push-notification")
|
||||
s.WaitElementLocatedByClassName(ctx, s.T(), "failure-icon")
|
||||
s.doLoginOneFactor(s.T(), s.Context(ctx), "john", "password", false, "")
|
||||
s.doChangeMethod(s.T(), s.Context(ctx), "push-notification")
|
||||
s.WaitElementLocatedByClassName(s.T(), s.Context(ctx), "failure-icon")
|
||||
}
|
||||
|
||||
type DuoPushDefaultRedirectionSuite struct {
|
||||
*SeleniumSuite
|
||||
*RodSuite
|
||||
}
|
||||
|
||||
func NewDuoPushDefaultRedirectionSuite() *DuoPushDefaultRedirectionSuite {
|
||||
return &DuoPushDefaultRedirectionSuite{SeleniumSuite: new(SeleniumSuite)}
|
||||
return &DuoPushDefaultRedirectionSuite{RodSuite: new(RodSuite)}
|
||||
}
|
||||
|
||||
func (s *DuoPushDefaultRedirectionSuite) SetupSuite() {
|
||||
wds, err := StartWebDriver()
|
||||
browser, err := StartRod()
|
||||
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
|
||||
s.WebDriverSession = wds
|
||||
s.RodSession = browser
|
||||
}
|
||||
|
||||
func (s *DuoPushDefaultRedirectionSuite) TearDownSuite() {
|
||||
err := s.WebDriverSession.Stop()
|
||||
err := s.RodSession.Stop()
|
||||
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
|
@ -102,19 +112,25 @@ func (s *DuoPushDefaultRedirectionSuite) TearDownSuite() {
|
|||
}
|
||||
|
||||
func (s *DuoPushDefaultRedirectionSuite) SetupTest() {
|
||||
ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second)
|
||||
defer cancel()
|
||||
s.Page = s.doCreateTab(s.T(), HomeBaseURL)
|
||||
s.verifyIsHome(s.T(), s.Page)
|
||||
}
|
||||
|
||||
s.doLogout(ctx, s.T())
|
||||
func (s *DuoPushDefaultRedirectionSuite) TearDownTest() {
|
||||
s.collectCoverage(s.Page)
|
||||
s.MustClose()
|
||||
}
|
||||
|
||||
func (s *DuoPushDefaultRedirectionSuite) TestUserIsRedirectedToDefaultURL() {
|
||||
ctx, cancel := context.WithTimeout(context.Background(), 15*time.Second)
|
||||
defer cancel()
|
||||
defer func() {
|
||||
cancel()
|
||||
s.collectScreenshot(ctx.Err(), s.Page)
|
||||
}()
|
||||
|
||||
s.doLoginOneFactor(ctx, s.T(), "john", "password", false, "")
|
||||
s.doChangeMethod(ctx, s.T(), "push-notification")
|
||||
s.verifyURLIs(ctx, s.T(), HomeBaseURL+"/")
|
||||
s.doLoginOneFactor(s.T(), s.Context(ctx), "john", "password", false, "")
|
||||
s.doChangeMethod(s.T(), s.Context(ctx), "push-notification")
|
||||
s.verifyIsHome(s.T(), s.Page)
|
||||
}
|
||||
|
||||
type DuoPushSuite struct {
|
||||
|
|
|
@ -42,6 +42,13 @@ func init() {
|
|||
|
||||
fmt.Println(frontendLogs)
|
||||
|
||||
haproxyLogs, err := dockerEnvironment.Logs("haproxy", nil)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
fmt.Println(haproxyLogs)
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
|
|
|
@ -7,11 +7,11 @@ import (
|
|||
)
|
||||
|
||||
type HAProxySuite struct {
|
||||
*SeleniumSuite
|
||||
*RodSuite
|
||||
}
|
||||
|
||||
func NewHAProxySuite() *HAProxySuite {
|
||||
return &HAProxySuite{SeleniumSuite: new(SeleniumSuite)}
|
||||
return &HAProxySuite{RodSuite: new(RodSuite)}
|
||||
}
|
||||
|
||||
func (s *HAProxySuite) TestOneFactorScenario() {
|
||||
|
|
|
@ -13,25 +13,25 @@ import (
|
|||
)
|
||||
|
||||
type HighAvailabilityWebDriverSuite struct {
|
||||
*SeleniumSuite
|
||||
*RodSuite
|
||||
}
|
||||
|
||||
func NewHighAvailabilityWebDriverSuite() *HighAvailabilityWebDriverSuite {
|
||||
return &HighAvailabilityWebDriverSuite{SeleniumSuite: new(SeleniumSuite)}
|
||||
return &HighAvailabilityWebDriverSuite{RodSuite: new(RodSuite)}
|
||||
}
|
||||
|
||||
func (s *HighAvailabilityWebDriverSuite) SetupSuite() {
|
||||
wds, err := StartWebDriver()
|
||||
browser, err := StartRod()
|
||||
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
|
||||
s.WebDriverSession = wds
|
||||
s.RodSession = browser
|
||||
}
|
||||
|
||||
func (s *HighAvailabilityWebDriverSuite) TearDownSuite() {
|
||||
err := s.WebDriverSession.Stop()
|
||||
err := s.RodSession.Stop()
|
||||
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
|
@ -39,37 +39,42 @@ func (s *HighAvailabilityWebDriverSuite) TearDownSuite() {
|
|||
}
|
||||
|
||||
func (s *HighAvailabilityWebDriverSuite) SetupTest() {
|
||||
ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second)
|
||||
defer cancel()
|
||||
s.Page = s.doCreateTab(s.T(), HomeBaseURL)
|
||||
s.verifyIsHome(s.T(), s.Page)
|
||||
}
|
||||
|
||||
s.doLogout(ctx, s.T())
|
||||
s.doVisit(s.T(), HomeBaseURL)
|
||||
s.verifyIsHome(ctx, s.T())
|
||||
func (s *HighAvailabilityWebDriverSuite) TearDownTest() {
|
||||
s.collectCoverage(s.Page)
|
||||
s.MustClose()
|
||||
}
|
||||
|
||||
func (s *HighAvailabilityWebDriverSuite) TestShouldKeepUserSessionActive() {
|
||||
ctx, cancel := context.WithTimeout(context.Background(), 50*time.Second)
|
||||
defer cancel()
|
||||
ctx, cancel := context.WithTimeout(context.Background(), 60*time.Second)
|
||||
defer func() {
|
||||
cancel()
|
||||
s.collectScreenshot(ctx.Err(), s.Page)
|
||||
}()
|
||||
|
||||
secret := s.doRegisterThenLogout(ctx, s.T(), "john", "password")
|
||||
secret := s.doRegisterThenLogout(s.T(), s.Context(ctx), "john", "password")
|
||||
|
||||
err := haDockerEnvironment.Restart("redis-node-0")
|
||||
s.Require().NoError(err)
|
||||
|
||||
time.Sleep(5 * time.Second)
|
||||
|
||||
s.doLoginTwoFactor(ctx, s.T(), "john", "password", false, secret, "")
|
||||
s.verifyIsSecondFactorPage(ctx, s.T())
|
||||
s.doLoginTwoFactor(s.T(), s.Context(ctx), "john", "password", false, secret, "")
|
||||
s.verifyIsSecondFactorPage(s.T(), s.Context(ctx))
|
||||
}
|
||||
|
||||
func (s *HighAvailabilityWebDriverSuite) TestShouldKeepUserSessionActiveWithPrimaryRedisNodeFailure() {
|
||||
ctx, cancel := context.WithTimeout(context.Background(), 50*time.Second)
|
||||
defer cancel()
|
||||
ctx, cancel := context.WithTimeout(context.Background(), 60*time.Second)
|
||||
defer func() {
|
||||
cancel()
|
||||
s.collectScreenshot(ctx.Err(), s.Page)
|
||||
}()
|
||||
|
||||
secret := s.doRegisterThenLogout(ctx, s.T(), "john", "password")
|
||||
secret := s.doRegisterThenLogout(s.T(), s.Context(ctx), "john", "password")
|
||||
|
||||
s.doLoginTwoFactor(ctx, s.T(), "john", "password", false, secret, "")
|
||||
s.verifyIsSecondFactorPage(ctx, s.T())
|
||||
s.doLoginTwoFactor(s.T(), s.Context(ctx), "john", "password", false, secret, "")
|
||||
s.verifyIsSecondFactorPage(s.T(), s.Context(ctx))
|
||||
|
||||
err := haDockerEnvironment.Stop("redis-node-0")
|
||||
s.Require().NoError(err)
|
||||
|
@ -79,32 +84,32 @@ func (s *HighAvailabilityWebDriverSuite) TestShouldKeepUserSessionActiveWithPrim
|
|||
s.Require().NoError(err)
|
||||
}()
|
||||
|
||||
// Allow fail over to occur.
|
||||
time.Sleep(3 * time.Second)
|
||||
|
||||
s.doVisit(s.T(), HomeBaseURL)
|
||||
s.verifyIsHome(ctx, s.T())
|
||||
s.doVisit(s.T(), s.Context(ctx), HomeBaseURL)
|
||||
s.verifyIsHome(s.T(), s.Context(ctx))
|
||||
|
||||
// Verify the user is still authenticated
|
||||
s.doVisit(s.T(), GetLoginBaseURL())
|
||||
s.verifyIsSecondFactorPage(ctx, s.T())
|
||||
s.doVisit(s.T(), s.Context(ctx), GetLoginBaseURL())
|
||||
s.verifyIsSecondFactorPage(s.T(), s.Context(ctx))
|
||||
|
||||
// Then logout and login again to check we can see the secret.
|
||||
s.doLogout(ctx, s.T())
|
||||
s.verifyIsFirstFactorPage(ctx, s.T())
|
||||
s.doLogout(s.T(), s.Context(ctx))
|
||||
s.verifyIsFirstFactorPage(s.T(), s.Context(ctx))
|
||||
|
||||
s.doLoginTwoFactor(ctx, s.T(), "john", "password", false, secret, fmt.Sprintf("%s/secret.html", SecureBaseURL))
|
||||
s.verifySecretAuthorized(ctx, s.T())
|
||||
s.doLoginTwoFactor(s.T(), s.Context(ctx), "john", "password", false, secret, fmt.Sprintf("%s/secret.html", SecureBaseURL))
|
||||
s.verifySecretAuthorized(s.T(), s.Context(ctx))
|
||||
}
|
||||
|
||||
func (s *HighAvailabilityWebDriverSuite) TestShouldKeepUserSessionActiveWithPrimaryRedisSentinelFailureAndSecondaryRedisNodeFailure() {
|
||||
ctx, cancel := context.WithTimeout(context.Background(), 50*time.Second)
|
||||
defer cancel()
|
||||
ctx, cancel := context.WithTimeout(context.Background(), 90*time.Second)
|
||||
defer func() {
|
||||
cancel()
|
||||
s.collectScreenshot(ctx.Err(), s.Page)
|
||||
}()
|
||||
|
||||
secret := s.doRegisterThenLogout(ctx, s.T(), "john", "password")
|
||||
secret := s.doRegisterThenLogout(s.T(), s.Context(ctx), "john", "password")
|
||||
|
||||
s.doLoginTwoFactor(ctx, s.T(), "john", "password", false, secret, "")
|
||||
s.verifyIsSecondFactorPage(ctx, s.T())
|
||||
s.doLoginTwoFactor(s.T(), s.Context(ctx), "john", "password", false, secret, "")
|
||||
s.verifyIsSecondFactorPage(s.T(), s.Context(ctx))
|
||||
|
||||
err := haDockerEnvironment.Stop("redis-sentinel-0")
|
||||
s.Require().NoError(err)
|
||||
|
@ -122,38 +127,39 @@ func (s *HighAvailabilityWebDriverSuite) TestShouldKeepUserSessionActiveWithPrim
|
|||
s.Require().NoError(err)
|
||||
}()
|
||||
|
||||
// Allow fail over to occur.
|
||||
time.Sleep(3 * time.Second)
|
||||
|
||||
s.doVisit(s.T(), HomeBaseURL)
|
||||
s.verifyIsHome(ctx, s.T())
|
||||
s.doVisit(s.T(), s.Context(ctx), HomeBaseURL)
|
||||
s.verifyIsHome(s.T(), s.Context(ctx))
|
||||
|
||||
// Verify the user is still authenticated
|
||||
s.doVisit(s.T(), GetLoginBaseURL())
|
||||
s.verifyIsSecondFactorPage(ctx, s.T())
|
||||
s.doVisit(s.T(), s.Context(ctx), GetLoginBaseURL())
|
||||
s.verifyIsSecondFactorPage(s.T(), s.Context(ctx))
|
||||
}
|
||||
|
||||
func (s *HighAvailabilityWebDriverSuite) TestShouldKeepUserDataInDB() {
|
||||
ctx, cancel := context.WithTimeout(context.Background(), 50*time.Second)
|
||||
defer cancel()
|
||||
ctx, cancel := context.WithTimeout(context.Background(), 60*time.Second)
|
||||
defer func() {
|
||||
cancel()
|
||||
s.collectScreenshot(ctx.Err(), s.Page)
|
||||
}()
|
||||
|
||||
secret := s.doRegisterThenLogout(ctx, s.T(), "john", "password")
|
||||
secret := s.doRegisterThenLogout(s.T(), s.Context(ctx), "john", "password")
|
||||
|
||||
err := haDockerEnvironment.Restart("mariadb")
|
||||
s.Require().NoError(err)
|
||||
|
||||
time.Sleep(20 * time.Second)
|
||||
|
||||
s.doLoginTwoFactor(ctx, s.T(), "john", "password", false, secret, "")
|
||||
s.verifyIsSecondFactorPage(ctx, s.T())
|
||||
s.doLoginTwoFactor(s.T(), s.Context(ctx), "john", "password", false, secret, "")
|
||||
s.verifyIsSecondFactorPage(s.T(), s.Context(ctx))
|
||||
}
|
||||
|
||||
func (s *HighAvailabilityWebDriverSuite) TestShouldKeepSessionAfterAutheliaRestart() {
|
||||
ctx, cancel := context.WithTimeout(context.Background(), 120*time.Second)
|
||||
defer cancel()
|
||||
defer func() {
|
||||
cancel()
|
||||
s.collectScreenshot(ctx.Err(), s.Page)
|
||||
}()
|
||||
|
||||
secret := s.doRegisterAndLogin2FA(ctx, s.T(), "john", "password", false, "")
|
||||
s.verifyIsSecondFactorPage(ctx, s.T())
|
||||
secret := s.doRegisterAndLogin2FA(s.T(), s.Context(ctx), "john", "password", false, "")
|
||||
s.verifyIsSecondFactorPage(s.T(), s.Context(ctx))
|
||||
|
||||
err := haDockerEnvironment.Restart("authelia-backend")
|
||||
s.Require().NoError(err)
|
||||
|
@ -161,19 +167,19 @@ func (s *HighAvailabilityWebDriverSuite) TestShouldKeepSessionAfterAutheliaResta
|
|||
err = waitUntilAutheliaBackendIsReady(haDockerEnvironment)
|
||||
s.Require().NoError(err)
|
||||
|
||||
s.doVisit(s.T(), HomeBaseURL)
|
||||
s.verifyIsHome(ctx, s.T())
|
||||
s.doVisit(s.T(), s.Context(ctx), HomeBaseURL)
|
||||
s.verifyIsHome(s.T(), s.Context(ctx))
|
||||
|
||||
// Verify the user is still authenticated
|
||||
s.doVisit(s.T(), GetLoginBaseURL())
|
||||
s.verifyIsSecondFactorPage(ctx, s.T())
|
||||
s.doVisit(s.T(), s.Context(ctx), GetLoginBaseURL())
|
||||
s.verifyIsSecondFactorPage(s.T(), s.Context(ctx))
|
||||
|
||||
// Then logout and login again to check the secret is still there
|
||||
s.doLogout(ctx, s.T())
|
||||
s.verifyIsFirstFactorPage(ctx, s.T())
|
||||
s.doLogout(s.T(), s.Context(ctx))
|
||||
s.verifyIsFirstFactorPage(s.T(), s.Context(ctx))
|
||||
|
||||
s.doLoginTwoFactor(ctx, s.T(), "john", "password", false, secret, fmt.Sprintf("%s/secret.html", SecureBaseURL))
|
||||
s.verifySecretAuthorized(ctx, s.T())
|
||||
s.doLoginTwoFactor(s.T(), s.Context(ctx), "john", "password", false, secret, fmt.Sprintf("%s/secret.html", SecureBaseURL))
|
||||
s.verifySecretAuthorized(s.T(), s.Context(ctx))
|
||||
}
|
||||
|
||||
var UserJohn = "john"
|
||||
|
@ -220,31 +226,34 @@ var expectedAuthorizations = map[string](map[string]bool){
|
|||
}
|
||||
|
||||
func (s *HighAvailabilityWebDriverSuite) TestShouldVerifyAccessControl() {
|
||||
verifyUserIsAuthorized := func(ctx context.Context, t *testing.T, username, targetURL string, authorized bool) { //nolint:unparam
|
||||
s.doVisit(t, targetURL)
|
||||
s.verifyURLIs(ctx, t, targetURL)
|
||||
verifyUserIsAuthorized := func(ctx context.Context, t *testing.T, targetURL string, authorized bool) {
|
||||
s.doVisit(t, s.Context(ctx), targetURL)
|
||||
s.verifyURLIs(t, s.Context(ctx), targetURL)
|
||||
|
||||
if authorized {
|
||||
s.verifySecretAuthorized(ctx, t)
|
||||
s.verifySecretAuthorized(t, s.Context(ctx))
|
||||
} else {
|
||||
s.verifyBodyContains(ctx, t, "403 Forbidden")
|
||||
s.verifyBodyContains(t, s.Context(ctx), "403 Forbidden")
|
||||
}
|
||||
}
|
||||
|
||||
verifyAuthorization := func(username string) func(t *testing.T) {
|
||||
return func(t *testing.T) {
|
||||
ctx, cancel := context.WithTimeout(context.Background(), 20*time.Second)
|
||||
defer cancel()
|
||||
defer func() {
|
||||
s.collectScreenshot(ctx.Err(), s.Page)
|
||||
cancel()
|
||||
}()
|
||||
|
||||
s.doRegisterAndLogin2FA(ctx, t, username, "password", false, "")
|
||||
s.doRegisterAndLogin2FA(t, s.Context(ctx), username, "password", false, "")
|
||||
|
||||
for url, authorizations := range expectedAuthorizations {
|
||||
t.Run(url, func(t *testing.T) {
|
||||
verifyUserIsAuthorized(ctx, t, username, url, authorizations[username])
|
||||
verifyUserIsAuthorized(ctx, t, url, authorizations[username])
|
||||
})
|
||||
}
|
||||
|
||||
s.doLogout(ctx, t)
|
||||
s.doLogout(t, s.Context(ctx))
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -7,11 +7,11 @@ import (
|
|||
)
|
||||
|
||||
type KubernetesSuite struct {
|
||||
*SeleniumSuite
|
||||
*RodSuite
|
||||
}
|
||||
|
||||
func NewKubernetesSuite() *KubernetesSuite {
|
||||
return &KubernetesSuite{SeleniumSuite: new(SeleniumSuite)}
|
||||
return &KubernetesSuite{RodSuite: new(RodSuite)}
|
||||
}
|
||||
|
||||
func (s *KubernetesSuite) TestOneFactorScenario() {
|
||||
|
|
|
@ -7,11 +7,11 @@ import (
|
|||
)
|
||||
|
||||
type LDAPSuite struct {
|
||||
*SeleniumSuite
|
||||
*RodSuite
|
||||
}
|
||||
|
||||
func NewLDAPSuite() *LDAPSuite {
|
||||
return &LDAPSuite{SeleniumSuite: new(SeleniumSuite)}
|
||||
return &LDAPSuite{RodSuite: new(RodSuite)}
|
||||
}
|
||||
|
||||
func (s *LDAPSuite) TestOneFactorScenario() {
|
||||
|
|
|
@ -7,11 +7,11 @@ import (
|
|||
)
|
||||
|
||||
type MariadbSuite struct {
|
||||
*SeleniumSuite
|
||||
*RodSuite
|
||||
}
|
||||
|
||||
func NewMariadbSuite() *MariadbSuite {
|
||||
return &MariadbSuite{SeleniumSuite: new(SeleniumSuite)}
|
||||
return &MariadbSuite{RodSuite: new(RodSuite)}
|
||||
}
|
||||
|
||||
func (s *MariadbSuite) TestOneFactorScenario() {
|
||||
|
|
|
@ -7,11 +7,11 @@ import (
|
|||
)
|
||||
|
||||
type MySQLSuite struct {
|
||||
*SeleniumSuite
|
||||
*RodSuite
|
||||
}
|
||||
|
||||
func NewMySQLSuite() *MySQLSuite {
|
||||
return &MySQLSuite{SeleniumSuite: new(SeleniumSuite)}
|
||||
return &MySQLSuite{RodSuite: new(RodSuite)}
|
||||
}
|
||||
|
||||
func (s *MySQLSuite) TestOneFactorScenario() {
|
||||
|
|
|
@ -21,20 +21,21 @@ func (s *NetworkACLSuite) TestShouldAccessSecretUpon2FA() {
|
|||
ctx, cancel := context.WithTimeout(context.Background(), 30*time.Second)
|
||||
defer cancel()
|
||||
|
||||
wds, err := StartWebDriver()
|
||||
browser, err := StartRod()
|
||||
s.Require().NoError(err)
|
||||
|
||||
defer func() {
|
||||
err = wds.Stop()
|
||||
err = browser.WebDriver.Close()
|
||||
s.Require().NoError(err)
|
||||
browser.Launcher.Cleanup()
|
||||
}()
|
||||
|
||||
targetURL := fmt.Sprintf("%s/secret.html", SecureBaseURL)
|
||||
wds.doVisit(s.T(), targetURL)
|
||||
wds.verifyIsFirstFactorPage(ctx, s.T())
|
||||
page := browser.doCreateTab(s.T(), targetURL).Context(ctx)
|
||||
|
||||
wds.doRegisterAndLogin2FA(ctx, s.T(), "john", "password", false, targetURL)
|
||||
wds.verifySecretAuthorized(ctx, s.T())
|
||||
browser.verifyIsFirstFactorPage(s.T(), page)
|
||||
browser.doRegisterAndLogin2FA(s.T(), page, "john", "password", false, targetURL)
|
||||
browser.verifySecretAuthorized(s.T(), page)
|
||||
}
|
||||
|
||||
// from network 192.168.240.201/32.
|
||||
|
@ -42,21 +43,22 @@ func (s *NetworkACLSuite) TestShouldAccessSecretUpon1FA() {
|
|||
ctx, cancel := context.WithTimeout(context.Background(), 30*time.Second)
|
||||
defer cancel()
|
||||
|
||||
wds, err := StartWebDriverWithProxy("http://proxy-client1.example.com:3128", GetWebDriverPort())
|
||||
browser, err := StartRodWithProxy("http://proxy-client1.example.com:3128")
|
||||
s.Require().NoError(err)
|
||||
|
||||
defer func() {
|
||||
err = wds.Stop()
|
||||
err = browser.WebDriver.Close()
|
||||
s.Require().NoError(err)
|
||||
browser.Launcher.Cleanup()
|
||||
}()
|
||||
|
||||
targetURL := fmt.Sprintf("%s/secret.html", SecureBaseURL)
|
||||
wds.doVisit(s.T(), targetURL)
|
||||
wds.verifyIsFirstFactorPage(ctx, s.T())
|
||||
page := browser.doCreateTab(s.T(), targetURL).Context(ctx)
|
||||
|
||||
wds.doLoginOneFactor(ctx, s.T(), "john", "password",
|
||||
browser.verifyIsFirstFactorPage(s.T(), page)
|
||||
browser.doLoginOneFactor(s.T(), page, "john", "password",
|
||||
false, fmt.Sprintf("%s/secret.html", SecureBaseURL))
|
||||
wds.verifySecretAuthorized(ctx, s.T())
|
||||
browser.verifySecretAuthorized(s.T(), page)
|
||||
}
|
||||
|
||||
// from network 192.168.240.202/32.
|
||||
|
@ -64,16 +66,18 @@ func (s *NetworkACLSuite) TestShouldAccessSecretUpon0FA() {
|
|||
ctx, cancel := context.WithTimeout(context.Background(), 30*time.Second)
|
||||
defer cancel()
|
||||
|
||||
wds, err := StartWebDriverWithProxy("http://proxy-client2.example.com:3128", GetWebDriverPort())
|
||||
browser, err := StartRodWithProxy("http://proxy-client2.example.com:3128")
|
||||
s.Require().NoError(err)
|
||||
|
||||
defer func() {
|
||||
err = wds.Stop()
|
||||
err = browser.WebDriver.Close()
|
||||
s.Require().NoError(err)
|
||||
browser.Launcher.Cleanup()
|
||||
}()
|
||||
|
||||
wds.doVisit(s.T(), fmt.Sprintf("%s/secret.html", SecureBaseURL))
|
||||
wds.verifySecretAuthorized(ctx, s.T())
|
||||
page := browser.doCreateTab(s.T(), fmt.Sprintf("%s/secret.html", SecureBaseURL)).Context(ctx)
|
||||
|
||||
browser.verifySecretAuthorized(s.T(), page)
|
||||
}
|
||||
|
||||
func TestNetworkACLSuite(t *testing.T) {
|
||||
|
|
|
@ -7,11 +7,11 @@ import (
|
|||
)
|
||||
|
||||
type OIDCSuite struct {
|
||||
*SeleniumSuite
|
||||
*RodSuite
|
||||
}
|
||||
|
||||
func NewOIDCSuite() *OIDCSuite {
|
||||
return &OIDCSuite{SeleniumSuite: new(SeleniumSuite)}
|
||||
return &OIDCSuite{RodSuite: new(RodSuite)}
|
||||
}
|
||||
|
||||
func (s *OIDCSuite) TestOIDCScenario() {
|
||||
|
|
|
@ -7,11 +7,11 @@ import (
|
|||
)
|
||||
|
||||
type OIDCTraefikSuite struct {
|
||||
*SeleniumSuite
|
||||
*RodSuite
|
||||
}
|
||||
|
||||
func NewOIDCTraefikSuite() *OIDCTraefikSuite {
|
||||
return &OIDCTraefikSuite{SeleniumSuite: new(SeleniumSuite)}
|
||||
return &OIDCTraefikSuite{RodSuite: new(RodSuite)}
|
||||
}
|
||||
|
||||
func (s *OIDCTraefikSuite) TestOIDCScenario() {
|
||||
|
|
|
@ -15,25 +15,25 @@ type OneFactorOnlySuite struct {
|
|||
}
|
||||
|
||||
type OneFactorOnlyWebSuite struct {
|
||||
*SeleniumSuite
|
||||
*RodSuite
|
||||
}
|
||||
|
||||
func NewOneFactorOnlyWebSuite() *OneFactorOnlyWebSuite {
|
||||
return &OneFactorOnlyWebSuite{SeleniumSuite: new(SeleniumSuite)}
|
||||
return &OneFactorOnlyWebSuite{RodSuite: new(RodSuite)}
|
||||
}
|
||||
|
||||
func (s *OneFactorOnlyWebSuite) SetupSuite() {
|
||||
wds, err := StartWebDriver()
|
||||
browser, err := StartRod()
|
||||
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
|
||||
s.WebDriverSession = wds
|
||||
s.RodSession = browser
|
||||
}
|
||||
|
||||
func (s *OneFactorOnlyWebSuite) TearDownSuite() {
|
||||
err := s.WebDriverSession.Stop()
|
||||
err := s.RodSession.Stop()
|
||||
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
|
@ -41,62 +41,81 @@ func (s *OneFactorOnlyWebSuite) TearDownSuite() {
|
|||
}
|
||||
|
||||
func (s *OneFactorOnlyWebSuite) SetupTest() {
|
||||
ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second)
|
||||
defer cancel()
|
||||
s.Page = s.doCreateTab(s.T(), HomeBaseURL)
|
||||
s.verifyIsHome(s.T(), s.Page)
|
||||
}
|
||||
|
||||
s.doLogout(ctx, s.T())
|
||||
func (s *OneFactorOnlyWebSuite) TearDownTest() {
|
||||
s.collectCoverage(s.Page)
|
||||
s.MustClose()
|
||||
}
|
||||
|
||||
// No target url is provided, then the user should be redirect to the default url.
|
||||
func (s *OneFactorOnlyWebSuite) TestShouldRedirectUserToDefaultURL() {
|
||||
ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second)
|
||||
defer cancel()
|
||||
defer func() {
|
||||
cancel()
|
||||
s.collectScreenshot(ctx.Err(), s.Page)
|
||||
}()
|
||||
|
||||
s.doLoginOneFactor(ctx, s.T(), "john", "password", false, "")
|
||||
s.verifyURLIs(ctx, s.T(), HomeBaseURL+"/")
|
||||
s.doLoginOneFactor(s.T(), s.Context(ctx), "john", "password", false, "")
|
||||
s.verifyIsHome(s.T(), s.Context(ctx))
|
||||
}
|
||||
|
||||
// Unsafe URL is provided, then the user should be redirect to the default url.
|
||||
func (s *OneFactorOnlyWebSuite) TestShouldRedirectUserToDefaultURLWhenURLIsUnsafe() {
|
||||
ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second)
|
||||
defer cancel()
|
||||
defer func() {
|
||||
cancel()
|
||||
s.collectScreenshot(ctx.Err(), s.Page)
|
||||
}()
|
||||
|
||||
s.doLoginOneFactor(ctx, s.T(), "john", "password", false, "http://unsafe.local")
|
||||
s.verifyURLIs(ctx, s.T(), HomeBaseURL+"/")
|
||||
s.doLoginOneFactor(s.T(), s.Context(ctx), "john", "password", false, "http://unsafe.local")
|
||||
s.verifyIsHome(s.T(), s.Context(ctx))
|
||||
}
|
||||
|
||||
// When use logged in and visit the portal again, she gets redirect to the authenticated view.
|
||||
func (s *OneFactorOnlyWebSuite) TestShouldDisplayAuthenticatedView() {
|
||||
ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second)
|
||||
defer cancel()
|
||||
defer func() {
|
||||
cancel()
|
||||
s.collectScreenshot(ctx.Err(), s.Page)
|
||||
}()
|
||||
|
||||
s.doLoginOneFactor(ctx, s.T(), "john", "password", false, "")
|
||||
s.verifyURLIs(ctx, s.T(), HomeBaseURL+"/")
|
||||
s.doVisit(s.T(), GetLoginBaseURL())
|
||||
s.verifyIsAuthenticatedPage(ctx, s.T())
|
||||
s.doLoginOneFactor(s.T(), s.Context(ctx), "john", "password", false, "")
|
||||
s.verifyIsHome(s.T(), s.Context(ctx))
|
||||
s.doVisit(s.T(), s.Context(ctx), GetLoginBaseURL())
|
||||
s.verifyIsAuthenticatedPage(s.T(), s.Context(ctx))
|
||||
}
|
||||
|
||||
func (s *OneFactorOnlyWebSuite) TestShouldRedirectAlreadyAuthenticatedUser() {
|
||||
ctx, cancel := context.WithTimeout(context.Background(), 20*time.Second)
|
||||
defer cancel()
|
||||
defer func() {
|
||||
cancel()
|
||||
s.collectScreenshot(ctx.Err(), s.Page)
|
||||
}()
|
||||
|
||||
s.doLoginOneFactor(ctx, s.T(), "john", "password", false, "")
|
||||
s.verifyURLIs(ctx, s.T(), HomeBaseURL+"/")
|
||||
s.doLoginOneFactor(s.T(), s.Context(ctx), "john", "password", false, "")
|
||||
s.verifyIsHome(s.T(), s.Context(ctx))
|
||||
|
||||
s.doVisit(s.T(), fmt.Sprintf("%s?rd=https://singlefactor.example.com:8080/secret.html", GetLoginBaseURL()))
|
||||
s.verifyURLIs(ctx, s.T(), "https://singlefactor.example.com:8080/secret.html")
|
||||
s.doVisit(s.T(), s.Context(ctx), fmt.Sprintf("%s?rd=https://singlefactor.example.com:8080/secret.html", GetLoginBaseURL()))
|
||||
s.verifySecretAuthorized(s.T(), s.Context(ctx))
|
||||
s.verifyURLIs(s.T(), s.Context(ctx), "https://singlefactor.example.com:8080/secret.html")
|
||||
}
|
||||
|
||||
func (s *OneFactorOnlyWebSuite) TestShouldNotRedirectAlreadyAuthenticatedUserToUnsafeURL() {
|
||||
ctx, cancel := context.WithTimeout(context.Background(), 20*time.Second)
|
||||
defer cancel()
|
||||
defer func() {
|
||||
cancel()
|
||||
s.collectScreenshot(ctx.Err(), s.Page)
|
||||
}()
|
||||
|
||||
s.doLoginOneFactor(ctx, s.T(), "john", "password", false, "")
|
||||
s.verifyURLIs(ctx, s.T(), HomeBaseURL+"/")
|
||||
s.doLoginOneFactor(s.T(), s.Context(ctx), "john", "password", false, "")
|
||||
s.verifyIsHome(s.T(), s.Context(ctx))
|
||||
|
||||
// Visit the login page and wait for redirection to 2FA page with success icon displayed.
|
||||
s.doVisit(s.T(), fmt.Sprintf("%s?rd=https://secure.example.local:8080", GetLoginBaseURL()))
|
||||
s.verifyNotificationDisplayed(ctx, s.T(), "Redirection was determined to be unsafe and aborted. Ensure the redirection URL is correct.")
|
||||
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.")
|
||||
}
|
||||
|
||||
func (s *OneFactorOnlySuite) TestWeb() {
|
||||
|
|
|
@ -7,11 +7,11 @@ import (
|
|||
)
|
||||
|
||||
type PathPrefixSuite struct {
|
||||
*SeleniumSuite
|
||||
*RodSuite
|
||||
}
|
||||
|
||||
func NewPathPrefixSuite() *PathPrefixSuite {
|
||||
return &PathPrefixSuite{SeleniumSuite: new(SeleniumSuite)}
|
||||
return &PathPrefixSuite{RodSuite: new(RodSuite)}
|
||||
}
|
||||
|
||||
func (s *PathPrefixSuite) TestOneFactorScenario() {
|
||||
|
|
|
@ -7,11 +7,11 @@ import (
|
|||
)
|
||||
|
||||
type PostgresSuite struct {
|
||||
*SeleniumSuite
|
||||
*RodSuite
|
||||
}
|
||||
|
||||
func NewPostgresSuite() *PostgresSuite {
|
||||
return &PostgresSuite{SeleniumSuite: new(SeleniumSuite)}
|
||||
return &PostgresSuite{RodSuite: new(RodSuite)}
|
||||
}
|
||||
|
||||
func (s *PostgresSuite) TestOneFactorScenario() {
|
||||
|
|
|
@ -7,11 +7,11 @@ import (
|
|||
)
|
||||
|
||||
type ShortTimeoutsSuite struct {
|
||||
*SeleniumSuite
|
||||
*RodSuite
|
||||
}
|
||||
|
||||
func NewShortTimeoutsSuite() *ShortTimeoutsSuite {
|
||||
return &ShortTimeoutsSuite{SeleniumSuite: new(SeleniumSuite)}
|
||||
return &ShortTimeoutsSuite{RodSuite: new(RodSuite)}
|
||||
}
|
||||
|
||||
func (s *ShortTimeoutsSuite) TestDefaultRedirectionURLScenario() {
|
||||
|
|
|
@ -18,25 +18,25 @@ import (
|
|||
)
|
||||
|
||||
type StandaloneWebDriverSuite struct {
|
||||
*SeleniumSuite
|
||||
*RodSuite
|
||||
}
|
||||
|
||||
func NewStandaloneWebDriverSuite() *StandaloneWebDriverSuite {
|
||||
return &StandaloneWebDriverSuite{SeleniumSuite: new(SeleniumSuite)}
|
||||
return &StandaloneWebDriverSuite{RodSuite: new(RodSuite)}
|
||||
}
|
||||
|
||||
func (s *StandaloneWebDriverSuite) SetupSuite() {
|
||||
wds, err := StartWebDriver()
|
||||
browser, err := StartRod()
|
||||
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
|
||||
s.WebDriverSession = wds
|
||||
s.RodSession = browser
|
||||
}
|
||||
|
||||
func (s *StandaloneWebDriverSuite) TearDownSuite() {
|
||||
err := s.WebDriverSession.Stop()
|
||||
err := s.RodSession.Stop()
|
||||
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
|
@ -44,62 +44,78 @@ func (s *StandaloneWebDriverSuite) TearDownSuite() {
|
|||
}
|
||||
|
||||
func (s *StandaloneWebDriverSuite) SetupTest() {
|
||||
ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second)
|
||||
defer cancel()
|
||||
s.Page = s.doCreateTab(s.T(), HomeBaseURL)
|
||||
s.verifyIsHome(s.T(), s.Page)
|
||||
}
|
||||
|
||||
s.doLogout(ctx, s.T())
|
||||
s.WebDriverSession.doVisit(s.T(), HomeBaseURL)
|
||||
s.verifyIsHome(ctx, s.T())
|
||||
func (s *StandaloneWebDriverSuite) TearDownTest() {
|
||||
s.collectCoverage(s.Page)
|
||||
s.MustClose()
|
||||
}
|
||||
|
||||
func (s *StandaloneWebDriverSuite) TestShouldLetUserKnowHeIsAlreadyAuthenticated() {
|
||||
ctx, cancel := context.WithTimeout(context.Background(), 20*time.Second)
|
||||
defer cancel()
|
||||
defer func() {
|
||||
cancel()
|
||||
s.collectScreenshot(ctx.Err(), s.Page)
|
||||
}()
|
||||
|
||||
_ = s.doRegisterAndLogin2FA(ctx, s.T(), "john", "password", false, "")
|
||||
_ = s.doRegisterAndLogin2FA(s.T(), s.Context(ctx), "john", "password", false, "")
|
||||
|
||||
// Visit home page to change context.
|
||||
s.doVisit(s.T(), HomeBaseURL)
|
||||
s.verifyIsHome(ctx, s.T())
|
||||
s.doVisit(s.T(), s.Context(ctx), HomeBaseURL)
|
||||
s.verifyIsHome(s.T(), s.Context(ctx))
|
||||
|
||||
// Visit the login page and wait for redirection to 2FA page with success icon displayed.
|
||||
s.doVisit(s.T(), GetLoginBaseURL())
|
||||
s.verifyIsAuthenticatedPage(ctx, s.T())
|
||||
s.doVisit(s.T(), s.Context(ctx), GetLoginBaseURL())
|
||||
s.verifyIsAuthenticatedPage(s.T(), s.Context(ctx))
|
||||
}
|
||||
|
||||
func (s *StandaloneWebDriverSuite) TestShouldRedirectAlreadyAuthenticatedUser() {
|
||||
ctx, cancel := context.WithTimeout(context.Background(), 20*time.Second)
|
||||
defer cancel()
|
||||
defer func() {
|
||||
cancel()
|
||||
s.collectScreenshot(ctx.Err(), s.Page)
|
||||
}()
|
||||
|
||||
_ = s.doRegisterAndLogin2FA(ctx, s.T(), "john", "password", false, "")
|
||||
_ = s.doRegisterAndLogin2FA(s.T(), s.Context(ctx), "john", "password", false, "")
|
||||
|
||||
// Visit home page to change context.
|
||||
s.doVisit(s.T(), HomeBaseURL)
|
||||
s.verifyIsHome(ctx, s.T())
|
||||
s.doVisit(s.T(), s.Context(ctx), HomeBaseURL)
|
||||
s.verifyIsHome(s.T(), s.Context(ctx))
|
||||
|
||||
// Visit the login page and wait for redirection to 2FA page with success icon displayed.
|
||||
s.doVisit(s.T(), fmt.Sprintf("%s?rd=https://secure.example.com:8080", GetLoginBaseURL()))
|
||||
s.verifyURLIs(ctx, s.T(), "https://secure.example.com:8080/")
|
||||
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/")
|
||||
}
|
||||
|
||||
func (s *StandaloneWebDriverSuite) TestShouldNotRedirectAlreadyAuthenticatedUserToUnsafeURL() {
|
||||
ctx, cancel := context.WithTimeout(context.Background(), 20*time.Second)
|
||||
defer cancel()
|
||||
defer func() {
|
||||
cancel()
|
||||
s.collectScreenshot(ctx.Err(), s.Page)
|
||||
}()
|
||||
|
||||
_ = s.doRegisterAndLogin2FA(ctx, s.T(), "john", "password", false, "")
|
||||
_ = s.doRegisterAndLogin2FA(s.T(), s.Context(ctx), "john", "password", false, "")
|
||||
|
||||
// Visit home page to change context.
|
||||
s.doVisit(s.T(), HomeBaseURL)
|
||||
s.verifyIsHome(ctx, s.T())
|
||||
s.doVisit(s.T(), s.Context(ctx), HomeBaseURL)
|
||||
s.verifyIsHome(s.T(), s.Context(ctx))
|
||||
|
||||
// Visit the login page and wait for redirection to 2FA page with success icon displayed.
|
||||
s.doVisit(s.T(), fmt.Sprintf("%s?rd=https://secure.example.local:8080", GetLoginBaseURL()))
|
||||
s.verifyNotificationDisplayed(ctx, s.T(), "Redirection was determined to be unsafe and aborted. Ensure the redirection URL is correct.")
|
||||
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.")
|
||||
}
|
||||
|
||||
func (s *StandaloneWebDriverSuite) TestShouldCheckUserIsAskedToRegisterDevice() {
|
||||
ctx, cancel := context.WithTimeout(context.Background(), 15*time.Second)
|
||||
defer cancel()
|
||||
ctx, cancel := context.WithTimeout(context.Background(), 30*time.Second)
|
||||
defer func() {
|
||||
cancel()
|
||||
s.collectScreenshot(ctx.Err(), s.Page)
|
||||
}()
|
||||
|
||||
username := "john"
|
||||
password := "password"
|
||||
|
@ -109,21 +125,21 @@ func (s *StandaloneWebDriverSuite) TestShouldCheckUserIsAskedToRegisterDevice()
|
|||
require.NoError(s.T(), provider.DeleteTOTPSecret(username))
|
||||
|
||||
// Login one factor.
|
||||
s.doLoginOneFactor(ctx, s.T(), username, password, false, "")
|
||||
s.doLoginOneFactor(s.T(), s.Context(ctx), username, password, false, "")
|
||||
|
||||
// Check the user is asked to register a new device.
|
||||
s.WaitElementLocatedByClassName(ctx, s.T(), "state-not-registered")
|
||||
s.WaitElementLocatedByClassName(s.T(), s.Context(ctx), "state-not-registered")
|
||||
|
||||
// Then register the TOTP factor.
|
||||
s.doRegisterTOTP(ctx, s.T())
|
||||
s.doRegisterTOTP(s.T(), s.Context(ctx))
|
||||
// And logout.
|
||||
s.doLogout(ctx, s.T())
|
||||
s.doLogout(s.T(), s.Context(ctx))
|
||||
|
||||
// Login one factor again.
|
||||
s.doLoginOneFactor(ctx, s.T(), username, password, false, "")
|
||||
s.doLoginOneFactor(s.T(), s.Context(ctx), username, password, false, "")
|
||||
|
||||
// now the user should be asked to perform 2FA
|
||||
s.WaitElementLocatedByClassName(ctx, s.T(), "state-method")
|
||||
s.WaitElementLocatedByClassName(s.T(), s.Context(ctx), "state-method")
|
||||
}
|
||||
|
||||
type StandaloneSuite struct {
|
||||
|
|
|
@ -10,11 +10,11 @@ import (
|
|||
)
|
||||
|
||||
type Traefik2Suite struct {
|
||||
*SeleniumSuite
|
||||
*RodSuite
|
||||
}
|
||||
|
||||
func NewTraefik2Suite() *Traefik2Suite {
|
||||
return &Traefik2Suite{SeleniumSuite: new(SeleniumSuite)}
|
||||
return &Traefik2Suite{RodSuite: new(RodSuite)}
|
||||
}
|
||||
|
||||
func (s *Traefik2Suite) TestOneFactorScenario() {
|
||||
|
@ -30,31 +30,34 @@ func (s *Traefik2Suite) TestCustomHeaders() {
|
|||
}
|
||||
|
||||
func (s *Traefik2Suite) TestShouldKeepSessionAfterRedisRestart() {
|
||||
ctx, cancel := context.WithTimeout(context.Background(), 30*time.Second)
|
||||
defer cancel()
|
||||
|
||||
wds, err := StartWebDriver()
|
||||
s.Require().NoError(err)
|
||||
|
||||
ctx, cancel := context.WithTimeout(context.Background(), 45*time.Second)
|
||||
defer func() {
|
||||
err = wds.Stop()
|
||||
cancel()
|
||||
s.collectCoverage(s.Page)
|
||||
s.collectScreenshot(ctx.Err(), s.Page)
|
||||
s.MustClose()
|
||||
err := s.RodSession.Stop()
|
||||
s.Require().NoError(err)
|
||||
}()
|
||||
|
||||
secret := wds.doRegisterThenLogout(ctx, s.T(), "john", "password")
|
||||
browser, err := StartRod()
|
||||
s.Require().NoError(err)
|
||||
s.RodSession = browser
|
||||
|
||||
wds.doLoginTwoFactor(ctx, s.T(), "john", "password", false, secret, "")
|
||||
s.Page = s.doCreateTab(s.T(), HomeBaseURL)
|
||||
s.verifyIsHome(s.T(), s.Page)
|
||||
secret := s.doRegisterThenLogout(s.T(), s.Context(ctx), "john", "password")
|
||||
|
||||
wds.doVisit(s.T(), fmt.Sprintf("%s/secret.html", SecureBaseURL))
|
||||
wds.verifySecretAuthorized(ctx, s.T())
|
||||
s.doLoginTwoFactor(s.T(), s.Context(ctx), "john", "password", false, secret, "")
|
||||
|
||||
s.doVisit(s.T(), s.Context(ctx), fmt.Sprintf("%s/secret.html", SecureBaseURL))
|
||||
s.verifySecretAuthorized(s.T(), s.Context(ctx))
|
||||
|
||||
err = traefik2DockerEnvironment.Restart("redis")
|
||||
s.Require().NoError(err)
|
||||
|
||||
time.Sleep(5 * time.Second)
|
||||
|
||||
wds.doVisit(s.T(), fmt.Sprintf("%s/secret.html", SecureBaseURL))
|
||||
wds.verifySecretAuthorized(ctx, s.T())
|
||||
s.doVisit(s.T(), s.Context(ctx), fmt.Sprintf("%s/secret.html", SecureBaseURL))
|
||||
s.verifySecretAuthorized(s.T(), s.Context(ctx))
|
||||
}
|
||||
|
||||
func TestTraefik2Suite(t *testing.T) {
|
||||
|
|
|
@ -7,11 +7,11 @@ import (
|
|||
)
|
||||
|
||||
type TraefikSuite struct {
|
||||
*SeleniumSuite
|
||||
*RodSuite
|
||||
}
|
||||
|
||||
func NewTraefikSuite() *TraefikSuite {
|
||||
return &TraefikSuite{SeleniumSuite: new(SeleniumSuite)}
|
||||
return &TraefikSuite{RodSuite: new(RodSuite)}
|
||||
}
|
||||
|
||||
func (s *TraefikSuite) TestOneFactorScenario() {
|
||||
|
|
|
@ -1,15 +1,16 @@
|
|||
package suites
|
||||
|
||||
import (
|
||||
"github.com/go-rod/rod"
|
||||
"github.com/stretchr/testify/suite"
|
||||
"github.com/tebeka/selenium"
|
||||
)
|
||||
|
||||
// SeleniumSuite is a selenium suite.
|
||||
type SeleniumSuite struct {
|
||||
// RodSuite is a go-rod suite.
|
||||
type RodSuite struct {
|
||||
suite.Suite
|
||||
|
||||
*WebDriverSession
|
||||
*RodSession
|
||||
*rod.Page
|
||||
}
|
||||
|
||||
// CommandSuite is a command line interface suite.
|
||||
|
@ -21,8 +22,3 @@ type CommandSuite struct {
|
|||
|
||||
*DockerEnvironment
|
||||
}
|
||||
|
||||
// WebDriver return the webdriver of the suite.
|
||||
func (s *SeleniumSuite) WebDriver() selenium.WebDriver {
|
||||
return s.WebDriverSession.WebDriver
|
||||
}
|
||||
|
|
|
@ -1,11 +1,17 @@
|
|||
package suites
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"io/ioutil"
|
||||
"log"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"strconv"
|
||||
"runtime"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/go-rod/rod"
|
||||
)
|
||||
|
||||
// GetLoginBaseURL returns the URL of the login portal and the path prefix if specified.
|
||||
|
@ -17,16 +23,51 @@ func GetLoginBaseURL() string {
|
|||
return LoginBaseURL
|
||||
}
|
||||
|
||||
// GetWebDriverPort returns the port to initialize the webdriver with.
|
||||
func GetWebDriverPort() int {
|
||||
driverPort := os.Getenv("CHROMEDRIVER_PORT")
|
||||
if driverPort == "" {
|
||||
driverPort = defaultChromeDriverPort
|
||||
func (rs *RodSession) collectCoverage(page *rod.Page) {
|
||||
coverageDir := "../../web/.nyc_output"
|
||||
now := time.Now()
|
||||
|
||||
resp, err := page.Eval("JSON.stringify(window.__coverage__)")
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
|
||||
p, _ := strconv.Atoi(driverPort)
|
||||
coverageData := fmt.Sprintf("%v", resp.Value)
|
||||
|
||||
return p
|
||||
_ = os.MkdirAll(coverageDir, 0775)
|
||||
|
||||
if coverageData != "<nil>" {
|
||||
err = ioutil.WriteFile(fmt.Sprintf("%s/coverage-%d.json", coverageDir, now.Unix()), []byte(coverageData), 0664) //nolint:gosec
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
|
||||
err = filepath.Walk("../../web/.nyc_output", fixCoveragePath)
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func (rs *RodSession) collectScreenshot(err error, page *rod.Page) {
|
||||
if err == context.DeadlineExceeded && os.Getenv("CI") == stringTrue {
|
||||
base := "/buildkite/screenshots"
|
||||
build := os.Getenv("BUILDKITE_BUILD_NUMBER")
|
||||
suite := strings.ToLower(os.Getenv("SUITE"))
|
||||
job := os.Getenv("BUILDKITE_JOB_ID")
|
||||
path := filepath.Join(fmt.Sprintf("%s/%s/%s/%s", base, build, suite, job)) //nolint: gocritic
|
||||
|
||||
if err := os.MkdirAll(path, 0755); err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
|
||||
pc, _, _, _ := runtime.Caller(2)
|
||||
fn := runtime.FuncForPC(pc)
|
||||
p := "github.com/authelia/authelia/v4/internal/suites."
|
||||
r := strings.NewReplacer(p, "", "(", "", ")", "", "*", "", ".", "-")
|
||||
|
||||
page.MustScreenshotFullPage(fmt.Sprintf("%s/%s.jpg", path, r.Replace(fn.Name())))
|
||||
}
|
||||
}
|
||||
|
||||
func fixCoveragePath(path string, file os.FileInfo, err error) error {
|
||||
|
|
|
@ -1,33 +1,29 @@
|
|||
package suites
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"strings"
|
||||
"testing"
|
||||
|
||||
"github.com/go-rod/rod"
|
||||
"github.com/stretchr/testify/assert"
|
||||
"github.com/stretchr/testify/require"
|
||||
"github.com/tebeka/selenium"
|
||||
)
|
||||
|
||||
func (wds *WebDriverSession) verifyBodyContains(ctx context.Context, t *testing.T, pattern string) {
|
||||
err := wds.Wait(ctx, func(wd selenium.WebDriver) (bool, error) {
|
||||
bodyElement, err := wds.WebDriver.FindElement(selenium.ByTagName, "body")
|
||||
func (rs *RodSession) verifyBodyContains(t *testing.T, page *rod.Page, pattern string) {
|
||||
body, err := page.Element("body")
|
||||
assert.NoError(t, err)
|
||||
assert.NotNil(t, body)
|
||||
|
||||
if err != nil {
|
||||
return false, err
|
||||
text, err := body.Text()
|
||||
assert.NoError(t, err)
|
||||
assert.NotNil(t, text)
|
||||
|
||||
if strings.Contains(text, pattern) {
|
||||
err = nil
|
||||
} else {
|
||||
err = fmt.Errorf("body does not contain pattern: %s", pattern)
|
||||
}
|
||||
|
||||
if bodyElement == nil {
|
||||
return false, nil
|
||||
}
|
||||
|
||||
content, err := bodyElement.Text()
|
||||
|
||||
if err != nil {
|
||||
return false, err
|
||||
}
|
||||
|
||||
return strings.Contains(content, pattern), nil
|
||||
})
|
||||
require.NoError(t, err)
|
||||
}
|
||||
|
|
|
@ -1,10 +1,11 @@
|
|||
package suites
|
||||
|
||||
import (
|
||||
"context"
|
||||
"testing"
|
||||
|
||||
"github.com/go-rod/rod"
|
||||
)
|
||||
|
||||
func (wds *WebDriverSession) verifyIsAuthenticatedPage(ctx context.Context, t *testing.T) {
|
||||
wds.WaitElementLocatedByID(ctx, t, "authenticated-stage")
|
||||
func (rs *RodSession) verifyIsAuthenticatedPage(t *testing.T, page *rod.Page) {
|
||||
rs.WaitElementLocatedByCSSSelector(t, page, "authenticated-stage")
|
||||
}
|
||||
|
|
|
@ -1,10 +1,11 @@
|
|||
package suites
|
||||
|
||||
import (
|
||||
"context"
|
||||
"testing"
|
||||
|
||||
"github.com/go-rod/rod"
|
||||
)
|
||||
|
||||
func (wds *WebDriverSession) verifyIsConsentPage(ctx context.Context, t *testing.T) {
|
||||
wds.WaitElementLocatedByID(ctx, t, "consent-stage")
|
||||
func (rs *RodSession) verifyIsConsentPage(t *testing.T, page *rod.Page) {
|
||||
rs.WaitElementLocatedByCSSSelector(t, page, "consent-stage")
|
||||
}
|
||||
|
|
|
@ -1,10 +1,11 @@
|
|||
package suites
|
||||
|
||||
import (
|
||||
"context"
|
||||
"testing"
|
||||
|
||||
"github.com/go-rod/rod"
|
||||
)
|
||||
|
||||
func (wds *WebDriverSession) verifyIsFirstFactorPage(ctx context.Context, t *testing.T) {
|
||||
wds.WaitElementLocatedByID(ctx, t, "first-factor-stage")
|
||||
func (rs *RodSession) verifyIsFirstFactorPage(t *testing.T, page *rod.Page) {
|
||||
rs.WaitElementLocatedByCSSSelector(t, page, "first-factor-stage")
|
||||
}
|
||||
|
|
|
@ -1,11 +1,13 @@
|
|||
package suites
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"testing"
|
||||
|
||||
"github.com/go-rod/rod"
|
||||
)
|
||||
|
||||
func (wds *WebDriverSession) verifyIsHome(ctx context.Context, t *testing.T) {
|
||||
wds.verifyURLIs(ctx, t, fmt.Sprintf("%s/", HomeBaseURL))
|
||||
func (rs *RodSession) verifyIsHome(t *testing.T, page *rod.Page) {
|
||||
page.MustElementR("h1", "Access the secret")
|
||||
rs.verifyURLIs(t, page, fmt.Sprintf("%s/", HomeBaseURL))
|
||||
}
|
||||
|
|
|
@ -0,0 +1,12 @@
|
|||
package suites
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"github.com/go-rod/rod"
|
||||
)
|
||||
|
||||
func (rs *RodSession) verifyIsOIDC(t *testing.T, page *rod.Page, pattern, url string) {
|
||||
page.MustElementR("body", pattern)
|
||||
rs.verifyURLIs(t, page, url)
|
||||
}
|
|
@ -0,0 +1,13 @@
|
|||
package suites
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"testing"
|
||||
|
||||
"github.com/go-rod/rod"
|
||||
)
|
||||
|
||||
func (rs *RodSession) verifyIsPublic(t *testing.T, page *rod.Page) {
|
||||
page.MustElementR("body", "headers")
|
||||
rs.verifyURLIs(t, page, fmt.Sprintf("%s/headers", PublicBaseURL))
|
||||
}
|
|
@ -1,10 +1,11 @@
|
|||
package suites
|
||||
|
||||
import (
|
||||
"context"
|
||||
"testing"
|
||||
|
||||
"github.com/go-rod/rod"
|
||||
)
|
||||
|
||||
func (wds *WebDriverSession) verifyIsSecondFactorPage(ctx context.Context, t *testing.T) {
|
||||
wds.WaitElementLocatedByID(ctx, t, "second-factor-stage")
|
||||
func (rs *RodSession) verifyIsSecondFactorPage(t *testing.T, page *rod.Page) {
|
||||
rs.WaitElementLocatedByCSSSelector(t, page, "second-factor-stage")
|
||||
}
|
||||
|
|
|
@ -1,10 +1,11 @@
|
|||
package suites
|
||||
|
||||
import (
|
||||
"context"
|
||||
"testing"
|
||||
|
||||
"github.com/go-rod/rod"
|
||||
)
|
||||
|
||||
func (wds *WebDriverSession) verifyMailNotificationDisplayed(ctx context.Context, t *testing.T) {
|
||||
wds.verifyNotificationDisplayed(ctx, t, "An email has been sent to your address to complete the process.")
|
||||
func (rs *RodSession) verifyMailNotificationDisplayed(t *testing.T, page *rod.Page) {
|
||||
rs.verifyNotificationDisplayed(t, page, "An email has been sent to your address to complete the process.")
|
||||
}
|
||||
|
|
|
@ -1,14 +1,14 @@
|
|||
package suites
|
||||
|
||||
import (
|
||||
"context"
|
||||
"testing"
|
||||
|
||||
"github.com/go-rod/rod"
|
||||
"github.com/stretchr/testify/assert"
|
||||
)
|
||||
|
||||
func (wds *WebDriverSession) verifyNotificationDisplayed(ctx context.Context, t *testing.T, message string) {
|
||||
el := wds.WaitElementLocatedByClassName(ctx, t, "notification")
|
||||
func (rs *RodSession) verifyNotificationDisplayed(t *testing.T, page *rod.Page, message string) {
|
||||
el, err := page.ElementR(".notification", message)
|
||||
assert.NoError(t, err)
|
||||
assert.NotNil(t, el)
|
||||
wds.WaitElementTextContains(ctx, t, el, message)
|
||||
}
|
||||
|
|
|
@ -1,10 +1,11 @@
|
|||
package suites
|
||||
|
||||
import (
|
||||
"context"
|
||||
"testing"
|
||||
|
||||
"github.com/go-rod/rod"
|
||||
)
|
||||
|
||||
func (wds *WebDriverSession) verifySecretAuthorized(ctx context.Context, t *testing.T) {
|
||||
wds.WaitElementLocatedByID(ctx, t, "secret")
|
||||
func (rs *RodSession) verifySecretAuthorized(t *testing.T, page *rod.Page) {
|
||||
rs.WaitElementLocatedByCSSSelector(t, page, "secret")
|
||||
}
|
||||
|
|
|
@ -1,23 +1,13 @@
|
|||
package suites
|
||||
|
||||
import (
|
||||
"context"
|
||||
"testing"
|
||||
|
||||
"github.com/go-rod/rod"
|
||||
"github.com/stretchr/testify/require"
|
||||
"github.com/tebeka/selenium"
|
||||
)
|
||||
|
||||
func (wds *WebDriverSession) verifyURLIs(ctx context.Context, t *testing.T, url string) {
|
||||
err := wds.Wait(ctx, func(driver selenium.WebDriver) (bool, error) {
|
||||
currentURL, err := driver.CurrentURL()
|
||||
|
||||
if err != nil {
|
||||
return false, err
|
||||
}
|
||||
|
||||
return currentURL == url, nil
|
||||
})
|
||||
|
||||
require.NoError(t, err)
|
||||
func (rs *RodSession) verifyURLIs(t *testing.T, page *rod.Page, url string) {
|
||||
currentURL := page.MustInfo().URL
|
||||
require.Equal(t, url, currentURL, "they should be equal")
|
||||
}
|
||||
|
|
|
@ -1,250 +1,115 @@
|
|||
package suites
|
||||
|
||||
import (
|
||||
"context"
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"fmt"
|
||||
"io/ioutil"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"strings"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
log "github.com/sirupsen/logrus"
|
||||
"github.com/go-rod/rod"
|
||||
"github.com/go-rod/rod/lib/launcher"
|
||||
"github.com/stretchr/testify/require"
|
||||
"github.com/tebeka/selenium"
|
||||
"github.com/tebeka/selenium/chrome"
|
||||
)
|
||||
|
||||
// WebDriverSession binding a selenium service and a webdriver.
|
||||
type WebDriverSession struct {
|
||||
service *selenium.Service
|
||||
WebDriver selenium.WebDriver
|
||||
}
|
||||
|
||||
// StartWebDriverWithProxy create a selenium session.
|
||||
func StartWebDriverWithProxy(proxy string, port int) (*WebDriverSession, error) {
|
||||
driverPath := os.Getenv("CHROMEDRIVER_PATH")
|
||||
if driverPath == "" {
|
||||
driverPath = "/usr/bin/chromedriver"
|
||||
}
|
||||
|
||||
service, err := selenium.NewChromeDriverService(driverPath, port)
|
||||
|
||||
if err != nil {
|
||||
return nil, err
|
||||
// RodSession binding a chrome session with devtool protocol.
|
||||
type RodSession struct {
|
||||
Launcher *launcher.Launcher
|
||||
WebDriver *rod.Browser
|
||||
}
|
||||
|
||||
// StartRodWithProxy create a rod/chromedp session.
|
||||
func StartRodWithProxy(proxy string) (*RodSession, error) {
|
||||
browserPath := os.Getenv("BROWSER_PATH")
|
||||
if browserPath == "" {
|
||||
browserPath = "/usr/bin/chromium-browser"
|
||||
}
|
||||
|
||||
chromeCaps := chrome.Capabilities{
|
||||
Path: browserPath,
|
||||
}
|
||||
|
||||
chromeCaps.Args = append(chromeCaps.Args, "--ignore-certificate-errors")
|
||||
headless := false
|
||||
trace := true
|
||||
motion := 0 * time.Second
|
||||
|
||||
if os.Getenv("HEADLESS") != "" {
|
||||
chromeCaps.Args = append(chromeCaps.Args, "--headless")
|
||||
chromeCaps.Args = append(chromeCaps.Args, "--no-sandbox")
|
||||
headless = true
|
||||
trace = false
|
||||
motion = 0 * time.Second
|
||||
}
|
||||
|
||||
if proxy != "" {
|
||||
chromeCaps.Args = append(chromeCaps.Args, fmt.Sprintf("--proxy-server=%s", proxy))
|
||||
}
|
||||
l := launcher.New().
|
||||
Bin(browserPath).
|
||||
Proxy(proxy).
|
||||
Headless(headless).
|
||||
Devtools(true)
|
||||
url := l.MustLaunch()
|
||||
|
||||
caps := selenium.Capabilities{}
|
||||
caps.AddChrome(chromeCaps)
|
||||
browser := rod.New().
|
||||
ControlURL(url).
|
||||
Trace(trace).
|
||||
SlowMotion(motion).
|
||||
MustConnect()
|
||||
|
||||
wd, err := selenium.NewRemote(caps, fmt.Sprintf("http://localhost:%d/wd/hub", port))
|
||||
if err != nil {
|
||||
_ = service.Stop()
|
||||
browser.MustIgnoreCertErrors(true)
|
||||
|
||||
log.Fatal(err)
|
||||
}
|
||||
|
||||
return &WebDriverSession{
|
||||
service: service,
|
||||
WebDriver: wd,
|
||||
return &RodSession{
|
||||
Launcher: l,
|
||||
WebDriver: browser,
|
||||
}, nil
|
||||
}
|
||||
|
||||
// StartWebDriver create a selenium session.
|
||||
func StartWebDriver() (*WebDriverSession, error) {
|
||||
return StartWebDriverWithProxy("", GetWebDriverPort())
|
||||
// StartRod create a rod/chromedp session.
|
||||
func StartRod() (*RodSession, error) {
|
||||
return StartRodWithProxy("")
|
||||
}
|
||||
|
||||
// Stop stop the selenium session.
|
||||
func (wds *WebDriverSession) Stop() error {
|
||||
var coverage map[string]interface{}
|
||||
|
||||
coverageDir := "../../web/.nyc_output"
|
||||
time := time.Now()
|
||||
|
||||
resp, err := wds.WebDriver.ExecuteScriptRaw("return JSON.stringify(window.__coverage__)", nil)
|
||||
// Stop stop the rod/chromedp session.
|
||||
func (rs *RodSession) Stop() error {
|
||||
err := rs.WebDriver.Close()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
err = json.Unmarshal(resp, &coverage)
|
||||
if err != nil {
|
||||
rs.Launcher.Cleanup()
|
||||
|
||||
return err
|
||||
}
|
||||
|
||||
coverageData := fmt.Sprintf("%s", coverage["value"])
|
||||
|
||||
_ = os.MkdirAll(coverageDir, 0775)
|
||||
|
||||
err = ioutil.WriteFile(fmt.Sprintf("%s/coverage-%d.json", coverageDir, time.Unix()), []byte(coverageData), 0664) //nolint:gosec
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
err = filepath.Walk("../../web/.nyc_output", fixCoveragePath)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
err = wds.WebDriver.Quit()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return wds.service.Stop()
|
||||
}
|
||||
|
||||
// WithWebdriver run some actions against a webdriver.
|
||||
func WithWebdriver(fn func(webdriver selenium.WebDriver) error) error {
|
||||
wds, err := StartWebDriver()
|
||||
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
defer wds.Stop() //nolint:errcheck // TODO: Legacy code, consider refactoring time permitting.
|
||||
|
||||
return fn(wds.WebDriver)
|
||||
}
|
||||
|
||||
// Wait wait until condition holds true.
|
||||
func (wds *WebDriverSession) Wait(ctx context.Context, condition selenium.Condition) error {
|
||||
done := make(chan error, 1)
|
||||
|
||||
go func() {
|
||||
done <- wds.WebDriver.Wait(condition)
|
||||
}()
|
||||
|
||||
select {
|
||||
case <-ctx.Done():
|
||||
return errors.New("waiting timeout reached")
|
||||
case err := <-done:
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
func (wds *WebDriverSession) waitElementLocated(ctx context.Context, t *testing.T, by, value string) selenium.WebElement {
|
||||
var el selenium.WebElement
|
||||
|
||||
err := wds.Wait(ctx, func(driver selenium.WebDriver) (bool, error) {
|
||||
var err error
|
||||
el, err = driver.FindElement(by, value)
|
||||
|
||||
if err != nil {
|
||||
if strings.Contains(err.Error(), "no such element") {
|
||||
return false, nil
|
||||
}
|
||||
return false, err
|
||||
}
|
||||
|
||||
return el != nil, nil
|
||||
})
|
||||
|
||||
require.NoError(t, err)
|
||||
require.NotNil(t, el)
|
||||
|
||||
return el
|
||||
}
|
||||
|
||||
func (wds *WebDriverSession) waitElementsLocated(ctx context.Context, t *testing.T, by, value string) []selenium.WebElement {
|
||||
var el []selenium.WebElement
|
||||
|
||||
err := wds.Wait(ctx, func(driver selenium.WebDriver) (bool, error) {
|
||||
var err error
|
||||
el, err = driver.FindElements(by, value)
|
||||
|
||||
if err != nil {
|
||||
if strings.Contains(err.Error(), "no such element") {
|
||||
return false, nil
|
||||
}
|
||||
return false, err
|
||||
}
|
||||
|
||||
return el != nil, nil
|
||||
})
|
||||
|
||||
require.NoError(t, err)
|
||||
require.NotNil(t, el)
|
||||
|
||||
return el
|
||||
}
|
||||
|
||||
// WaitElementLocatedByID wait an element is located by id.
|
||||
func (wds *WebDriverSession) WaitElementLocatedByID(ctx context.Context, t *testing.T, id string) selenium.WebElement {
|
||||
return wds.waitElementLocated(ctx, t, selenium.ByID, id)
|
||||
}
|
||||
|
||||
// WaitElementLocatedByTagName wait an element is located by tag name.
|
||||
func (wds *WebDriverSession) WaitElementLocatedByTagName(ctx context.Context, t *testing.T, tagName string) selenium.WebElement {
|
||||
return wds.waitElementLocated(ctx, t, selenium.ByTagName, tagName)
|
||||
}
|
||||
|
||||
// WaitElementLocatedByClassName wait an element is located by class name.
|
||||
func (wds *WebDriverSession) WaitElementLocatedByClassName(ctx context.Context, t *testing.T, className string) selenium.WebElement {
|
||||
return wds.waitElementLocated(ctx, t, selenium.ByClassName, className)
|
||||
}
|
||||
func (rs *RodSession) WaitElementLocatedByClassName(t *testing.T, page *rod.Page, className string) *rod.Element {
|
||||
e, err := page.Element("." + className)
|
||||
require.NoError(t, err)
|
||||
require.NotNil(t, e)
|
||||
|
||||
// WaitElementLocatedByLinkText wait an element is located by link text.
|
||||
func (wds *WebDriverSession) WaitElementLocatedByLinkText(ctx context.Context, t *testing.T, linkText string) selenium.WebElement {
|
||||
return wds.waitElementLocated(ctx, t, selenium.ByLinkText, linkText)
|
||||
return e
|
||||
}
|
||||
|
||||
// WaitElementLocatedByCSSSelector wait an element is located by class name.
|
||||
func (wds *WebDriverSession) WaitElementLocatedByCSSSelector(ctx context.Context, t *testing.T, cssSelector string) selenium.WebElement {
|
||||
return wds.waitElementLocated(ctx, t, selenium.ByCSSSelector, cssSelector)
|
||||
func (rs *RodSession) WaitElementLocatedByCSSSelector(t *testing.T, page *rod.Page, cssSelector string) *rod.Element {
|
||||
e, err := page.Element("#" + cssSelector)
|
||||
require.NoError(t, err)
|
||||
require.NotNil(t, e)
|
||||
|
||||
return e
|
||||
}
|
||||
|
||||
// WaitElementsLocatedByCSSSelector wait an element is located by CSS selector.
|
||||
func (wds *WebDriverSession) WaitElementsLocatedByCSSSelector(ctx context.Context, t *testing.T, cssSelector string) []selenium.WebElement {
|
||||
return wds.waitElementsLocated(ctx, t, selenium.ByCSSSelector, cssSelector)
|
||||
func (rs *RodSession) WaitElementsLocatedByCSSSelector(t *testing.T, page *rod.Page, cssSelector string) rod.Elements {
|
||||
e, err := page.Elements("#" + cssSelector)
|
||||
require.NoError(t, err)
|
||||
require.NotNil(t, e)
|
||||
|
||||
return e
|
||||
}
|
||||
|
||||
// WaitElementTextContains wait the text of an element contains a pattern.
|
||||
func (wds *WebDriverSession) WaitElementTextContains(ctx context.Context, t *testing.T, element selenium.WebElement, pattern string) {
|
||||
err := wds.Wait(ctx, func(driver selenium.WebDriver) (bool, error) {
|
||||
text, err := element.Text()
|
||||
func (rs *RodSession) waitBodyContains(t *testing.T, page *rod.Page, pattern string) {
|
||||
text, err := page.MustElementR("body", pattern).Text()
|
||||
require.NoError(t, err)
|
||||
require.NotNil(t, text)
|
||||
|
||||
if err != nil {
|
||||
return false, err
|
||||
if strings.Contains(text, pattern) {
|
||||
err = nil
|
||||
} else {
|
||||
err = fmt.Errorf("body does not contain pattern: %s", pattern)
|
||||
}
|
||||
|
||||
return strings.Contains(text, pattern), nil
|
||||
})
|
||||
require.NoError(t, err)
|
||||
}
|
||||
|
||||
func (wds *WebDriverSession) waitBodyContains(ctx context.Context, t *testing.T, pattern string) {
|
||||
err := wds.Wait(ctx, func(driver selenium.WebDriver) (bool, error) {
|
||||
text, err := wds.WaitElementLocatedByTagName(ctx, t, "body").Text()
|
||||
|
||||
if err != nil {
|
||||
return false, err
|
||||
}
|
||||
|
||||
return strings.Contains(text, pattern), nil
|
||||
})
|
||||
require.NoError(t, err)
|
||||
}
|
||||
|
|
|
@ -33,7 +33,7 @@ const (
|
|||
|
||||
const (
|
||||
// Hour is an int based representation of the time unit.
|
||||
Hour = time.Minute * 60
|
||||
Hour = time.Minute * 60 //nolint: revive
|
||||
|
||||
// Day is an int based representation of the time unit.
|
||||
Day = Hour * 24
|
||||
|
|
Loading…
Reference in New Issue