Skip to content

Commit

Permalink
Merge branch 'master' into master
Browse files Browse the repository at this point in the history
  • Loading branch information
mapkon authored Sep 5, 2023
2 parents e0e6ded + 4fdd078 commit 0e8ad52
Show file tree
Hide file tree
Showing 8 changed files with 256 additions and 8 deletions.
4 changes: 2 additions & 2 deletions go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -9,10 +9,10 @@ require (
github.com/PuerkitoBio/goquery v1.8.1
github.com/alecthomas/kingpin v2.2.6+incompatible
github.com/avast/retry-go v3.0.0+incompatible
github.com/aws/aws-sdk-go v1.44.323
github.com/aws/aws-sdk-go v1.45.2
github.com/beevik/etree v1.2.0
github.com/danieljoos/wincred v1.2.0
github.com/google/uuid v1.3.0
github.com/google/uuid v1.3.1
github.com/h2non/gock v1.2.0
github.com/keybase/go-keychain v0.0.0-20211119201326-e02f34051621
github.com/marshallbrekka/go-u2fhost v0.0.0-20210111072507-3ccdec8c8105
Expand Down
8 changes: 4 additions & 4 deletions go.sum
Original file line number Diff line number Diff line change
Expand Up @@ -24,8 +24,8 @@ github.com/andybalholm/cascadia v1.3.1/go.mod h1:R4bJ1UQfqADjvDa4P6HZHLh/3OxWWEq
github.com/armon/consul-api v0.0.0-20180202201655-eb2c6b5be1b6/go.mod h1:grANhF5doyWs3UAsr3K4I6qtAmlQcZDesFNEHPZAzj8=
github.com/avast/retry-go v3.0.0+incompatible h1:4SOWQ7Qs+oroOTQOYnAHqelpCO0biHSxpiH9JdtuBj0=
github.com/avast/retry-go v3.0.0+incompatible/go.mod h1:XtSnn+n/sHqQIpZ10K1qAevBhOOCWBLXXy3hyiqqBrY=
github.com/aws/aws-sdk-go v1.44.323 h1:97/dn93DWrN1VfhAWQ2tV+xuE6oO/LO9rSsEsuC4PLU=
github.com/aws/aws-sdk-go v1.44.323/go.mod h1:aVsgQcEevwlmQ7qHE9I3h+dtQgpqhFB+i8Phjh7fkwI=
github.com/aws/aws-sdk-go v1.45.2 h1:hTong9YUklQKqzrGk3WnKABReb5R8GjbG4Y6dEQfjnk=
github.com/aws/aws-sdk-go v1.45.2/go.mod h1:aVsgQcEevwlmQ7qHE9I3h+dtQgpqhFB+i8Phjh7fkwI=
github.com/bearsh/hid v1.3.0 h1:GLNa8hvEzJxzQEEpheDUr2SivvH7iwTrJrDhFKutfX8=
github.com/bearsh/hid v1.3.0/go.mod h1:KbQByg8WfPr92v7aaKAHTtZUEVG7e2XRpcF8+TopQv8=
github.com/beevik/etree v1.2.0 h1:l7WETslUG/T+xOPs47dtd6jov2Ii/8/OjCldk5fYfQw=
Expand Down Expand Up @@ -75,8 +75,8 @@ github.com/golang/protobuf v1.3.1/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5y
github.com/google/btree v1.0.0/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ=
github.com/google/go-cmp v0.2.0/go.mod h1:oXzfMopK8JAjlY9xF4vHSVASa0yLyX7SntLO5aqRK0M=
github.com/google/go-cmp v0.5.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
github.com/google/uuid v1.3.0 h1:t6JiXgmwXMjEs8VusXIJk2BXHsn+wx8BZdTaoZ5fu7I=
github.com/google/uuid v1.3.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
github.com/google/uuid v1.3.1 h1:KjJaJ9iWZ3jOFZIf1Lqf4laDRCasjl0BCmnEGxkdLb4=
github.com/google/uuid v1.3.1/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
github.com/gorilla/websocket v1.4.0/go.mod h1:E7qHFY5m1UJ88s3WnNqhKjPHQ0heANvMoAMk2YaljkQ=
github.com/grpc-ecosystem/go-grpc-middleware v1.0.0/go.mod h1:FiyG127CGDf3tlThmgyCl78X/SZQqEOJBCDaAfeWzPs=
github.com/grpc-ecosystem/go-grpc-prometheus v1.2.0/go.mod h1:8NvIoxWQoOIhqOTXgfV/d3M/q6VIi02HzZEHgUlZvzk=
Expand Down
3 changes: 1 addition & 2 deletions pkg/provider/browser/browser.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,6 @@ package browser

import (
"errors"
"fmt"
"net/url"
"regexp"

Expand Down Expand Up @@ -94,7 +93,7 @@ var getSAMLResponse = func(page playwright.Page, loginDetails *creds.LoginDetail
return "", err
}

fmt.Println("waiting ...")
logger.Info("waiting ...")
r, _ := page.WaitForRequest(signin_re)
data, err := r.PostData()
if err != nil {
Expand Down
6 changes: 6 additions & 0 deletions pkg/provider/okta/okta.go
Original file line number Diff line number Diff line change
Expand Up @@ -863,6 +863,12 @@ func verifyMfa(oc *Client, oktaOrgHost string, loginDetails *creds.LoginDetails,
return "", err
}
body = updatedContext.challengeResponseBody
if gjson.Get(body, "status").String() == "MFA_CHALLENGE" {
correctAnswer := gjson.Get(body, "_embedded.factor._embedded.challenge.correctAnswer").String()
if correctAnswer != "" {
log.Printf("Correct Answer: %s", correctAnswer)
}
}

case "TIMEOUT":
log.Println(" Timeout")
Expand Down
73 changes: 73 additions & 0 deletions pkg/provider/okta/okta_test.go
Original file line number Diff line number Diff line change
@@ -1,13 +1,16 @@
package okta

import (
"bytes"
"crypto/tls"
"errors"
"fmt"
"io"
"log"
"net/http"
"net/http/httptest"
"net/url"
"os"
"strings"
"testing"
"testing/iotest"
Expand Down Expand Up @@ -191,6 +194,76 @@ func TestGetMfaChallengeContext(t *testing.T) {
})
}

func TestVerifyMfa(t *testing.T) {
verifyCounter := 0
ts := httptest.NewTLSServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
switch r.URL.Path {
case "/verify":
switch verifyCounter {
case 0, 1:
_, err := w.Write([]byte(`{
"stateToken": "TOKEN_2",
"status": "MFA_CHALLENGE",
"factorResult": "WAITING",
"_embedded": {
"factor": {
"id": "PUSH",
"provider": "OKTA",
"factorType": "PUSH",
"_embedded": {
"challenge": {
"correctAnswer": 92
}
}
}
}
}`))
assert.Nil(t, err)
case 2:
_, err := w.Write([]byte(`{
"sessionToken": "TOKEN_3",
"status": "SUCCESS"
}`))
assert.Nil(t, err)
}
verifyCounter++
default:
http.Error(w, http.StatusText(http.StatusBadRequest), http.StatusBadRequest)
}
}))
defer ts.Close()

