egg-rbac-instance
Version:
Role Based Access Control for eggjs
423 lines (383 loc) • 13.1 kB
JavaScript
'use strict';
const path = require('path');
const fs = require('fs');
const debug = require('debug')('egg-rbac');
const mongooseStorage = require('./mongoose/index');
/**
* @class
*/
class rbac {
/**
* @constructs role
* @param {object} app eggjs application object
*/
constructor(app) {
this.config = app.config.mongoose;
this.mongoose = app.mongoose;
this.db = app.mongooseDB.get('db1');
this.storage = new mongooseStorage(this.mongoose,this.db);
if (app.config.rbac.initOnStart) {
app.beforeStart(() => {
const appRbacFilePath = path.join(app.baseDir, 'config/rbac.js');
debug('app rbac config file path %s file path exit %s ', appRbacFilePath, fs.existsSync(appRbacFilePath));
if (fs.existsSync(appRbacFilePath)) {
const data = require(appRbacFilePath);
return this.initData(data.permissions, data.roles, app.config.rbac.superRole);
}
});
}
}
/**
* before start init permissions and roles
* @method rbac#initData
* @param {object[]} permissions - permission item array
* @param {string} permissions[].name - permission name
* @param {string} permissions[].alias - permission alias
* @param {object[]} roles - role item array
* @param {string} roles[].name - role name
* @param {string} roles[].alias - role alias
* @param {object} superRole super role info
* @param {string} superRole.name role name
* @param {string} superRole.alias role alias
* @return {object} error object
* @yields {boolean}
*/
initData(permissions, roles, superRole) {
if (!permissions || permissions.length === 0) {
throw new Error('[egg-rbac] initData parameter permissions is undefined');
}
if (!roles || roles.length === 0) {
throw new Error('[egg-rbac] initData parameter roles is undefined');
}
debug('init data permissions.length %O roles.length %O', permissions.length, roles.length);
return this._initPermissions(permissions)
.then(() => this._initRole(roles, superRole));
}
/**
* Initialize permission
* @method rbac#_initPermissions
* @private
* @param {object[]} permissions - permission item array
* @param {string} permissions[].name - permission name
* @param {string} permissions[].alias - permission alias
* @return {string[]|null} null or ObjectId array
*/
_initPermissions(permissions) {
debug('init permission permission type is %s', typeof permissions);
return this.getAllPermission()
.then(oldPermission => {
const oldPermissionObj = {};
oldPermission.forEach(item => {
oldPermissionObj[item.name] = item;
});
permissions = permissions.filter(item => {
if (oldPermissionObj[item.name]) {
return false;
}
return true;
});
if (permissions.length === 0) {
return [];
}
return this.storage.insertManyPermission(permissions);
});
}
/**
* Initialize roles and initialize superadmin role
* @method rbac#_initRole
* @private
* @param {object[]} roles - role item array
* @param {string} roles[].name - role name
* @param {string} roles[].alias - role alias
* @param {object} superRole super role info
* @param {string} superRole.name role name
* @param {string} superRole.alias role alias
* @return {object} promise
*/
_initRole(roles, superRole) {
const arr = roles.map(roleData => {
return Promise.all([
this.storage.getPermissions(roleData.grants).then(permissions => permissions.map(per => per._id)),
this.storage.getRole(roleData.name),
]).then(([ ids, role ]) => {
debug('init role %s ', role);
if (role === null) {
roleData.grants = ids;
return this.newRole(roleData);
}
return this.addPermission(role._id, ids);
});
});
arr.push(
Promise.all([
this.getAllPermission().then(allPermission => allPermission.map(per => per._id)),
this.storage.getRole(superRole.name),
]).then(([ ids, admin ]) => {
if (admin === null) {
return this.newRole({ name: superRole.name, alias: superRole.alias, grants: ids });
}
return this.addPermission(admin._id, ids);
})
);
debug('init role promise all array length = %s', arr.length);
return Promise.all(arr);
}
/**
* @method rbac#newRole
* @param {object} options role info
* @param {string} options.name - role short name
* @param {string} options.alias - role full name such as chinese name
* @param {string[]} options.grants - string such as mongodb ObjectId
* @return {object} promise
*/
newRole({ name, alias, grants, isself }) {
console.log({ name, alias, grants, isself })
if (!name) {
throw new Error('[egg-rbac] newRole parameter name is undefined');
}
if (!alias) {
throw new Error('[egg-rbac] newRole parameter alias is undefined');
}
if (isself==null) {
throw new Error('[egg-rbac] newRole parameter isself is null');
}
debug('new role name %s alias %s', name, alias);
return this.storage.newRole({ name, alias, grants, isself });
}
newPermissionCheckRepeat({ name, methods }) {
return this.storage.newPermissionCheckRepeat({ name, methods });
}
/**
* @method rbac#newPermission
* @param {object} options role info
* @param {string} options.name - role short name
* @param {string} options.alias - role full name such as chinese name
* @return {object} promise
*/
newPermission({ name, alias, sort, methods }) {
if (!name) {
throw new Error('[egg-rbac] newPermission parameter name is undefined');
}
if (!alias) {
throw new Error('[egg-rbac] newPermission parameter alias is undefined');
}
if (!sort) {
throw new Error('[egg-rbac] newPermission parameter sort is undefined');
}
if (!methods) {
throw new Error('[egg-rbac] newPermission parameter methods is undefined');
}
debug('new permission name %s alias %s', name, alias, sort, methods);
return this.storage.newPermission({ name, alias, sort, methods });
}
newAdpt({name, alias, values}){
if (!name) {
throw new Error('[egg-rbac] newAdpt parameter name is undefined');
}
if (!alias) {
throw new Error('[egg-rbac] newAdpt parameter alias is undefined');
}
if (!values) {
throw new Error('[egg-rbac] newAdpt parameter values is undefined');
}
debug('new adpt name %s alias %s', name, alias, values);
return this.storage.newAdpt({ name, alias, values });
}
/**
* @method rbac#addPermission
* @param {string} _id - role id
* @param {string[]} permissionIds - permission ids
* @return {object} promise
*/
addPermission(_id, permissionIds) {
if (!_id) {
throw new Error('[egg-rbac] addPermission parameter _id is undefined');
}
if (!permissionIds || typeof permissionIds !== 'object' || permissionIds.length === 0) {
throw new Error('[egg-rbac] addPermission parameter permissionIds is undefined');
}
debug('new permission name %s alias %s', _id, permissionIds);
return this.storage.addPermission(_id, permissionIds);
}
/**
* @method rbac#removePermissions
* @param {string} _id - role id
* @param {string[]} permissionIds - permission ids
* @return {object} promise
*/
removePermissions(_id, permissionIds) {
if (!_id) {
throw new Error('[egg-rbac] removePermissions parameter _id is undefined');
}
if (!permissionIds || typeof permissionIds !== 'object' || permissionIds.length === 0) {
throw new Error('[egg-rbac] removePermissions parameter permissionIds is undefined');
}
debug('new permission name %s alias %s', _id, permissionIds);
return this.storage.removePermissions(_id, permissionIds);
}
/**
* @method rbac#removeRole
* @param {string} _id - role _id
* @return {object} promise
*/
removeRole(_id) {
if (!_id) {
throw new Error('[egg-rbac] removeRole parameter _id is undefined');
}
return this.storage.removeRole(_id);
}
removeAdpt(alias) {
if (!alias) {
throw new Error('[egg-rbac] removeAdpt parameter _id is undefined');
}
return this.storage.removeAdpt(alias);
}
/**
* @method rbac#removePermission
* @param {string} _id - permission _id
* @return {object} promise
*/
removePermission(_id) {
if (!_id) {
throw new Error('[egg-rbac] removePermission parameter _id is undefined');
}
return this.storage.removePermission(_id);
}
/**
* @method rbac#modifyRoleAlias
* @param {string} _id - role _id
* @param {string} alias - new alias string
* @return {object} promise
*/
modifyRoleAlias(_id, alias, isself) {
if (!_id) {
throw new Error('[egg-rbac] modifyRoleAlias parameter _id is undefined');
}
if (!alias) {
throw new Error('[egg-rbac] modifyRoleAlias parameter alias is undefined');
}
if (isself == null) {
throw new Error('[egg-rbac] modifyRoleAlias parameter isself is null');
}
return this.storage.modifyRoleAlias(_id, alias, isself);
}
/**
* @method rbac#modifyRoleGrants
* @param {string} _id - role _id
* @param {Array} grants - object array id
* @return {object} promise
*/
modifyRoleGrants(_id, grants) {
if (!_id) {
throw new Error('[egg-rbac] modifyRoleAlias parameter _id is undefined');
}
if (!grants || !Array.isArray(grants)) {
throw new Error('[egg-rbac] modifyRoleGrants parameter grant is not array');
}
return this.storage.modifyRoleGrants(_id, grants);
}
/**
* @method rbac#modifyPermissionAlias
* @param {string} _id - role _id
* @param {string} alias - new alias string
* @param {string} sort - sort of permission
* @return {object} promise
*/
modifyPermissionAlias(_id, alias, sort, methods) {
if (!_id) {
throw new Error('[egg-rbac] modifyPermissionAlias parameter _id is undefined');
}
if (!alias) {
throw new Error('[egg-rbac] modifyPermissionAlias parameter alias is undefined');
}
if (!sort) {
throw new Error('[egg-rbac] modifyPermissionAlias parameter sort is undefined');
}
if (!methods) {
throw new Error('[egg-rbac] modifyPermissionAlias parameter methods is undefined');
}
return this.storage.modifyPermissionAlias(_id, alias, sort, methods);
}
modifyRoleAdpt (_id, adpt) {
if (!_id) throw new Error('[egg-rbac] modifyRoleAdpt parameter _id is undefined')
if (Array.isArray(adpt)) throw new Error('[egg-rbac] modifyRoleAdpt parameter adpt is not array')
return this.storage.modifyRoleAdpt(_id, adpt)
}
/**
* @method rbac#getRolePermission
* @param {string} name - role name
* @return {object} promise
*/
getRolePermission(name) {
if (!name || typeof name !== 'string') {
throw new Error('[egg-rbac] getRolePermission parameter name must string');
}
debug('get role permission role name is %s', name);
return this.storage.getRole(name)
.then(role => role.grants);
}
getRolePermissionAndAdpt(name) {
if (!name || typeof name !== 'string') {
throw new Error('[egg-rbac] getRolePermissionAndAdpt parameter name must string');
}
debug('get role permission role name is %s', name);
return this.storage.getRole(name)
.then((role) => {return {grants:role.grants,adpts:role.adpt,isself:role.isself}});
}
/**
* @method rbac#getAllPermission
* @return {object} promise
*/
getAllPermission() {
return this.storage.Permission.find({});
}
/**
* @method rbac#getRole
* @param {string} name role name
* @return {object} promise
*/
getRole(name) {
if (!name || typeof name !== 'string') {
throw new Error('[egg-rbac] getRole parameter name must string');
}
return this.storage.getRole(name);
}
getAdpt(alias){
if (!alias || typeof alias !== 'string') throw new Error('[egg-rbac] getAdpt parameter alias must string')
return this.storage.getAdpt(alias)
}
getAllAdpt(query = {}) { return this.storage.getAllAdpt(query) }
/**
* @method rbac#getAllRoles
* @return {object} promise
*/
getAllRoles() {
return this.storage.getAllRoles();
}
modifyAdpt ({name, alias, values}) {
return this.storage.modifyAdpt({name, alias, values})
}
/**
* @method rbac#can
* @param {string} permissionName - permission name
* @return {function} middleware function
*/
can(permissionName) {
return async function(ctx, next) {
// this is instance of Context
if (ctx.role && ctx.role.can && ctx.role.can(permissionName)) {
await next();
} else {
// https://tools.ietf.org/html/rfc2616#page-66
ctx.status = 401; // 'Unauthorized'
}
};
}
}
let singleton = null;
module.exports = exports = function(app) {
if (singleton === null) {
singleton = new rbac(app);
}
return singleton;
};