oidc-lib
Version:
A library for creating OIDC Service Providers
430 lines (377 loc) • 11.5 kB
JavaScript
var _module_views;
const url_module = require('url');
var handlebars = require('./handlebars');
const protocolHandlerSig = 'web%2Bopenid%3A';
var res = new ResFactory();
var req = new ReqFactory();
var query = null;
var body = null;
var headers = [];
var payload = null;
var settings = {
views: '\\views'
}
var entryPointUrlFunction = null;
const PCOOKIE = 'pcookie_';
var dispatchPoints = [];
var registeredCookies = {};
var renderScript = null;
module.exports = function(){
Object.defineProperty(this, "module_views", {
get: function() {
if (_module_views){
return _module_views;
}
},
set: function(value) {
_module_views = value;
},
enumerable: true
});
Object.defineProperty(this, "settings", {
get: function() {
return settings;
},
enumerable: true
});
Object.defineProperty(this, "options", {
value: function(name, callback) {
}
});
Object.defineProperty(this, "registerCookie", {
value: function(name) {
name = PCOOKIE + name;
if (registeredCookies[name] === undefined){
registeredCookies[name] = null;
}
}
});
Object.defineProperty(this, "renderScript", {
value: function(arg0, arg1, arg2, arg3, arg4, arg5) {
return _module_views.scripts[renderScript][arg0](arg1,arg2,arg3,arg4,arg5);
}
});
Object.defineProperty(this, "renderDispatcher", {
value: function(method, path, parameters) {
var point = locateDispatchMatch(method, path);
if (point === null){
throw create_claimer_error('server_error', 'No dispatchPoint matches: ' + method + ', ' + path);
}
method = method.toUpperCase();
switch (method){
case 'GET':
req.query = parameters;
break;
case 'POST':
req.body = parameters;
break;
case 'DEFAULT':
throw create_claimer_error('server_error', 'invalid method in renderDispatcher: ' + method);
}
point.invokeThis(req, res);
return payload;
}
});
/*
Object.defineProperty(this, "get", {
value: function(path, invokeThis) {
var dispatchPoint = {path: 'GET' + path.toLowerCase(), invokeThis: invokeThis};
dispatchPoints.push(dispatchPoint);
}
});
Object.defineProperty(this, "post", {
value: function(path, invokeThis) {
var dispatchPoint = {path: 'POST' + path.toLowerCase(), invokeThis: invokeThis};
dispatchPoints.push(dispatchPoint);
}
});
*/
// get and post may either take a path and a function, or path, cors callback and function
// in the shim there is no cors since all access is via redirects but for compatibility
// if a cors callback is present we ignore it and use the next parameter: the funciton
Object.defineProperty(this, "get", {
value: function(path, dummyCorsOrInvokeThis, undefinedOrInvokeThis) {
var toInvoke = undefinedOrInvokeThis ? undefinedOrInvokeThis : dummyCorsOrInvokeThis;
var dispatchPoint = {path: 'GET' + path.toLowerCase(), invokeThis: toInvoke};
dispatchPoints.push(dispatchPoint);
}
});
Object.defineProperty(this, "post", {
value: function(path, dummyCorsOrInvokeThis, undefinedOrInvokeThis) {
var toInvoke = undefinedOrInvokeThis ? undefinedOrInvokeThis : dummyCorsOrInvokeThis;
var dispatchPoint = {path: 'POST' + path.toLowerCase(), invokeThis: toInvoke};
dispatchPoints.push(dispatchPoint);
}
});
Object.defineProperty(this, "invokeEntryPoint", {
value: function() {
var point = locateDispatchMatch(req.method, req.entryPointUrl);
if (point !== null){
point.invokeThis(req, res);
return;
}
throw create_claimer_error('server_error', 'No dispatchPoint matches ' + req.originalUrl);
}
});
Object.defineProperty(this, "entryPointUrlFunction", {
set: function(value) {
entryPointUrlFunction = value;
},
enumerable: true
});
return this;
}
function locateDispatchMatch(method, path){
var method = method.toUpperCase();
var path = path.toLowerCase();
var segs = path.split('/');
var w0 = method + path;
var w1 = method + '/*/' + segs[2];
var w2 = method + '/' + segs[1] + '/*';
for (var count=0; count < dispatchPoints.length; count++){
var point = dispatchPoints[count];
var target = point.path;
if (w0 === target || w1 === target || w2 === target){
return point;
}
}
return null;
}
function ResFactory() {
Object.defineProperty(this, "views", {
get: function() {
return _module_views;
}
});
Object.defineProperty(this, "cookie", {
value: function(name, value, timeout) {
var expires;
if (timeout === undefined || timeout === 0){
expires = 0;
}
else{
var d = new Date();
expires = d.getTime() + timeout.maxAge;
}
var contentObj = {
value: value,
expires: expires
};
content = JSON.stringify(contentObj);
localStorage.setItem(PCOOKIE + name, content);
return;
},
enumerable: true
});
Object.defineProperty(this, "redirect", {
value: function(location) {
window.location = location;
return;
}
});
Object.defineProperty(this, "end", {
value: function() {
return;
}
});
Object.defineProperty(this, "json", {
value: function(jsonObj) {
payload = jsonObj;
}
});
Object.defineProperty(this, "jsonp", {
value: function() {
alert('res.jsonp not yet implemented in shim');
}
});
Object.defineProperty(this, "render", {
value: function (viewLocation, contentObject) {
viewPage = expandView(viewLocation, contentObject);
var template = handlebars.compile(viewPage);
var outputPage = template(contentObject);
var divEl = document.getElementById("render");
divEl.innerHTML = outputPage;
renderScript = viewLocation.toLowerCase();
var selectedScript = res.views.scripts[renderScript];
if (selectedScript !== undefined){
var onload = selectedScript['onload'];
if (onload !== undefined){
onload();
}
}
}
});
Object.defineProperty(this, "send", {
value: function() {
alert('res.send not yet implemented in shim');
}
});
Object.defineProperty(this, "sendFile", {
value: function() {
alert('res.sendFile not yet implemented in shim');
}
});
Object.defineProperty(this, "sendStatus", {
value: function() {
alert('res.sendStatus not yet implemented in shim');
}
});
}
function ReqFactory() {
Object.defineProperty(this, "cookies", {
get: function() {
var result = {};
for (var key in registeredCookies){
var content = localStorage.getItem(key);
if (content === undefined || content === null){
continue;
}
var contentObj = JSON.parse(content);
if (contentObj.expires !== null){
var now = new Date().getTime();
if (contentObj.expires <= now){
continue;
}
}
result[key.substring(PCOOKIE.length)] = contentObj.value;
}
return result;
},
enumerable: true
});
Object.defineProperty(this, "redirect", {
value: function(location) {
window.location = location;
return;
}
});
Object.defineProperty(this, "originalUrl", {
get: function() {
return window.location.href;
},
enumerable: true
});
Object.defineProperty(this, "hostname", {
get: function() {
return window.location.hostname;
},
enumerable: true
});
Object.defineProperty(this, "path", {
get: function() {
return window.location.pathname;
},
enumerable: true
});
Object.defineProperty(this, "hash", {
get: function() {
return window.location.hash;
},
enumerable: true
});
Object.defineProperty(this, "entryPointUrl", {
get: function() {
if (!entryPointUrlFunction){
alert("mandatory entryPointUrlFunction not defined in express shim.")
return;
}
return entryPointUrlFunction(this);
},
enumerable: true
});
Object.defineProperty(this, "method", {
get: function() {
return 'GET';
},
enumerable: true
});
Object.defineProperty(this, "query", {
get: function() {
if (query === null){
var urlInfo = url_module.parse(window.location.href);
query = {};
if (urlInfo.query !== undefined && urlInfo.query !== null){
var keyValuePairs = urlInfo.query.split('&');
if (keyValuePairs.length === 1){
var keyValuePair = keyValuePairs[0].split('=');
if (keyValuePair[0] === 'openid' && keyValuePair[1].startsWith(protocolHandlerSig)){
var payload = decodeURIComponent(keyValuePair[1].substring(protocolHandlerSig.length));
keyValuePairs = payload.split('&');
}
}
for (var count=0; count < keyValuePairs.length; count++){
var keyValuePair = keyValuePairs[count].split('=');
var value = keyValuePair[1].replace(/\+/, ' ');
query[decodeURIComponent(keyValuePair[0])] = decodeURIComponent(value);
}
}
}
return query;
},
set: function(value){
query = value;
},
enumerable: true
});
Object.defineProperty(this, "body", {
get: function() {
return body;
},
set: function(value) {
body = value;
},
enumerable: true
});
Object.defineProperty(this, "headers", {
get: function() {
return headers;
},
set: function(value) {
headers = value;
},
enumerable: true
});
}
function create_claimer_error(error, errorDescription){
var errorObj = {
error: error,
error_description: errorDescription
};
return errorObj;
}
function expandView(viewLocation, contentObject){
var regex = /(\{include:\'([\w|\d]+)\'\})|(\{include:\$([\w|\d]+)\})/g;
viewLocation = viewLocation.toLowerCase();
var viewBase64 = res.views.viewCollection[viewLocation];
if (viewBase64 === undefined){
var error = create_claimer_error('server_error', 'Unable to render missing view: ' + viewLocation);
throw error;
}
var viewPage = pk.base64url.decode(viewBase64);
do {
var myArray = regex.exec(viewPage);
if (myArray === null){
break;
}
var found, nextPage, target;
if (myArray[2] !== undefined){
target = myArray[1];
nextPage = myArray[2];
}
else if (myArray[4] !== undefined){
target = myArray[3];
var nextPageAttribute = myArray[4];
nextPage = contentObject[nextPageAttribute];
if (nextPage === undefined){
throw 'View ' + viewLocation + ' references attribute ' + nextPageAttribute + ' but it is not defined in contentObject';
}
}
else{
break;
}
var path = viewLocation.substring(0, viewLocation.lastIndexOf('\\') + 1);
var subPage = expandView(path + nextPage, contentObject);
viewPage = viewPage.replace(target, subPage);
} while (true);
return viewPage;
}