diff --git a/pkg/provider/pingfed/example/loginpage.html b/pkg/provider/pingfed/example/loginpage.html new file mode 100644 index 000000000..d78f8a5db --- /dev/null +++ b/pkg/provider/pingfed/example/loginpage.html @@ -0,0 +1,11 @@ + + + + Example Login Page + + +
+ +
+ + diff --git a/pkg/provider/pingfed/example/loginpage_absolute.html b/pkg/provider/pingfed/example/loginpage_absolute.html new file mode 100644 index 000000000..6c00b8e2a --- /dev/null +++ b/pkg/provider/pingfed/example/loginpage_absolute.html @@ -0,0 +1,11 @@ + + + + Example Login Page + + +
+ +
+ + diff --git a/pkg/provider/pingfed/pingfed.go b/pkg/provider/pingfed/pingfed.go index bae7682d8..69a3edb00 100644 --- a/pkg/provider/pingfed/pingfed.go +++ b/pkg/provider/pingfed/pingfed.go @@ -283,21 +283,12 @@ func (ac *Client) getLoginForm(loginDetails *creds.LoginDetails) (string, url.Va updateLoginFormData(authForm, s, loginDetails) }) - authSubmitURL := "" - - doc.Find("form").Each(func(i int, s *goquery.Selection) { - action, ok := s.Attr("action") - if !ok { - return - } - authSubmitURL = action - }) - - if authSubmitURL == "" { + authSubmitURL, err := extractAuthSubmitURL(loginDetails.URL, doc) + if err != nil { return "", nil, fmt.Errorf("unable to locate IDP authentication form submit URL") } - return fmt.Sprintf("%s%s", loginDetails.URL, authSubmitURL), authForm, nil + return authSubmitURL, authForm, nil } func updateLoginFormData(authForm url.Values, s *goquery.Selection, user *creds.LoginDetails) { @@ -321,6 +312,28 @@ func updateLoginFormData(authForm url.Values, s *goquery.Selection, user *creds. } } +func extractAuthSubmitURL(baseURL string, doc *goquery.Document) (authSubmitURL string, err error){ + doc.Find("form").Each(func(i int, s *goquery.Selection) { + action, ok := s.Attr("action") + if !ok { + return + } + authSubmitURL = action + }) + + if authSubmitURL == "" { + err = fmt.Errorf("unable to locate IDP authentication form submit URL") + return + } + + // account for relative action URI + if url, urlErr := url.ParseRequestURI(authSubmitURL); urlErr == nil && !url.IsAbs() { + authSubmitURL = fmt.Sprintf("%s%s", baseURL, authSubmitURL) + } + + return +} + func extractFormData(res *http.Response) (url.Values, string, error) { formData := url.Values{} var actionURL string diff --git a/pkg/provider/pingfed/pingfed_test.go b/pkg/provider/pingfed/pingfed_test.go index 4591c1270..4c8a7ae4f 100644 --- a/pkg/provider/pingfed/pingfed_test.go +++ b/pkg/provider/pingfed/pingfed_test.go @@ -22,3 +22,25 @@ func TestExtractMfaFormData(t *testing.T) { require.Equal(t, "https://authenticator.pingone.com/pingid/ppm/auth/poll", actionURL) require.Equal(t, url.Values{"csrfToken": []string{"fc80998c-34d8-4dd2-925c-3b3be8a0dee8"}}, mfaForm) } + +var extractAuthSubmitURLTests = []struct { + f string // input html file + expected string // expected url +}{ + {"example/loginpage.html", "https://example.com/relative/login"}, + {"example/loginpage_absolute.html", "https://other.example.com/login"}, +} + +func TestExtractAuthSubmitURL(t *testing.T) { + for _, tt := range extractAuthSubmitURLTests { + data, err := ioutil.ReadFile(tt.f) + require.Nil(t, err) + + doc, err := goquery.NewDocumentFromReader(bytes.NewReader(data)) + require.Nil(t, err) + + url, err := extractAuthSubmitURL("https://example.com", doc) + require.Nil(t, err) + require.Equal(t, tt.expected, url) + } +}