/** * @preserve Copyright (c) 2014-2015 IONU Security, Inc. All rights reserved. * * @file Permissions class for management and signature generation */ PERMISSION_KEY_OWNER = 'owner'; PERMISSION_KEY_TOPIC = 'topic'; PERMISSION_READ = 'read'; PERMISSION_WRITE = 'write'; PERMISSION_ADMIN = 'admin'; /* PERMISSION_DOORWAY = 'doorway'; PERMISSION_STREAM = 'stream'; PERMISSION_HIDE = 'hide'; */ PERMISSION_ASSIGNMENT_SUFFIX = 'Assign'; PERMISSION_ROOT_KEY = 'perms'; PERMISSION_TYPE_KEY = 'type'; // unset/empty 'file'; 'group' PERMISSION_MEMBERS_KEY = "mbrs"; PERMISSION_SIGNATURE_KEY = "signature"; PERMISSION_DIGEST_KEY = "digest"; /** * Permissions object * @constructor * @param {string|object} perms - JSON string or object */ function Permissions (perms) { if (!(this instanceof Permissions)){ return new Permissions (perms); } if (typeof perms === 'string') { this.perms = JSON.parse (perms); } else if (typeof perms === 'object') { this.perms = perms; } else { var error = new Error ('Invalid permissions'); throw error; } } function createPermissionsFromOffice(officeId){ return createPermissionsFromCollaborators(officeId, [officeId]); } function createPermissionsFromCollaborators(owner, offices, noSub){ var p = {read:[], write:[], admin:[], readAssign:[], writeAssign:[], adminAssign:[]}; for (var i = 0; i < offices.length; ++i) { var officeId = offices[i]; if (officeId.match(/^urn:sl/) && !noSub) { officeId = officeId.replace(/^urn:sl:.{6}:/,'').replace(/:.*/,''); } p.read.push(officeId); p.write.push(officeId); p.admin.push(officeId); p.readAssign.push(officeId); p.writeAssign.push(officeId); p.adminAssign.push(officeId); //p.hideAssign.push(officeId); } p.owner = owner||''; if (typeof p.owner === 'object'){ p.owner = p.owner._id.replace(/^urn:sl:.{6}:/,'').replace(/:.*/,''); } p = new Permissions(p); return p; } function createPermissionsFromTeam(owner, teamUrn){ return createPermissionsFromCollaborators(owner, [teamUrn], true); } // Define the prototype methods for Permissions object Permissions.prototype = { constructor: Permissions, /** * Add all unique references from the current permission key * * @param {array} set of all unique references * @param {string} perms for key, e.g. read/readAssign... * @return {array} set of all unique references */ addUniqueReferences: function (refs, perms) { if (perms !== undefined) { var numPerms = perms.length; for (var j = 0; j < numPerms; ++j) { var permVal = perms[j]; if (permVal === PERMISSION_KEY_OWNER) { permVal = this.perms.owner; } var urn = permVal; if (urn.slice (0, 3) !== 'urn') { urn = this.urnPrefix + permVal + '::'; } var len = refs.length; var found = false; for (var i = 0; i < len; ++i) { if (refs[i] == urn) { found = true; break; } } if (found === false) { refs.push (urn); } } } return refs; }, /** * Get all the unique references in urn format, sorted with '|' delimiter and optional topic id * * @param {string} van - urn string urn:sl:000000::: * @return {string} ordered set of all references */ getOrderedReferences: function (van) { var refs = []; var owner = this.perms.owner; var permissionKeys = [PERMISSION_READ,PERMISSION_WRITE,PERMISSION_ADMIN/*,PERMISSION_DOORWAY,PERMISSION_STREAM,PERMISSION_HIDE*/]; var numKeys = permissionKeys.length, key = null; this.urnPrefix = ''; if (van !== undefined && van !== null) { var urnFields = van.split (':'); if (urnFields.length > 4) { this.urnPrefix = urnFields[0] + ':' + urnFields[1] + ':' + urnFields[2] + ':'; } } for (var i = 0; i < numKeys; ++i) { key = permissionKeys[i]; var perms = this.perms[key]; refs = this.addUniqueReferences (refs, perms); key = permissionKeys[i] + PERMISSION_ASSIGNMENT_SUFFIX; perms = this.perms[key]; refs = this.addUniqueReferences (refs, perms); } refs.sort(); var delim = ''; var str = ''; refs.forEach(function(value) { str += delim + value; delim = '|'; }); // Append "topic"... var topic = this.perms[PERMISSION_KEY_TOPIC]; if (topic !== undefined && topic.length > 0) { str += delim + topic; } return str; }, /** * Convert permissions to ordered JSON string for path hash computation * removes "owner" property, replaces "owner" in perms arrays with urn * * @param {string} van - urn string urn:sl:000000::: * @return {string} ordered set of all references and optional topic */ getSignature: function (van) { var perms = this.getOrderedReferences (van); var md = forge.md.md5.create(); md.update (perms); var sig = md.digest().toHex(); return sig.substring (0, 12); }, /** * Convert permissions to ordered JSON string for path hash computation * removes "owner" property, replaces "owner" in perms arrays with urn * * @return {string} JSON string */ toOrderedString: function() { var permissionKeys = [PERMISSION_READ,PERMISSION_WRITE,PERMISSION_ADMIN/*,PERMISSION_DOORWAY,PERMISSION_STREAM,PERMISSION_HIDE*/]; var json = '{'; var numKeys = permissionKeys.length, key = null; var comma = ''; var owner = this.perms.owner; for (var i = 0; i < 2; ++i) { var suffix = (i == 0) ? '' : PERMISSION_ASSIGNMENT_SUFFIX; for (var j = 0; j < numKeys; ++j) { key = permissionKeys[j] + suffix; var perms = this.perms[key]; if (perms === undefined) { continue; } perms.sort(); var numPerms = perms.length; json += comma + '"' + key + '":['; var bHasOwner = false; for (var k = 0; k < numPerms; ++k) { var permVal = perms[k]; if (permVal === owner) { bHasOwner = true; } if (permVal === PERMISSION_KEY_OWNER) { if (bHasOwner === true) { // we already emitted the UUID for owner so don't do it again // note that normal "assign" logic prevents all other duplicate values continue; } else { permVal = owner; } } if (k > 0) { json += ','; } json += '"' + permVal + '"'; } json += ']'; comma = ","; } } // Append "topic"... var topic = this.perms[PERMISSION_KEY_TOPIC]; if (topic !== undefined && topic.length > 0) { json += ',"' + PERMISSION_KEY_TOPIC + '":"' + topic + '"'; } json += '}'; return json; }, /** * Generate the path hash, left 12 bytes of hex encoded MD5 hash of the ordered JSON permissions * * @return {string} path hash */ getPathHash: function () { var perms = this.toOrderedString(); var md = forge.md.md5.create(); md.update (perms); var sig = md.digest().toHex(); return sig.substring (0, 12); } }