UNPKG

happner

Version:

distributed application engine with evented storage and mesh services

573 lines (368 loc) 15.5 kB
var async = require('async'); module.exports = Security; function Security() { this.__initialized = false; this.__adminUser = null; this.__systemGroups = {}; Security.prototype.__createUsersAndGroups = function($happn, callback){ var _this = this; var createSystemGroups = function(adminUser, done){ async.eachSeries(['_MESH_ADM', '_MESH_GST'], function(groupName, eachCallback){ var group = {name:groupName}; if (groupName == '_MESH_ADM') group.permissions = { '/mesh/*':{actions:['*'], description:"mesh system permission"}, '/_exchange/*':{actions:['*'], description:"mesh system permission"}, '/_events/*':{actions:['*'], description:"mesh system permission"} }; if (groupName == '_MESH_GST') group.permissions = { '/mesh/schema/*':{actions:['get','on'], description:"mesh system guest permission"}, '/_exchange/requests/*/security/updateOwnUser':{actions:['*'], description:"mesh system permission"}, '/_exchange/responses/*/security/updateOwnUser':{actions:['*'], description:"mesh system quest permission"} }; _this.__securityService.upsertGroup(group, {}, function(e, upsertedGroup){ if (e) eachCallback(e); _this.__systemGroups[groupName] = upsertedGroup; if (groupName == '_MESH_ADM') _this.__securityService.linkGroup(upsertedGroup, adminUser, eachCallback); else eachCallback(); }); }, function(e){ if (e) return callback(e); _this.__initialized = true; done(); }); } _this.__securityService.getUser('_ADMIN', {}, function(e, userFound){ if (e) return callback(e); if (!userFound) return callback(new Error('admin user not found, happn may have been incorrectly configured')); else { _this.__adminUser = userFound; createSystemGroups(_this.__adminUser, callback); } }); } Security.prototype.initialize = function($happn, callback){ try{ //_mesh.datalayer.server //_this.__securityService = $happn._mesh.data.__securityService; var _this = this; if (!$happn._mesh.config.datalayer.secure){ if (typeof $happn._mesh.config.datalayer.secure != 'boolean') { // no warning if explicitly set to false $happn.log.warn('data layer is not set to secure in config'); } return callback(); } _this.__securityService = $happn._mesh.datalayer.server.services.security; _this.__pubsubService = $happn._mesh.datalayer.server.services.pubsub; _this.__createUsersAndGroups($happn, callback); }catch(e){ callback(e); } } var attachToSecurityChangesActivated = false; Security.prototype.attachToSecurityChanges = function($happn, callback){ var _this = this; try{ if (!attachToSecurityChangesActivated){ _this.__securityService.onDataChanged(function(whatHappnd, changedData){ $happn.emit(whatHappnd, changedData); }); attachToSecurityChangesActivated = true; } callback(); }catch(e){ callback(e); } } var attachToSessionChangesActivated = false; Security.prototype.attachToSessionChanges = function($happn, callback){ var _this = this; try{ if (!attachToSessionChangesActivated){ _this.__pubsubService.on('authentic', function(data){ $happn.emit('connect', data); }); _this.__pubsubService.on('disconnect', function(data){ $happn.emit('disconnect', data); }); attachToSessionChangesActivated = true; } callback(); }catch(e){ callback(e); } } Security.prototype.getComponentId = function(){ return this.__componentId; } Security.prototype.__synchronizeEndpointGroups = function(){ } Security.prototype.__synchronizeChildGroups = function(){ } Security.prototype.__validateRequest = function(methodName, callArguments, callback){ var _this = this; if (!this.__initialized) return callback(new Error('security module not initialized, is your datalayer configured to be secure?')); if (methodName == 'updateOwnUser'){ var $happn = callArguments[0]; var sessionInfo = callArguments[1]; var userUpdate = callArguments[2]; var session = _this.__pubsubService.getSession(sessionInfo.id); if (userUpdate.password && !userUpdate.oldPassword) return callback(new Error('missing oldPassword parameter')); if (!session) return callback(new Error('matching session was not found for security update')); if (session.user.username != sessionInfo.username) return callback(new Error('session with the matching id is for a different user')); if (sessionInfo.username != userUpdate.username)//cool we are modifying our own user return callback(new Error('attempt to update someone else\'s user details')); if (userUpdate.password){ // we need to validate against the previous password return _this.__securityService.getPasswordHash(userUpdate.username, function(e, hash){ if (e) return callback(e); _this.__securityService.matchPassword(userUpdate.oldPassword, hash, function(e, match) { if (e) return callback(e); if (!match) return callback(new Error('old password incorrect')); callback(); }); }); } } callback(); } /* This will return possible assignable system permissions by iterating through the mesh description */ this.__cachedSystemPermissions = null; Security.prototype.getSystemPermissions = function($happn, params, callback) { try{ this.__validateRequest('getSystemPermissions', arguments, function(e){ if (e) return callback(e); if (!params) params = {}; if (!this.__cachedSystemPermissions || params.nocache){ var _this = this; var permissions = {events:{}, methods:{}, web:{}}; var meshName = $happn._mesh.config.name; for (var componentName in $happn.exchange[meshName]){ permissions.methods['/' + meshName + '/' + componentName + '/*'] = {authorized:true, description:"system permission"}; for (var methodName in $happn.exchange[meshName][componentName]) { permissions.methods['/' + meshName + '/' + componentName + '/' + methodName] = {authorized:true, description:"system permission"}; } } for (var componentName in $happn.event[meshName]){ permissions.events['/' + meshName + '/' + componentName + '/*'] = {authorized:true, description:"system permission"}; for (var eventName in $happn.event[meshName][componentName]) { permissions.events['/' + meshName + '/' + componentName + '/' + eventName] = {authorized:true, description:"system permission"}; } } for (var componentName in $happn._mesh.config.components) { permissions.web['/' + meshName + '/' + componentName + '/*'] = {authorized:true, description:"system permission"}; if ($happn._mesh.config.components[componentName].web && $happn._mesh.config.components[componentName].web.routes) for (var webMethod in $happn._mesh.config.components[componentName].web.routes) { permissions.web['/' + meshName + '/' + componentName + '/' + webMethod] = {authorized:true, description:"system permission"}; } } this.__cachedSystemPermissions = permissions; } callback(null, this.__cachedSystemPermissions); }); }catch(e){ callback(e); } } Security.prototype.__getPermissionPath = function($happn, rawPath, prefix, wildcard){ var meshName = $happn._mesh.config.name; //we add a wildcard to the end of the path if (wildcard) rawPath = rawPath.replace(/[\/*]+$/, "") + '/*'; if (rawPath.substring(0,1) != '/') rawPath = '/' + rawPath; if (rawPath.indexOf('/' + meshName) == -1) rawPath = rawPath.replace('/', '/' + meshName + '/'); return '/' + prefix + rawPath; } Security.prototype.__transformMeshGroup = function($happn, group){ if (!group) return group; var transformed = JSON.parse(JSON.stringify(group)); transformed.permissions = this.__transformMeshPermissions($happn, group.permissions); return transformed; } Security.prototype.__transformHappnGroups = function($happn, happnGroups){ var transformed = []; var _this = this; happnGroups.forEach(function(happnGroup){ transformed.push(_this.__transformHappnGroup($happn, happnGroup)); }); return transformed; } Security.prototype.__transformHappnGroup = function($happn, group){ if (!group) return group; var transformed = JSON.parse(JSON.stringify(group)); transformed.permissions = this.__transformHappnPermissions($happn, group.permissions); return transformed; } /* turns the mesh groups permissions to happn permissions, uses the mesh description to verify */ Security.prototype.__transformMeshPermissions = function($happn, meshGroupPermissions){ var permissions = {}; for (var permissionPath in meshGroupPermissions.events){ if (meshGroupPermissions.events[permissionPath].authorized) permissions[this.__getPermissionPath($happn, permissionPath, '_events')] = {actions:['on'], description:meshGroupPermissions.events[permissionPath].description}; } for (var permissionPath in meshGroupPermissions.methods){ if (meshGroupPermissions.methods[permissionPath].authorized){ permissions[this.__getPermissionPath($happn, permissionPath, '_exchange/requests')] = {actions:['set'], description:meshGroupPermissions.methods[permissionPath].description}; permissions[this.__getPermissionPath($happn, permissionPath, '_exchange/responses', true)] = {actions:['on','set'], description:meshGroupPermissions.methods[permissionPath].description}; } } for (var permissionPath in meshGroupPermissions.web){ var actions = meshGroupPermissions.web[permissionPath]; if (permissionPath.substring(0, 1) != '/') permissionPath = '/' + permissionPath; permissions['/@HTTP' + permissionPath] = actions; } return permissions; } /* turns the happn groups permissions to mesh permissions, uses the mesh description to verify */ Security.prototype.__transformHappnPermissions = function($happn, happnGroupPermissions){ var permissions = { methods:{}, events:{}, web:{} } for (var happnPermissionPath in happnGroupPermissions){ if (happnPermissionPath.indexOf('/_events/') == 0){ var happnPermission = happnGroupPermissions[happnPermissionPath]; if (happnPermission.actions.indexOf('on') > -1) permissions.events[happnPermissionPath.replace('/_events/','')] = {authorized:true, description:happnPermission.description}; } if (happnPermissionPath.indexOf('/_exchange/') == 0){ var happnPermission = happnGroupPermissions[happnPermissionPath]; if (happnPermission.actions.indexOf('set') > -1) permissions.methods[happnPermissionPath.replace('/_exchange/','')] = {authorized:true, description:happnPermission.description}; } if (happnPermissionPath.indexOf('/_web/') == 0){ var happnPermission = happnGroupPermissions[happnPermissionPath]; if (happnPermission.actions.indexOf('get') > -1) permissions.web[happnPermissionPath.replace('/_web/','')] = {authorized:true, description:happnPermission.description}; } } return permissions; } Security.prototype.addGroup = function($happn, group, callback){ var _this = this; _this.__validateRequest('addGroup', arguments, function(e){ if (e) return callback(e); _this.__securityService.upsertGroup(_this.__transformMeshGroup($happn, group), {overwrite:false}, function(e, addedGroup){ if (e) return callback(e); return callback(null, _this.__transformHappnGroup($happn, addedGroup)); }); }); }; Security.prototype.updateGroup = function($happn, group, callback){ var _this = this; _this.__validateRequest('updateGroup', arguments, function(e){ if (e) return callback(e); _this.__securityService.upsertGroup(_this.__transformMeshGroup($happn, group), function(e, updatedGroup){ if (e) return callback(e); return callback(null, _this.__transformHappnGroup($happn, updatedGroup)); }); }); }; Security.prototype.addUser = function($happn, user, callback){ var _this = this; _this.__validateRequest('addUser', arguments, function(e){ if (e) return callback(e); _this.__securityService.upsertUser(user, {overwrite:false}, function(e, upsertedUser){ if (e) return callback(e); _this.linkGroup($happn, _this.__systemGroups['_MESH_GST'], upsertedUser, function(e){ if (e) return callback(e); callback(null, upsertedUser); }); }); }); }; Security.prototype.updateOwnUser = function($happn, $origin, user, callback){ this.__validateRequest('updateOwnUser', arguments, function(e){ if (e) return callback(e); this.__securityService.upsertUser(user, callback); }.bind(this)); }; Security.prototype.updateUser = function($happn, user, callback){ this.__validateRequest('updateUser', arguments, function(e){ if (e) return callback(e); this.__securityService.upsertUser(user, callback); }.bind(this)); }; Security.prototype.linkGroup = function($happn, group, user, callback){ var _this = this; _this.__validateRequest('linkGroup', arguments, function(e){ if (e) return callback(e); _this.__securityService.linkGroup(_this.__transformMeshGroup($happn, group), user, {}, callback); }.bind(this)); }; Security.prototype.unlinkGroup = function($happn, group, user, callback){ var _this = this; _this.__validateRequest('unlinkGroup', arguments, function(e){ if (e) return callback(e); _this.__securityService.unlinkGroup(_this.__transformMeshGroup($happn, group), user, {}, callback); }.bind(this)); }; Security.prototype.listGroups = function($happn, groupName, callback){ var _this = this; _this.__validateRequest('listGroups', arguments, function(e){ if (e) return callback(e); _this.__securityService.listGroups(groupName, {}, function(e, happnGroups){ if (e) return callback(e); callback(null, _this.__transformHappnGroups($happn, happnGroups)) }); }); }; Security.prototype.listUsers = function($happn, userName, callback){ this.__validateRequest('listUsers', arguments, function(e){ if (e) return callback(e); this.__securityService.listUsers(userName, {}, callback); }.bind(this)); }; Security.prototype.getUser = function($happn, userName, callback){ this.__validateRequest('getUser', arguments, function(e){ if (e) return callback(e); this.__securityService.getUser(userName, {}, callback); }.bind(this)); } Security.prototype.getGroup = function($happn, groupName, callback){ var _this = this; _this.__validateRequest('getGroup', arguments, function(e){ if (e) return callback(e); _this.__securityService.getGroup(groupName, {}, function(e, group){ if (e) return callback(e); if (group) return callback(null, _this.__transformHappnGroup($happn, group)) // callback with null if group does not exist (same as getUser) callback(null, null); }.bind(this)); }); } Security.prototype.deleteGroup = function(group, callback){ this.__validateRequest('deleteGroup', arguments, function(e){ if (e) return callback(e); this.__securityService.deleteGroup(group, {}, callback); }.bind(this)); } Security.prototype.deleteUser = function(user, callback) { this.__validateRequest('deleteUser', arguments, function(e){ if (e) return callback(e); this.__securityService.deleteUser(user, {}, callback); }.bind(this)); } };