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
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
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.")
+}