Skip to content

Commit

Permalink
feat(ping-protect): new module for Ping Protect
Browse files Browse the repository at this point in the history
- Create new module for Ping Protect
- Add protect callback support
- Add README with short summary and quick start
  • Loading branch information
cerebrl committed Feb 1, 2024
1 parent 33ff849 commit 55153c5
Show file tree
Hide file tree
Showing 25 changed files with 14,017 additions and 2 deletions.
138 changes: 138 additions & 0 deletions e2e/autoscript-apps/src/authn-protect/autoscript.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,138 @@
/*
* @forgerock/javascript-sdk
*
* autoscript.ts
*
* Copyright (c) 2020 ForgeRock. All rights reserved.
* This software may be modified and distributed under the terms
* of the MIT license. See the LICENSE file for details.
*/
// @ts-nocheck
import * as forgerock from '@forgerock/javascript-sdk';
import { PIProtect } from '@forgerock/ping-protect';
import { delay as rxDelay, map, mergeMap } from 'rxjs/operators';
import { from } from 'rxjs';

function autoscript() {
const delay = 0;

const url = new URL(window.location.href);
const amUrl = url.searchParams.get('amUrl') || 'https://auth.example.com:9443/am';
const realmPath = url.searchParams.get('realmPath') || 'root';
const un = url.searchParams.get('un') || 'sdkuser';
const pw = url.searchParams.get('pw') || 'password';
const tree = url.searchParams.get('tree') || 'TEST_LoginPingProtect';

console.log('Configure the SDK');
forgerock.Config.set({
realmPath,
tree,
serverConfig: {
baseUrl: amUrl,
},
});

try {
forgerock.SessionManager.logout();
} catch (err) {
// Do nothing
}

console.log('Initiate first step with `undefined`');
// Wrapping in setTimeout to give the test time to bind listener to console.log
setTimeout(function () {
from(forgerock.FRAuth.start())
.pipe(
mergeMap(
(step) => {
if (step.getCallbacksOfType('PingOneProtectInitializeCallback')) {
const cb = step.getCallbackOfType('PingOneProtectInitializeCallback');
const config = cb.getConfig();

console.log(JSON.stringify(config));

try {
// Asynchronous call
return PIProtect.init(config);
} catch (err) {
cb.setClientError(err.message);
}
}
},
(step) => {
return step;
},
),
rxDelay(delay),
mergeMap((step) => {
return forgerock.FRAuth.next(step);
}),
rxDelay(delay),
mergeMap((step) => {
console.log('Set values on auth tree callbacks');
step.getCallbackOfType('NameCallback').setName(un);
step.getCallbackOfType('PasswordCallback').setPassword(pw);
return forgerock.FRAuth.next(step);
}),
rxDelay(delay),
mergeMap(
(step) => {
if (step.getCallbacksOfType('PingOneProtectEvaluationCallback')) {
const cb = step.getCallbackOfType('PingOneProtectEvaluationCallback');

try {
// Asynchronous call
return PIProtect.getData();
} catch (err) {
return err;
}
}
},
(step, data) => {
return { step, data };
},
),
mergeMap(({ step, data }) => {
const cb = step.getCallbackOfType('PingOneProtectEvaluationCallback');
const shouldPause = cb.getPauseBehavioralData();

console.log(`getPauseBehavioralData: ${shouldPause}`);

if (shouldPause) {
PIProtect.pauseBehavioralData();
}

if (typeof data === 'string') {
cb.setData(data);
} else {
cb.setClientError(data.message);
}

return forgerock.FRAuth.next(step);
}),
map((step) => {
if (step.payload.status === 401) {
throw new Error('Auth_Error');
} else if (step.payload.tokenId) {
console.log('Basic login with Protect successful');
document.body.innerHTML = '<p class="Logged_In">Login successful</p>';
} else {
throw new Error('Something went wrong.');
}
}),
)
.subscribe({
error: (err) => {
console.log(`Error: ${err.message}`);
document.body.innerHTML = `<p class="Test_Complete">${err.message}</p>`;
},
complete: () => {
console.log('Test script complete');
document.body.innerHTML = `<p class="Test_Complete">Test script complete</p>`;
},
});
}, 250);
}
autoscript();

export default autoscript;
33 changes: 33 additions & 0 deletions e2e/autoscript-apps/src/authn-protect/index.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
<!DOCTYPE html>
<html>
<head>
<title>E2E Test | ForgeRock JavaScript SDK</title>

<!-- Needed only for automation with mock server -->
<meta name="referrer" content="unsafe-url" />

<link rel="shortcut icon" href="/fr-ico.png" type="image/png" />

<style>
@media (prefers-color-scheme: dark) {
html {
background-color: black;
color: white;
}
a {
color: lightblue;
}
a:visited {
color: lavender;
}
a:hover {
color: lightskyblue;
}
}
</style>
</head>