t.Run("Push", func(t *testing.T) {
oc, loginDetails := setupTestClient(t, ts, "PUSH")

err := oc.setDeviceTokenCookie(loginDetails)
assert.Nil(t, err)

var out bytes.Buffer
log.SetOutput(&out)
context, err := verifyMfa(oc, "", &creds.LoginDetails{}, fmt.Sprintf(`{
"stateToken": "TOKEN_1",
"_embedded": {
"factors": [
{
"id": "PUSH",
"provider": "OKTA",
"factorType": "PUSH",
"_links": {
"verify": { "href": "%s/verify" }
}
}
]
}
}`, ts.URL))
log.SetOutput(os.Stderr)
assert.Nil(t, err)
assert.Contains(t, out.String(), "Correct Answer: 92")

assert.Equal(t, context, "TOKEN_3")
})
}

func setupTestClient(t *testing.T, ts *httptest.Server, mfa string) (*Client, *creds.LoginDetails) {
testTransport := http.DefaultTransport.(*http.Transport).Clone()
testTransport.TLSClientConfig = &tls.Config{InsecureSkipVerify: true}
Expand Down
104 changes: 104 additions & 0 deletions pkg/provider/pingfed/example/swipe-number.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,104 @@
<!DOCTYPE html>
<html>
<head>
<title></title>
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<meta name="format-detection" content="telephone=no">
<meta http-equiv="x-ua-compatible" content="IE=edge">
<link rel="stylesheet" href="/pingid/assets/css/main-v21.144.css" media="screen" title="no title" charset="utf-8">
<link rel="stylesheet" media="screen" type="text/css" href="/pingid/assets/css/jsdisabled.css" />
<script type="text/javascript" src="/pingid/assets/js/jquery-1.11.1.min.js"></script>
<script type="text/javascript" src="/pingid/assets/js/spin.js"></script>
</head>
<body>
<noscript>
<!DOCTYPE html>
<html>
<head>
<title></title>
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<meta name = "format-detection" content = "telephone=no">
<link rel="stylesheet" href="/pingid/assets/css/jsdisabled.css" media="screen" title="no title" charset="utf-8">
</head>
<body>
<div class="nojspage">
<div class="window error">
<div class="content">
<div class="status"></div>
<div class="title-text">
Important
</div>
<div class="error-text">
<div class="text">
PingID requires Javascript to be enabled. If the problem persists, please contact your administrator.
</div>
</div>
</div>
</div>
<div class="footer">
<div class="pingid_logo"></div>
<div class="copyright">
Copyright &copy; 2003-2018 Ping Identity Corporation. All rights reserved.
</div>
</div>
</div>
</body>
</html>
<style type="text/css">
.dialog { display:none; }
</style>
</noscript>
<div class="dialog">
<div class="window authenticating">
<div class="content">
<h1>
Authentication
</h1>
<p>
Select the number displayed in your PingID mobile app
</p>
<div class="status">
<div class="numbermatching">10</div>
</div>
<div class="text device">
Authenticating on
<div class="device-name">
iPhone X
</div>
</div>
<a class="button" href="/pingid/ppm/devices">Change Device</a>
</div>
</div>
<div class="admin-message">corporate motd</div>
<div class="footer">
<a class="button settings-btn" href="https://authenticator.pingone.com/pingid/ppm/settings">Settings</a>
<div class="logo"></div>
<div class="copyright">
Copyright &copy; 2003-2018 Ping Identity Corporation. All rights reserved.
</div>
</div>
<!-- This can be removed once Async FF is removed. -->
<form method="POST" action="https://authenticator.pingone.com/pingid/ppm/auth/status" id="form1">
<input type="hidden" name="csrfToken" id="csrfToken" value="abdb4264-6aab-4e1a-a830-63c9188e2395" encode="false" />
<noscript><input type="submit" value="Resume"/></noscript>
</form>
<form method="GET" action="https://authenticator.pingone.com/pingid/ppm/auth/response" id="reponseView">
<input type="hidden" name="csrfToken" id="csrfToken" value="abdb4264-6aab-4e1a-a830-63c9188e2395" encode="false" />
<input type="hidden" name="status" id="status" encode="false" />
<noscript><input type="submit" value="Resume"/></noscript>
</form>
<form method="GET" action="https://authenticator.pingone.com/pingid/ppm/auth/response" id="errorReponseView">
<input type="hidden" name="csrfToken" id="csrfToken" value="abdb4264-6aab-4e1a-a830-63c9188e2395" encode="false" />
</form>
<div id="authModelSection">
<input type="hidden" name="isAsync" id="isAsync" value="true" encode="false" />
<input type="hidden" name="actionLink" id="actionLink" value="https://authenticator.pingone.com/pingid/ppm/auth/status" encode="false" />
<input type="hidden" name="useCodeUrl" id="useCodeUrl" value="/pingid/ppm/auth/usecode" encode="false" />
</div>
<script type="text/javascript" src="/pingid/assets/js/utils/spinner.js"></script>
<script type="text/javascript" src="/pingid/assets/js/utils/getAuthStatus.js"></script>
<script type="text/javascript" src="/pingid/assets/js/utils/authenticationselfsubmit.js"></script>
<script type="text/javascript" src="/pingid/assets/js/utils/usecodesubmit.js"></script>
</div>
</body>
</html>
5 changes: 5 additions & 0 deletions pkg/provider/pingfed/pingfed.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import (
"encoding/base64"
"fmt"
"io"
"log"
"net/http"
"net/url"
"time"
Expand Down Expand Up @@ -172,6 +173,10 @@ func (ac *Client) handleSwipe(ctx context.Context, doc *goquery.Document, _ *url
return ctx, nil, errors.Wrap(err, "error extracting swipe status form")
}

if number := doc.Find("div.numbermatching").Text(); number != "" {
log.Printf("Select %v in your PingID mobile app ...\n", number)
}

// poll status. request must specifically be a GET
form.Method = "GET"
req, err := form.BuildRequest()
Expand Down
61 changes: 61 additions & 0 deletions pkg/provider/pingfed/pingfed_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,9 +3,12 @@ package pingfed
import (
"bytes"
"context"
"crypto/tls"
"io"
"log"
"net/http"
"net/http/cookiejar"
"net/http/httptest"
"net/url"
"os"
"testing"
Expand Down Expand Up @@ -33,26 +36,31 @@ var docTests = []struct {
{docIsLogin, "example/login2.html", true},
{docIsLogin, "example/otp.html", false},
{docIsLogin, "example/swipe.html", false},
{docIsLogin, "example/swipe-number.html", false},
{docIsLogin, "example/form-redirect.html", false},
{docIsLogin, "example/webauthn.html", false},
{docIsOTP, "example/login.html", false},
{docIsOTP, "example/otp.html", true},
{docIsOTP, "example/swipe.html", false},
{docIsOTP, "example/swipe-number.html", false},
{docIsOTP, "example/form-redirect.html", false},
{docIsOTP, "example/webauthn.html", false},
{docIsSwipe, "example/login.html", false},
{docIsSwipe, "example/otp.html", false},
{docIsSwipe, "example/swipe.html", true},
{docIsSwipe, "example/swipe-number.html", true},
{docIsSwipe, "example/form-redirect.html", false},
{docIsSwipe, "example/webauthn.html", false},
{docIsFormRedirect, "example/login.html", false},
{docIsFormRedirect, "example/otp.html", false},
{docIsFormRedirect, "example/swipe.html", false},
{docIsFormRedirect, "example/swipe-number.html", false},
{docIsFormRedirect, "example/form-redirect.html", true},
{docIsFormRedirect, "example/webauthn.html", false},
{docIsWebAuthn, "example/login.html", false},
{docIsWebAuthn, "example/otp.html", false},
{docIsWebAuthn, "example/swipe.html", false},
{docIsWebAuthn, "example/swipe-number.html", false},
{docIsWebAuthn, "example/form-redirect.html", false},
{docIsWebAuthn, "example/webauthn.html", true},
}
Expand Down Expand Up @@ -135,6 +143,59 @@ func TestHandleOTP(t *testing.T) {
require.Contains(t, s, "csrfToken=some-token")
}

func TestHandleSwipe(t *testing.T) {
ts := httptest.NewTLSServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
switch r.URL.Path {
case "/pingid/ppm/auth/status":
_, err := w.Write([]byte("{\"status\":\"OK\"}"))
require.Nil(t, err)
default:
http.Error(w, http.StatusText(http.StatusBadRequest), http.StatusBadRequest)
}
}))
defer ts.Close()

performTest := func(data []byte) bytes.Buffer {
doc, err := goquery.NewDocumentFromReader(bytes.NewReader(bytes.ReplaceAll(data, []byte("https://authenticator.pingone.com"), []byte(ts.URL))))
require.Nil(t, err)

testTransport := http.DefaultTransport.(*http.Transport).Clone()
testTransport.TLSClientConfig = &tls.Config{InsecureSkipVerify: true}
ac := Client{
client: &provider.HTTPClient{Client: http.Client{Transport: testTransport}, Options: &provider.HTTPClientOptions{IsWithRetries: false}},
}

var out bytes.Buffer
log.SetOutput(&out)
_, req, err := ac.handleSwipe(context.Background(), doc, &url.URL{})
log.SetOutput(os.Stderr)
require.Nil(t, err)

b, err := io.ReadAll(req.Body)
require.Nil(t, err)

s := string(b[:])
require.Contains(t, s, "csrfToken=abdb4264-6aab-4e1a-a830-63c9188e2395")

return out
}

t.Run("Swipe", func(t *testing.T) {
data, err := os.ReadFile("example/swipe.html")
require.Nil(t, err)

performTest(data)
})

t.Run("Swipe with number", func(t *testing.T) {
data, err := os.ReadFile("example/swipe-number.html")
require.Nil(t, err)

out := performTest(data)
require.Contains(t, out.String(), "Select 10 in your PingID mobile app ...")
})
}

func TestHandleFormRedirect(t *testing.T) {
data, err := os.ReadFile("example/form-redirect.html")
require.Nil(t, err)
Expand Down

0 comments on commit 0e8ad52

Please sign in to comment.