wasteful-scope
Version:
OAuth2 scope utilities for the wasteful scope parsing, comparing, merging, etc
221 lines (182 loc) • 5.86 kB
JavaScript
;
var PromiseA = require('bluebird').Promise
;
function create(scopeGroups) {
var fieldGroups
;
fieldGroups = ['readable', 'writeable', 'executable'];
exports.parse = function parseScope(scope) {
var scopeObjs = {}
, invalid = []
, scopes
;
if (Array.isArray(scope)) {
scopes = scope;
} else {
scopes = (scope||'').split(' ');
}
// group.subgroup:rfields:wfields:xfields
// TODO check individual fields
scopes.forEach(function (scope) {
var groups = scope.split(':')
, group = groups[0] || ''
;
if (!groups[0]) {
groups = [];
}
if (!scopeGroups[group]) {
invalid.push({
group: group
, readable: (groups[1] || '').split(/,/g)
, writeable: (groups[2] || '').split(/,/g)
, executable: (groups[3] || '').split(/,/g)
, raw: scope
});
} else {
scopeObjs[group] = {
group: group
, readable: (groups[1] || '').split(/,/g)
, writeable: (groups[2] || '').split(/,/g)
, executable: (groups[3] || '').split(/,/g)
};
if (!scopeObjs[group].readable[0]) {
scopeObjs[group].readable = [];
}
if (!scopeObjs[group].writeable[0]) {
scopeObjs[group].writeable = [];
}
if (!scopeObjs[group].executable[0]) {
scopeObjs[group].executable = [];
}
}
});
return { scope: scopeObjs, invalid: invalid };
};
exports.diff = function (grantedScope, requestedScope) {
if ('string' === typeof grantedScope) {
grantedScope = exports.parse(grantedScope);
}
if ('string' === typeof requestedScope) {
requestedScope = exports.parse(requestedScope);
}
grantedScope = grantedScope || { scope: {} };
requestedScope = requestedScope || { scope: {} };
var deltaScope = {}
;
Object.keys(requestedScope.scope).forEach(function (groupname) {
var reqGroup = requestedScope.scope[groupname]
, hasGroup = grantedScope.scope[groupname]
, newGroup
;
if (!reqGroup) {
return;
}
if (!hasGroup) {
deltaScope[groupname] = reqGroup;
return;
}
fieldGroups.forEach(function (key) {
var reqFields = reqGroup[key]
, hasFields = hasGroup[key]
;
if (!reqFields || 0 === reqFields.length) {
return;
}
if (!hasFields) {
newGroup[key] = reqFields.slice(0);
return;
}
reqFields.forEach(function (field) {
if (-1 === hasFields.indexOf(field)) {
if (!newGroup) {
newGroup = deltaScope[groupname] = { };
newGroup.group = hasGroup.group || reqGroup.group;
}
if (!newGroup[key]) {
newGroup[key] = [];
}
newGroup[key].push(field);
}
});
});
});
return exports.stringify({ scope: deltaScope });
};
// TODO make subgroups subojects?
exports.merge = function (grantedScope, requestedScope) {
if ('string' === typeof grantedScope) {
grantedScope = exports.parse(grantedScope);
}
if ('string' === typeof requestedScope) {
requestedScope = exports.parse(requestedScope);
}
grantedScope = grantedScope || { scope: {} };
requestedScope = requestedScope || { scope: {} };
Object.keys(requestedScope.scope).forEach(function (groupname) {
// groupname i.e. stake.leadership
// group i.e. { group: "", readable: [], writeable: [], executable: [] }
var curGroup = grantedScope.scope[groupname] = grantedScope.scope[groupname] || {}
, newGroup = requestedScope.scope[groupname] = requestedScope.scope[groupname] || {}
;
curGroup.group = curGroup.group || newGroup.group;
fieldGroups.forEach(function (key) {
curGroup[key] = curGroup[key] || [];
newGroup[key] = newGroup[key] || [];
newGroup[key].forEach(function (newField) {
if (-1 === curGroup[key].indexOf(newField)) {
curGroup[key].push(newField);
}
});
});
});
return exports.stringify(grantedScope);
};
function getScopeDelta(grantedScopeString, reqScopeString) {
// We only want to ask the user to grant this application permissions
// that the user has not yet given it before.
// However, this auth code should be bound to the scope it requested
return new PromiseA(function (resolve/*, reject*/) {
var grantedScope = exports.parse(grantedScopeString)
// wrapped with scope
, requestedScope = exports.parse(reqScopeString)
, info = {
deltaScopeString: exports.diff(grantedScope, requestedScope)
, invalids: requestedScope.invalid
, requestedScope: requestedScope
}
;
resolve(info);
});
}
exports.stringify = function (scopeMap) {
scopeMap = scopeMap.scope || scopeMap;
var scopeArr
;
if (Array.isArray(scopeMap)) {
scopeArr = scopeMap;
} else {
scopeMap = scopeMap.scope || scopeMap;
scopeArr = Object.keys(scopeMap).map(function (k) {
if (k !== scopeMap[k].group) {
throw new Error("index and name do not match '" + k + "' '" + scopeMap[k].group + "'");
}
return scopeMap[k];
});
}
return scopeArr.map(function (s) {
var str = s.group
;
// read, write, exec
fieldGroups.forEach(function (g) {
if (!s[g]) {
s[g] = [];
}
str += ':' + s[g].join(',');
});
return str;
}).join(' ');
};
exports.getScopeDelta = getScopeDelta;
return exports;
}
module.exports.create = create;