Last active
September 18, 2018 03:44
-
-
Save sampathsris/0f4be7166879b8c3131fc31382ff00b4 to your computer and use it in GitHub Desktop.
Authenticate an IFS Aurena endpoint with DB Provider in Node.js and retrieve a projection
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/** | |
* This is a crude hack to connect to an IFS Aurena endpoint using IFS Database | |
* Identity Provider, and then query for projections from Node.js. Not sure the | |
* purpose of this, but feeling it would come handy for something. You can do | |
* the same in Postman, but I'm feeling the stateful client (read: with cookies) | |
* will be helpful in chaining requests. | |
* | |
* Tested with: | |
* "dom-parser": "^0.1.5", | |
* "request": "^2.87.0", | |
* "request-promise-native": "^1.0.5" | |
*/ | |
const request = require('request-promise-native'); | |
const url = require('url'); | |
const DomParser = require('dom-parser'); | |
const parser = new DomParser(); | |
const WEB_ROOT = '/main/ifsapplications/web/'; | |
const WEB_LOGIN = '/openid-connect-provider/login'; | |
const OPENID_AUTH = '/openid-connect-provider/idp/authorization'; | |
const APP_INDEX = '/main/ifsapplications/web/index.html'; | |
// gets 'action' attribute from an HTML form element | |
const getFormAction = form => form.attributes.reduce((acc, curr) => { | |
if (curr.name === 'action') | |
return curr.value; | |
return acc; | |
}, null); | |
// gets 'action' attribute of the first form in a DOM | |
const getFormActionFromDOM = | |
dom => getFormAction(dom.getElementsByTagName('form')[0]); | |
// gets 'action' attribute of the first form in an HTML source | |
const getFormActionFromText = | |
text => getFormActionFromDOM(parser.parseFromString(text)); | |
// returns a function that checks the HTTP response status and a given final redirect URI path | |
const checkRequestSuccess = expectedPath => res => { | |
if (res.statusCode === 200 && res.request.uri.pathname === expectedPath) { | |
return res.body; | |
} | |
throw new Error({ | |
target: expectedPath, | |
statusCode: res.statusCode, | |
uri: res.request.uri | |
}); | |
}; | |
// authenticates with an Aurena endpoint and returns promise that resolves to an stateful client | |
const auth = (HOST, username, password) => { | |
const client = request.defaults({ | |
jar: true, | |
followAllRedirects: true, | |
resolveWithFullResponse: true | |
}); | |
return client | |
// Initial request: | |
.get(url.resolve(HOST, WEB_ROOT)) | |
// Client should eventually redirect to the login form. | |
.then(checkRequestSuccess(WEB_LOGIN)) | |
// Grab the login form's redirect URL, | |
.then(getFormActionFromText) | |
// Prepare request options for next step, with form action URL from previous step. | |
.then(loginUrl => ({ | |
url: url.resolve(HOST, loginUrl), | |
form: { | |
username, | |
password | |
} | |
}) | |
) | |
// Use the options object created in the last step for a POST request. | |
.then(client.post) | |
// Last POST request should redirect to OpenID Auth page | |
.then(checkRequestSuccess(OPENID_AUTH)) | |
// ...which contains an scripted redirect using a form. Grab the form data | |
// and prepare the options for next request. | |
.then(body => { | |
let dom = parser.parseFromString(body); | |
let redirectUrl = getFormActionFromDOM(dom); | |
let params = dom.getElementsByTagName('input').reduce((paramsObj, item) => { | |
let paramName, paramValue; | |
item.attributes.forEach(attr => { | |
switch (attr.name) { | |
case 'name': | |
paramName = attr.value; | |
break; | |
case 'value': | |
paramValue = attr.value; | |
break; | |
default: | |
break; | |
} | |
}); | |
paramsObj[paramName] = paramValue; | |
return paramsObj; | |
}, {}); | |
return { | |
url: redirectUrl, | |
form: params | |
}; | |
}) | |
// Do a POST request with options constructed in the last step. | |
.then(client.post) | |
// Check the success of the last step. | |
.then(checkRequestSuccess(APP_INDEX)) | |
// This should be the index page. return a promise that resolves | |
// to the client we just built. | |
.then(() => client); | |
}; | |
module.exports = auth; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/** | |
* | |
* Needs following environment variables to be set: | |
* IFS_HOST=https://<host>:<port> | |
* IFS_USERNAME=<username> | |
* IFS_PASSWORD=<password> | |
* | |
* Also need following set if the endpoint uses a self-signed certificate: | |
* NODE_TLS_REJECT_UNAUTHORIZED=0 | |
* | |
*/ | |
const auth = require('./aurena'); | |
const HOST = process.env.IFS_HOST; | |
const USERNAME = process.env.IFS_USERNAME; | |
const PASSWORD = process.env.IFS_PASSWORD; | |
const PROJECTIONS = url.resolve(HOST, '/main/ifsapplications/projection/v1/'); | |
const NAVIGATOR_ENTRIES = 'ClientNavigator.svc/ClientNavigatorMain?$orderby=ParentId,SortOrder,Label'; | |
// let's try authenticating with an endpoint and try to call a projection: in this | |
// case, ClientNavigator.svc/ClientNavigatorMain. | |
auth(HOST, USERNAME, PASSWORD) | |
.then(client => | |
client | |
.get(url.resolve(PROJECTIONS, NAVIGATOR_ENTRIES)) | |
.then(res => res.body) | |
.then(JSON.parse) | |
.then(data => data.value) | |
.then(navigator => 'Retrieved ' + navigator.length + ' navigator entries') | |
.then(console.log) | |
// If all goes well, this should print something like: | |
// Retrieved 357 navigator entries | |
) | |
.catch(console.error); |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment