diff --git a/pkg/provider/browser/browser.go b/pkg/provider/browser/browser.go index 0681589b1..40661b53e 100644 --- a/pkg/provider/browser/browser.go +++ b/pkg/provider/browser/browser.go @@ -32,12 +32,12 @@ type Client struct { // New create new browser based client func New(idpAccount *cfg.IDPAccount) (*Client, error) { return &Client{ - Headless: idpAccount.Headless, - BrowserDriverDir: idpAccount.BrowserDriverDir, - BrowserType: strings.ToLower(idpAccount.BrowserType), - BrowserExecutablePath: idpAccount.BrowserExecutablePath, - Timeout: idpAccount.Timeout, - BrowserAutoFill: idpAccount.BrowserAutoFill, + Headless: idpAccount.Headless, + BrowserDriverDir: idpAccount.BrowserDriverDir, + BrowserType: strings.ToLower(idpAccount.BrowserType), + BrowserExecutablePath: idpAccount.BrowserExecutablePath, + Timeout: idpAccount.Timeout, + BrowserAutoFill: idpAccount.BrowserAutoFill, }, nil } @@ -202,45 +202,63 @@ var getSAMLResponse = func(page playwright.Page, loginDetails *creds.LoginDetail return values.Get("SAMLResponse"), nil } +func locatedExists(locator playwright.Locator) (bool, error) { + count, err := locator.Count() + if err != nil { + return false, err + } + + return count > 0, nil +} + +func float64Ptr(n int) *float64 { + f64 := float64(n) + return &f64 +} + var autoFill = func(page playwright.Page, loginDetails *creds.LoginDetails) error { passwordField := page.Locator("input[type='password']") - err := passwordField.WaitFor(playwright.LocatorWaitForOptions{ - State: playwright.WaitForSelectorStateVisible, + _ = passwordField.WaitFor(playwright.LocatorWaitForOptions{ + State: playwright.WaitForSelectorStateVisible, + Timeout: float64Ptr(3000), }) + passwordFieldExists, err := locatedExists(passwordField) if err != nil { return err } - err = passwordField.Fill(loginDetails.Password) - if err != nil { - return err + if passwordFieldExists { + err = passwordField.Fill(loginDetails.Password) + if err != nil { + return err + } } - keyboard := page.Keyboard() - - // move to username field which is above password field - err = keyboard.Press("Shift+Tab") + usernameField := page.Locator("input[name='username']") // no need to wait since we can assume at this point that the form has been loaded + usernameFieldExists, err := locatedExists(usernameField) if err != nil { return err } - err = keyboard.InsertText(loginDetails.Username) - if err != nil { - return err + if usernameFieldExists { + err = usernameField.Fill(loginDetails.Username) + if err != nil { + return err + } } // Find the submit button or input of the form that the password field is in submitLocator := page.Locator("form", playwright.PageLocatorOptions{ Has: passwordField, }).Locator("[type='submit']") - count, err := submitLocator.Count() + submitLocatorExists, err := locatedExists(submitLocator) if err != nil { return err } // when submit locator exists, Click it - if count > 0 { + if submitLocatorExists { return submitLocator.Click() } else { // Use javascript to submit the form when no submit input or button is found _, err := page.Evaluate(`document.querySelector('input[type="password"]').form.submit()`, nil) diff --git a/pkg/provider/browser/browser_test.go b/pkg/provider/browser/browser_test.go index af702db2a..f5020f51e 100644 --- a/pkg/provider/browser/browser_test.go +++ b/pkg/provider/browser/browser_test.go @@ -203,6 +203,37 @@ func TestExpectRequestOptionsDefaultTimeout(t *testing.T) { } func TestAutoFill(t *testing.T) { + pageLocations := []string{ + "example/loginpage-without-username.html", + } + // iterate over each login page + for _, pageLocation := range pageLocations { + data, err := os.ReadFile(pageLocation) + require.Nil(t, err) + + ts := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + _, _ = w.Write(data) + })) + defer ts.Close() + + pw, _ := playwright.Run() + browser, _ := pw.Chromium.Launch() + context, _ := browser.NewContext() + page, _ := context.NewPage() + _, _ = page.Goto(ts.URL) + + loginDetails := &creds.LoginDetails{URL: ts.URL, Username: "golang", Password: "gopher"} + _ = autoFill(page, loginDetails) + + password, _ := page.Locator("input[type='password']").First().InputValue() + assert.Equal(t, "gopher", password) + + result, _ := page.Locator("div#result").Evaluate("el => el.innerText", nil) + assert.Equal(t, "golang:gopher", result) + } +} + +func TestAutoFillNoUsername(t *testing.T) { // 3 different login pages pageLocations := []string{ "example/loginpage.html", diff --git a/pkg/provider/browser/example/loginpage-without-username.html b/pkg/provider/browser/example/loginpage-without-username.html new file mode 100644 index 000000000..c0e2c935c --- /dev/null +++ b/pkg/provider/browser/example/loginpage-without-username.html @@ -0,0 +1,36 @@ + + + Fake Login Page + + +
+
+ + +
+ +
Login
+
+ + + + + + \ No newline at end of file