diff --git a/pkg/provider/pingfed/example/password-expired.html b/pkg/provider/pingfed/example/password-expired.html new file mode 100644 index 000000000..7a4dee028 --- /dev/null +++ b/pkg/provider/pingfed/example/password-expired.html @@ -0,0 +1,62 @@ + + + +
+ Change Password
+
+
+
+

+
+ +

+
+

+ +
+
+
+ +
+ +
+ +
+ +
+ Change Password +
+
+

+

+

+

+

To change your password, please access the password management system.
+


+

Your password is expired and must be changed.
+

+


+
+
+ +
+

+

+


+

+



+ + + \ No newline at end of file diff --git a/pkg/provider/pingfed/pingfed.go b/pkg/provider/pingfed/pingfed.go index 70882978d..af0c71b04 100644 --- a/pkg/provider/pingfed/pingfed.go +++ b/pkg/provider/pingfed/pingfed.go @@ -166,6 +166,9 @@ func (ac *Client) follow(ctx context.Context, req *http.Request) (string, error) } else if docIsWebAuthn(doc) { logger.WithField("type", "webauthn").Debug("doc detect") handler = ac.handleWebAuthn + } else if docIsPingMessage(doc) { + logger.WithField("type", "ping-message").Debug("doc detect") + handler = ac.handlePingMessage } else if docIsError(doc) { logger.WithField("type", "error").Debug("doc detect") pingError := strings.TrimSpace(doc.Find("div.ping-error").Text()) @@ -569,6 +572,28 @@ func (ac *Client) handleSwipe(ctx context.Context, doc *goquery.Document) (conte return ctx, req, err } +func (ac *Client) handlePingMessage(ctx context.Context, doc *goquery.Document) (context.Context, *http.Request, error) { + // Extract the error message + messages := doc.Find(".ping-messages>.ping-error") + pingError := strings.TrimSpace(messages.Text()) + + // Print out the link to change password if we can find it + if strings.Contains(pingError, "password is expired") { + changePassLink := doc.Find("#changePassLink>a") + if changePassLink.Size() == 1 { + if link, ok := changePassLink.Attr("href"); ok { + changePassText := strings.TrimSpace(changePassLink.Text()) + if changePassText == "" { + changePassText = "Change your password" + } + log.Printf("%s: %s", changePassText, link) + } + } + } + + return ctx, nil, errors.New(pingError) +} + func (ac *Client) handleFormRedirect(ctx context.Context, doc *goquery.Document) (context.Context, *http.Request, error) { form, err := page.NewFormFromDocument(doc, "") if err != nil { @@ -601,6 +626,10 @@ func docIsLogin(doc *goquery.Document) bool { doc.Has("input[name=\"pf.pass\"]").Size() == 1 } +func docIsPingMessage(doc *goquery.Document) bool { + return doc.Has(".ping-messages>.ping-error").Size() > 0 +} + func docIsToken(doc *goquery.Document) bool { return doc.Has("#login-password-field").Size() == 0 && doc.Has("input[name=\"pf.pass\"]").Size() == 1 diff --git a/pkg/provider/pingfed/pingfed_test.go b/pkg/provider/pingfed/pingfed_test.go index 028488b2a..cc8612588 100644 --- a/pkg/provider/pingfed/pingfed_test.go +++ b/pkg/provider/pingfed/pingfed_test.go @@ -49,6 +49,7 @@ var docTests = []struct { {docIsWebAuthn, "example/swipe.html", false}, {docIsWebAuthn, "example/form-redirect.html", false}, {docIsWebAuthn, "example/webauthn.html", true}, + {docIsPingMessage, "example/password-expired.html", true}, } func TestDocTypes(t *testing.T) { @@ -182,3 +183,15 @@ func TestHandleWebAuthn(t *testing.T) { s := string(b[:]) require.Contains(t, s, "isWebAuthnSupportedByBrowser=true") } + +func TestHandlePasswordExpired(t *testing.T) { + data, err := ioutil.ReadFile("example/password-expired.html") + require.Nil(t, err) + + doc, err := goquery.NewDocumentFromReader(bytes.NewReader(data)) + require.Nil(t, err) + + ac := Client{} + _, _, err = ac.handlePingMessage(context.Background(), doc) + require.Error(t, err, "Your password is expired and must be changed.") +}