<body>
<script src="autoscript.js"></script>
</body>
</html>
1 change: 1 addition & 0 deletions e2e/autoscript-apps/src/index.html
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@
<a href="./authn-basic/index.html">AuthN: Basic</a><br />
<a href="./authn-central-login/index.html">AuthN: Central Login</a><br />
<a href="./authn-device-profile/index.html">AuthN: Device Profile</a><br />
<a href="./authn-protect/index.html">AuthN: Ping Protect</a><br />
<a href="./authn-email-suspend/index.html">AuthN: Email Suspend</a><br />
<a href="./authn-no-session/index.html">AuthN: No Session</a><br />
<a href="./authn-oauth/index.html">AuthN: OAuth</a><br />
Expand Down
1 change: 1 addition & 0 deletions e2e/autoscript-apps/webpack.config.js
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ const pages = [
'authn-basic',
'authn-central-login',
'authn-device-profile',
'authn-protect',
'authn-email-suspend',
'authn-no-session',
'authn-oauth',
Expand Down
44 changes: 44 additions & 0 deletions e2e/autoscript-suites/src/suites/authn-protect.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
/*
* @forgerock/javascript-sdk
*
* authn-basic.lc.test.ts
*
* Copyright (c) 2020 ForgeRock. All rights reserved.
* This software may be modified and distributed under the terms
* of the MIT license. See the LICENSE file for details.
*/
import { test, expect } from '@playwright/test';
import { setupAndGo } from '../utilities/setup-and-go';

test.describe('Test basic login flow with Ping Protect', () => {
test(`should send Protect data and login successfully`, async ({ browserName, page }) => {
const { messageArray } = await setupAndGo(page, browserName, 'authn-protect/');
const configJson = messageArray.find((message) => message.includes('envId'));

let configObj;
try {
configObj = JSON.parse(configJson);
} catch (err) {
console.log('Error parsing configJson');
configObj = {};
}

// Test assertions
expect(configObj.envId.length).toBeGreaterThan(5);
expect(configObj.consoleLogEnabled).toBe(true);
expect(configObj.deviceAttributesToIgnore).toStrictEqual(['userAgent']);
expect(configObj.customHost).toBe('https://example.com');
expect(configObj.lazyMetadata).toBe(false);
expect(configObj.behavioralDataCollection).toBe(true);
expect(configObj.deviceKeyRsyncIntervals).toBe(14);
expect(configObj.enableTrust).toBe(false);
expect(configObj.disableTags).toBe(false);
expect(configObj.disableHub).toBe(false);

expect(messageArray.includes('[SignalsSDK] Starting Signals SDK...')).toBe(true);
expect(messageArray.includes('[SignalsSDK] calculated device attributes.')).toBe(true);
expect(messageArray.includes('getPauseBehavioralData: true')).toBe(true);
expect(messageArray.includes('Basic login with Protect successful')).toBe(true);
expect(messageArray.includes('Test script complete')).toBe(true);
});
});
82 changes: 82 additions & 0 deletions e2e/mock-api/src/app/responses.js
Original file line number Diff line number Diff line change
Expand Up @@ -284,6 +284,88 @@ export const passwordCallback = {
],
};

export const pingProtectEvaluate = {
authId: 'foo',
callbacks: [
{
type: 'PingOneProtectEvaluationCallback',
output: [
{
name: 'pauseBehavioralData',
value: true,
},
],
input: [
{
name: 'IDToken1signals',
value: '',
},
{
name: 'IDToken1clientError',
value: '',
},
],
},
],
};

export const pingProtectInitialize = {
authId: 'foo',
callbacks: [
{
type: 'PingOneProtectInitializeCallback',
output: [
{
name: 'envId',
value: '02fb1243-189a-4bc7-9d6c-a919edf6447',
},
{
name: 'consoleLogEnabled',
value: true,
},
{
name: 'deviceAttributesToIgnore',
value: ['userAgent'],
},
{
name: 'customHost',
value: 'https://example.com',
},
{
name: 'lazyMetadata',
value: false,
},
{
name: 'behavioralDataCollection',
value: true,
},
{
name: 'deviceKeyRsyncIntervals',
value: 14,
},
{
name: 'enableTrust',
value: false,
},
{
name: 'disableTags',
value: false,
},
{
name: 'disableHub',
value: false,
},
],
input: [
{
name: 'IDToken1clientError',
value: '',
},
],
},
],
};

export const choiceCallback = {
authId: 'foo',
callbacks: [
Expand Down
21 changes: 21 additions & 0 deletions e2e/mock-api/src/app/routes.auth.js
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,8 @@ import {
messageCallback,
noSessionSuccess,
pollingCallback,
pingProtectEvaluate,
pingProtectInitialize,
redirectCallback,
redirectCallbackSaml,
requestDeviceProfile,
Expand Down Expand Up @@ -78,6 +80,8 @@ export default function (app) {
req.query.authIndexValue === 'SAMLFailure'
) {
res.json(nameCallback);
} else if (req.query.authIndexValue === 'TEST_LoginPingProtect') {
res.json(pingProtectInitialize);
} else if (req.query.authIndexValue === 'IDMSocialLogin') {
res.json(selectIdPCallback);
} else if (req.query.authIndexValue === 'AMSocialLogin') {
Expand Down Expand Up @@ -266,6 +270,23 @@ export default function (app) {
res.status(401).json(authFail);
}
}
} else if (req.query.authIndexValue === 'TEST_LoginPingProtect') {
const protectInitCb = req.body.callbacks.find(
(cb) => cb.type === 'PingOneProtectInitializeCallback',
);
const usernameCb = req.body.callbacks.find((cb) => cb.type === 'NameCallback');
const protectEvalCb = req.body.callbacks.find(
(cb) => cb.type === 'PingOneProtectEvaluationCallback',
);
if (protectInitCb) {
res.json(initialBasicLogin);
} else if (usernameCb && usernameCb.input[0].value) {
res.json(pingProtectEvaluate);
} else if (protectEvalCb && protectEvalCb.input[0].value) {
res.json(authSuccess);
} else {
res.status(401).json(authFail);
}
} else if (req.body.callbacks.find((cb) => cb.type === 'PasswordCallback')) {
const pwCb = req.body.callbacks.find((cb) => cb.type === 'PasswordCallback');
if (pwCb.input[0].value !== USERS[0].pw) {
Expand Down
Loading

0 comments on commit 55153c5

Please sign in to comment.