Skip to content

Commit

Permalink
Merge pull request #5 from PerimeterX/tyler-updates
Browse files Browse the repository at this point in the history
Update README, seperate challenge script into seperate file, update s…
  • Loading branch information
Johnny Tordgeman authored Jan 12, 2022
2 parents f2b1b8f + b29991f commit e0ad743
Show file tree
Hide file tree
Showing 9 changed files with 146 additions and 114 deletions.
9 changes: 6 additions & 3 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -22,19 +22,22 @@ PerimeterX Bot Defender nodes for [Identity Platform][forgerock_platform] 7.1.0

## Installation

Copy the .jar files from the ../target directory into the ../web-container/webapps/openam/WEB-INF/lib directory where AM is deployed. Restart the web container to pick up the new node. The node will then appear in the authentication trees components palette.
To deploy these nodes, download the jar from the releases tab on github
[here](https://github.com/PerimeterX/perimeterx-forgerock-nodes/releases). Next, copy the jar into the ../web-container/webapps/openam/WEB-INF/lib directory where AM is deployed. Restart the web container to pick up the new node. The node will then appear in the authentication trees components palette.

## Usage

Drag the node to your journey and connect it after the Data Store Decision node. Connect the `true` output to your success node and the `false` output to the **pxChallengeNode** in order to show the challenge page for a blocked request. Finally, connect **pxChallengeNode** to the red X:

![ScreenShot](./pxChallengeNode_connect.png).
![ScreenShot](./images/pxTreeExample.png)

Make sure to fill in the required node properties for the **pxVerificationNode** node. You can find all the required data on the [PerimeterX Console](https://console.perimeterx.com).

![ScreenShot](./images/pxConsole.png)

When a request gets blocked, the default challenge page will be shown, as follows:

![ScreenShot](./block_page_screenshot.png)
![ScreenShot](./images/pxChallengePage.png)

# pxVerificationNode

Expand Down
File renamed without changes
File renamed without changes
Binary file added images/pxChallengePage.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added images/pxConsole.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added images/pxTreeExample.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
4 changes: 2 additions & 2 deletions pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -24,8 +24,8 @@

<groupId>com.perimeterx</groupId>
<version>1.0.0</version>
<name>Template for an Auth Tree Node maven project</name>
<description>An Authentication Tree Node for ForgeRock's Identity Platform</description>
<name>PerimeterX Auth Nodes</name>
<description>PerimeterX Bot Defender nodes for Identity Platform 7.1.0 and above.</description>

<properties>
<am.version>7.1.0</am.version>
Expand Down
143 changes: 34 additions & 109 deletions src/main/java/com/perimeterx/BD/nodes/pxChallengeNode.java
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,12 @@

package com.perimeterx.BD.nodes;

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.util.Arrays;
import java.util.stream.Collectors;

import javax.inject.Inject;

Expand Down Expand Up @@ -63,6 +68,23 @@ default String pxJsRef() {
}
}

public static String readFileString(String path) {
try {
InputStream in = Thread.currentThread().getContextClassLoader().getResourceAsStream(path);
String data = readAllLines(in);
in.close();
return data;
} catch (NullPointerException | IOException e) {
logger.error("Can´t read file " + path, e);
return null;
}
}

public static String readAllLines(InputStream in) {
BufferedReader reader = new BufferedReader(new InputStreamReader(in));
return reader.lines().parallel().collect(Collectors.joining("\n"));
}

/**
* Create the node using Guice injection. Just-in-time bindings can be used to
* obtain instances of other classes
Expand Down Expand Up @@ -93,124 +115,27 @@ public Action process(TreeContext context) throws NodeProcessException {
}

private String getScript(TreeContext context) {
String appId = context.sharedState.get("appId").asString();

String refId = context.sharedState.get("refId").asString();

String appId = context.sharedState.get("appId").asString();
String jsClientSrc = context.sharedState.get("jsClientSrc").asString();
boolean firstPartyEnabled = context.sharedState.get("firstPartyEnabled").asBoolean();
String vid = context.sharedState.get("vid").asString();
String uuid = context.sharedState.get("uuid").asString();
String cssRef = this.config.pxCssRef();
String jsRef = this.config.pxJsRef();
String hostUrl = context.sharedState.get("hostUrl").asString();
String blockScript = context.sharedState.get("blockScript").asString();
String jsClientSrc = context.sharedState.get("jsClientSrc").asString();
boolean firstPartyEnabled = context.sharedState.get("firstPartyEnabled").asBoolean();

StringBuffer script = new StringBuffer()
.append("document.head.insertAdjacentHTML(\"beforeend\", \"<style>.px-container{align-items:center;display:flex;flex:1;justify-content:space-between;flex-direction:column;height:100%}.px-container>div{width:100%;display:flex;justify-content:center}.px-container>div>div{display:flex;width:80%}.page-title-wrapper{flex-grow:2}.page-title{flex-direction:column-reverse}.content-wrapper{flex-grow:5}.content{flex-direction:column}.page-footer-wrapper{align-items:center;flex-grow:.2;background-color:#000;color:#c5c5c5;font-size:70%}.content p{margin:14px 0;}</style>\");\n")
.append("submitted = true;\n")
.append("\n")
.append("function createElement(type, classes, attributes, styles) {\n")
.append(" const elm = document.createElement(type);\n")
.append(" if (classes && classes.length > 0) {\n")
.append(" classes.forEach((e) => {\n")
.append(" elm.classList.add(e);\n")
.append(" });\n")
.append(" }\n")
.append(" if (attributes) {\n")
.append(" Object.keys(attributes).forEach((k) => {\n")
.append(" elm.setAttribute(k, attributes[k]);\n")
.append(" });\n")
.append(" }\n")
.append(" if (styles) {\n")
.append(" Object.keys(styles).forEach((k) => {\n")
.append(" elm.style[k] = styles[k];\n")
.append(" });\n")
.append(" }\n")
.append(" return elm;\n")
.append("}\n")
.append("function insertAfter(el, referenceNode) {\n")
.append(" referenceNode.parentNode.insertBefore(el, referenceNode.nextSibling);\n")
.append("}\n")
.append("\n")
.append("function callback() {\n")
.append(" const container = document.createElement(\"div\");\n")
.append(" container.classList.add(\"px-container\");\n")
.append(" const pageTitleWrapper = createElement('div', ['page-title-wrapper']);\n")
.append(" const pageTitle = createElement('div', ['page-title']);\n")
.append(" const title = createElement('h1');\n")
.append(" title.innerText = 'Please verify you are a human';\n")
.append(" pageTitle.appendChild(title);\n")
.append(" pageTitleWrapper.appendChild(pageTitle);\n")
.append(" const contentWrapper = createElement('div', ['content-wrapper']);\n")
.append(" const content = createElement('div', ['content']);\n")
.append(" const pxCaptcha = createElement('div', null, { id: 'px-captcha' });\n")
.append(" content.appendChild(pxCaptcha);\n")
.append(" const p1 = createElement('p');\n")
.append(" p1.innerText = 'Access to this page has been denied because we believe you are using automation tools to browse the website.';\n")
.append(" content.appendChild(p1);\n")
.append(" const p2 = createElement('p');\n")
.append(" p2.innerText = 'This may happen as a result of the following:';\n")
.append(" content.appendChild(p2);\n")
.append(" const ul = createElement('ul');\n")
.append(" const li1 = createElement('li');\n")
.append(" li1.innerText = 'Javascript is disabled or blocked by an extension (ad blockers for example)';\n")
.append(" ul.appendChild(li1);\n")
.append(" const li2 = createElement('li');\n")
.append(" li2.innerText = 'Your browser does not support cookies';\n")
.append(" ul.appendChild(li2);\n")
.append(" content.appendChild(ul);\n")
.append(" const p3 = createElement('p');\n")
.append(" p3.innerText = 'Please make sure that Javascript and cookies are enabled on your browser and that you are not blocking them from loading.';\n")
.append(" content.appendChild(p3);\n")
.append(" const p4 = createElement('p');\n")
.append(" p4.innerText = 'Reference ID: #" + refId + "';\n")
.append(" content.appendChild(p4);\n")
.append(" contentWrapper.appendChild(content);\n")
.append(" const pageFooterWrapper = createElement('div', ['page-footer-wrapper']);\n")
.append(" const pageFooter = createElement('div', ['page-footer']);\n")
.append(" const preLink = createElement('span');\n")
.append(" preLink.innerText = 'Powered by\u00A0';\n")
.append(" const link = createElement('a', null, { href: 'https://www.perimeterx.com/whywasiblocked' });\n")
.append(" link.innerText = '\u00A0PerimeterX';\n")
.append(" const postLink = createElement('span');\n")
.append(" postLink.innerText = ' , Inc.';\n")

.append(" pageFooter.appendChild(preLink);\n")
.append(" pageFooter.appendChild(link);\n")
.append(" pageFooter.appendChild(postLink);\n")
.append(" pageFooterWrapper.appendChild(pageFooter);\n")

.append(" container.appendChild(pageTitleWrapper);\n")
.append(" container.appendChild(contentWrapper);\n")
.append(" container.appendChild(pageFooterWrapper);\n")
.append(" document.querySelector(\".page-header\").remove();\n")
.append(" document.forms[0].remove();\n")
.append(" const anchor = document.querySelector(\".container\");\n")
.append(" anchor.appendChild(container);\n")

.append(getCssRefValue(cssRef))
.append(getJSRefValue(jsRef))

.append(" window._pxAppId='" + appId + "';\n")
.append(" window._pxJsClientSrc='" + jsClientSrc + "';\n")
.append(" window._pxFirstPartyEnabled=" + firstPartyEnabled + ";\n")
.append(" window._pxVid='" + vid + "';\n")
.append(" window._pxUuid='" + uuid + "';\n")
.append(" window._pxHostUrl='" + hostUrl + "';\n")
.append(" const blockScript = document.createElement('script');\n")
.append(" blockScript.src = '" + blockScript + "';\n")
.append(" const head = document.getElementsByTagName('head')[0];\n")
.append(" head.insertBefore(blockScript, null);\n")
.append("}\n")
.append("\n")
.append("if (document.readyState !== 'loading') {\n")
.append(" callback();\n")
.append("} else {\n")
.append(" document.addEventListener(\"DOMContentLoaded\", callback);\n")
.append("}");
String cssRef = this.config.pxCssRef();
String jsRef = this.config.pxJsRef();

return script.toString();
String script = readFileString("/js/challenge-script.js");
script = String.format(script, refId, appId, jsClientSrc, firstPartyEnabled, vid, uuid, hostUrl, blockScript);
script = script + getCssRefValue(cssRef) + getJSRefValue(jsRef);
return script;
}


private String getCssRefValue(String cssRef) {
logger.debug("reached getCssRefValue: {}", cssRef);
if (cssRef != null && !cssRef.isEmpty()) {
Expand Down
104 changes: 104 additions & 0 deletions src/main/resources/js/challenge-script.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,104 @@
document.head.insertAdjacentHTML("beforeend", "<style>.px-container{align-items:center;display:flex;flex:1;justify-content:space-between;flex-direction:column;height:100}.px-container>div{width:100;display:flex;justify-content:center}.px-container>div>div{display:flex;width:80}.page-title-wrapper{flex-grow:2}.page-title{flex-direction:column-reverse}.content-wrapper{flex-grow:5}.content{flex-direction:column}.page-footer-wrapper{align-items:center;flex-grow:.2;background-color:#000;color:#c5c5c5;font-size:70}.content p{margin:14px 0;}</style>");
var submitted = true;

function createElement(type, classes, attributes, styles) {
const elm = document.createElement(type);
if (classes && classes.length > 0) {
classes.forEach((e) => {
elm.classList.add(e);
});
}
if (attributes) {
Object.keys(attributes).forEach((k) => {
elm.setAttribute(k, attributes[k]);
});
}
if (styles) {
Object.keys(styles).forEach((k) => {
elm.style[k] = styles[k];
});
}
return elm;
}
function insertAfter(el, referenceNode) {
referenceNode.parentNode.insertBefore(el, referenceNode.nextSibling);
}

function callback() {
const container = document.createElement("div");
container.classList.add("px-container");
const pageTitleWrapper = createElement('div', ['page-title-wrapper']);
const pageTitle = createElement('div', ['page-title']);
const title = createElement('h1');
title.innerText = 'Please verify you are a human';
pageTitle.appendChild(title);
pageTitleWrapper.appendChild(pageTitle);
const contentWrapper = createElement('div', ['content-wrapper']);
const content = createElement('div', ['content']);
const pxCaptcha = createElement('div', null, { id: 'px-captcha' });
content.appendChild(pxCaptcha);
const p1 = createElement('p');
p1.innerText = 'Access to this page has been denied because we believe you are using automation tools to browse the website.';
content.appendChild(p1);
const p2 = createElement('p');
p2.innerText = 'This may happen as a result of the following:';
content.appendChild(p2);
const ul = createElement('ul');
const li1 = createElement('li');
li1.innerText = 'Javascript is disabled or blocked by an extension (ad blockers for example)';
ul.appendChild(li1);
const li2 = createElement('li');
li2.innerText = 'Your browser does not support cookies';
ul.appendChild(li2);
content.appendChild(ul);
const p3 = createElement('p');
p3.innerText = 'Please make sure that Javascript and cookies are enabled on your browser and that you are not blocking them from loading.';
content.appendChild(p3);
const p4 = createElement('p');
p4.innerText = "Reference ID: # %1$s";
content.appendChild(p4);
contentWrapper.appendChild(content);
const pageFooterWrapper = createElement('div', ['page-footer-wrapper']);
const pageFooter = createElement('div', ['page-footer']);
const preLink = createElement('span');
preLink.innerText = 'Powered by';
const link = createElement('a', null, { href: 'https://www.perimeterx.com/whywasiblocked' });
link.innerText = 'PerimeterX';
const postLink = createElement('span');
postLink.innerText = ' , Inc.';

pageFooter.appendChild(preLink);
pageFooter.appendChild(link);
pageFooter.appendChild(postLink);
pageFooterWrapper.appendChild(pageFooter);

container.appendChild(pageTitleWrapper);
container.appendChild(contentWrapper);
container.appendChild(pageFooterWrapper);
var anchor = null;
if (typeof loginHelpers !== 'undefined') {
document.querySelector("#body-append-el").remove();
anchor = document.querySelector("#callbacksPanel");
} else {
document.querySelector(".page-header").remove();
document.forms[0].remove();
anchor = document.querySelector(".container");
}
anchor.appendChild(container);
window._pxAppId="%2$s";
window._pxJsClientSrc="%3$s";
window._pxFirstPartyEnabled="%4$s";
window._pxVid="%5$s";
window._pxUuid="%6$s";
window._pxHostUrl="%7$s";
const blockScript = document.createElement('script');
blockScript.src ="%8$s";
const head = document.getElementsByTagName('head')[0];
head.insertBefore(blockScript, null);
}

if (document.readyState !== 'loading') {
callback();
} else {
document.addEventListener("DOMContentLoaded", callback);
}

0 comments on commit e0ad743

Please sign in to comment.