mailtrain/server/models/shares.js

729 lines
27 KiB
JavaScript
Raw Normal View History

2017-07-24 04:03:32 +00:00
'use strict';
const knex = require('../lib/knex');
const config = require('../lib/config');
const { enforce, castToInteger } = require('../lib/helpers');
2017-07-24 04:03:32 +00:00
const dtHelpers = require('../lib/dt-helpers');
2018-08-03 11:35:55 +00:00
const entitySettings = require('../lib/entity-settings');
const interoperableErrors = require('../../shared/interoperable-errors');
const log = require('../lib/log');
const {getGlobalNamespaceId} = require('../../shared/namespaces');
const {getAdminId} = require('../../shared/users');
2017-07-24 04:03:32 +00:00
// TODO: This would really benefit from some permission cache connected to rebuildPermissions
// A bit of the problem is that the cache would have to expunged as the result of other processes modifying entites/permissions
2017-07-24 04:03:32 +00:00
async function listByEntityDTAjax(context, entityTypeId, entityId, params) {
2017-08-13 18:11:58 +00:00
return await knex.transaction(async (tx) => {
2018-08-03 11:35:55 +00:00
const entityType = entitySettings.getEntityType(entityTypeId);
2017-08-13 18:11:58 +00:00
await enforceEntityPermissionTx(tx, context, entityTypeId, entityId, 'share');
2017-07-24 04:03:32 +00:00
2017-08-13 18:11:58 +00:00
return await dtHelpers.ajaxListTx(
tx,
params,
builder => builder
.from(entityType.sharesTable)
.innerJoin('users', entityType.sharesTable + '.user', 'users.id')
.innerJoin('generated_role_names', {
'generated_role_names.role': entityType.sharesTable + '.role',
'generated_role_names.entity_type': knex.raw('?', [entityTypeId])
})
2017-08-13 18:11:58 +00:00
.where(`${entityType.sharesTable}.entity`, entityId),
['users.username', 'users.name', 'generated_role_names.name', 'users.id', entityType.sharesTable + '.auto']
);
});
}
async function listByUserDTAjax(context, entityTypeId, userId, params) {
2017-08-13 18:11:58 +00:00
return await knex.transaction(async (tx) => {
const user = await tx('users').where('id', userId).first();
if (!user) {
shares.throwPermissionDenied();
}
2017-08-13 18:11:58 +00:00
await enforceEntityPermissionTx(tx, context, 'namespace', user.namespace, 'manageUsers');
2018-08-03 11:35:55 +00:00
const entityType = entitySettings.getEntityType(entityTypeId);
2017-08-13 18:11:58 +00:00
return await dtHelpers.ajaxListWithPermissionsTx(
tx,
context,
[{entityTypeId}],
params,
builder => builder
.from(entityType.sharesTable)
.innerJoin(entityType.entitiesTable, entityType.sharesTable + '.entity', entityType.entitiesTable + '.id')
.innerJoin('generated_role_names', 'generated_role_names.role', entityType.sharesTable + '.role')
.where('generated_role_names.entity_type', entityTypeId)
.where(entityType.sharesTable + '.user', userId),
[entityType.entitiesTable + '.name', 'generated_role_names.name', entityType.entitiesTable + '.id', entityType.sharesTable + '.auto']
);
});
2017-07-24 04:03:32 +00:00
}
async function listUnassignedUsersDTAjax(context, entityTypeId, entityId, params) {
2017-08-13 18:11:58 +00:00
return await knex.transaction(async (tx) => {
2018-08-03 11:35:55 +00:00
const entityType = entitySettings.getEntityType(entityTypeId);
2017-08-13 18:11:58 +00:00
await enforceEntityPermissionTx(tx, context, entityTypeId, entityId, 'share');
2017-07-24 04:03:32 +00:00
2017-08-13 18:11:58 +00:00
return await dtHelpers.ajaxListTx(
tx,
params,
builder => builder
.from('users')
.whereNotExists(function () {
return this
.select('*')
.from(entityType.sharesTable)
.whereRaw(`users.id = ${entityType.sharesTable}.user`)
.andWhere(`${entityType.sharesTable}.entity`, entityId);
}),
['users.id', 'users.username', 'users.name']
);
});
2017-07-24 04:03:32 +00:00
}
2017-08-13 18:11:58 +00:00
async function listRolesDTAjax(entityTypeId, params) {
return await dtHelpers.ajaxList(
params,
builder => builder
.from('generated_role_names')
.where({entity_type: entityTypeId}),
['role', 'name', 'description']
);
}
2017-07-24 04:03:32 +00:00
async function assign(context, entityTypeId, entityId, userId, role) {
2018-08-03 11:35:55 +00:00
const entityType = entitySettings.getEntityType(entityTypeId);
2017-07-24 04:03:32 +00:00
await knex.transaction(async tx => {
2017-08-11 06:51:30 +00:00
await enforceEntityPermissionTx(tx, context, entityTypeId, entityId, 'share');
2017-07-24 04:03:32 +00:00
enforce(await tx('users').where('id', userId).select('id').first(), 'Invalid user id');
const extraColumns = entityType.dependentPermissions ? entityType.dependentPermissions.extraColumns : [];
const entity = await tx(entityType.entitiesTable).where('id', entityId).select(['id', ...extraColumns]).first();
enforce(entity, 'Invalid entity id');
if (entityType.dependentPermissions) {
enforce(!entityType.dependentPermissions.getParent(entity), 'Cannot share/unshare a dependent entity');
}
2017-07-24 04:03:32 +00:00
const entry = await tx(entityType.sharesTable).where({user: userId, entity: entityId}).select('role').first();
2017-07-24 04:03:32 +00:00
if (entry) {
if (!role) {
await tx(entityType.sharesTable).where({user: userId, entity: entityId}).del();
2017-07-24 04:03:32 +00:00
} else if (entry.role !== role) {
await tx(entityType.sharesTable).where({user: userId, entity: entityId}).update('role', role);
2017-07-24 04:03:32 +00:00
}
} else {
await tx(entityType.sharesTable).insert({
user: userId,
entity: entityId,
role
});
}
await tx(entityType.permissionsTable).where({user: userId, entity: entityId}).del();
if (entityTypeId === 'namespace') {
await rebuildPermissionsTx(tx, {userId});
} else if (role) {
await rebuildPermissionsTx(tx, { entityTypeId, entityId, userId });
2017-07-24 04:03:32 +00:00
}
});
}
async function rebuildPermissionsTx(tx, restriction) {
restriction = restriction || {};
2018-08-03 11:35:55 +00:00
const namespaceEntityType = entitySettings.getEntityType('namespace');
// Collect entity types we care about
let restrictedEntityTypes;
if (restriction.entityTypeId) {
2018-08-03 11:35:55 +00:00
const entityType = entitySettings.getEntityType(restriction.entityTypeId);
restrictedEntityTypes = {
[restriction.entityTypeId]: entityType
};
} else {
restrictedEntityTypes = entitySettings.getEntityTypesWithPermissions();
}
// To prevent users locking out themselves, we consider user with id 1 to be the admin and always assign it
// the admin role. The admin role is a global role that has admin===true
// If this behavior is not desired, it is enough to delete the user with id 1.
2018-09-01 19:29:10 +00:00
const adminUser = await tx('users').where('id', getAdminId()).first();
if (adminUser) {
let adminRole;
for (const role in config.roles.global) {
if (config.roles.global[role].admin) {
adminRole = role;
break;
}
}
if (adminRole) {
2018-09-01 19:29:10 +00:00
await tx('users').update('role', adminRole).where('id', getAdminId());
}
}
// Reset root, own and shared namespaces shares as per the user roles
const usersAutoSharesQry = tx('users')
.select(['users.id', 'users.role', 'users.namespace']);
if (restriction.userId) {
usersAutoSharesQry.where('users.id', restriction.userId);
}
const usersAutoShares = await usersAutoSharesQry;
for (const user of usersAutoShares) {
const roleConf = config.roles.global[user.role];
if (roleConf) {
const desiredRoles = new Map();
if (roleConf.sharedNamespaces) {
for (const shrKey in roleConf.sharedNamespaces) {
const shrRole = roleConf.sharedNamespaces[shrKey];
const shrNsId = castToInteger(shrKey);
desiredRoles.set(shrNsId, shrRole);
}
}
if (roleConf.ownNamespaceRole) {
desiredRoles.set(user.namespace, roleConf.ownNamespaceRole);
}
if (roleConf.rootNamespaceRole) {
desiredRoles.set(getGlobalNamespaceId(), roleConf.rootNamespaceRole);
}
for (const [nsId, role] of desiredRoles.entries()) {
await tx(namespaceEntityType.sharesTable).where({ user: user.id, entity: nsId }).del();
await tx(namespaceEntityType.sharesTable).insert({ user: user.id, entity: nsId, role: role, auto: true });
}
}
}
// Build the map of all namespaces
// nsMap is a map of namespaces - each of the following shape:
// .id - id of the namespace
// .namespace - id of the parent or null if no parent
// .userPermissions - Map userId -> [entityTypeId] -> array of permissions
// .transitiveUserPermissions - the same as above, but taking into account transitive permission obtained from namespace parents
const namespaces = await tx('namespaces').select(['id', 'namespace']);
const nsMap = new Map();
for (const namespace of namespaces) {
namespace.userPermissions = new Map();
nsMap.set(namespace.id, namespace);
}
// This populates .userPermissions
const nsSharesQuery = tx(namespaceEntityType.sharesTable).select(['entity', 'user', 'role']);
if (restriction.userId) {
nsSharesQuery.where('user', restriction.userId);
}
const nsShares = await nsSharesQuery;
for (const nsShare of nsShares) {
const ns = nsMap.get(nsShare.entity);
2017-07-24 04:03:32 +00:00
const userPerms = {};
ns.userPermissions.set(nsShare.user, userPerms);
2017-07-24 04:03:32 +00:00
for (const entityTypeId in restrictedEntityTypes) {
if (config.roles.namespace[nsShare.role] &&
config.roles.namespace[nsShare.role].children &&
config.roles.namespace[nsShare.role].children[entityTypeId]) {
userPerms[entityTypeId] = new Set(config.roles.namespace[nsShare.role].children[entityTypeId]);
} else {
userPerms[entityTypeId] = new Set();
2017-07-24 04:03:32 +00:00
}
}
}
// This computes .transitiveUserPermissions
for (const ns of nsMap.values()) {
ns.transitiveUserPermissions = new Map();
for (const userPermsPair of ns.userPermissions) {
const userPerms = {};
ns.transitiveUserPermissions.set(userPermsPair[0], userPerms);
for (const entityTypeId in restrictedEntityTypes) {
userPerms[entityTypeId] = new Set(userPermsPair[1][entityTypeId]);
}
}
let parentId = ns.namespace;
while (parentId) {
const parent = nsMap.get(parentId);
for (const userPermsPair of parent.userPermissions) {
const user = userPermsPair[0];
if (ns.transitiveUserPermissions.has(user)) {
const userPerms = ns.transitiveUserPermissions.get(user);
for (const entityTypeId in restrictedEntityTypes) {
for (const perm of userPermsPair[1][entityTypeId]) {
userPerms[entityTypeId].add(perm);
}
}
} else {
const userPerms = {};
ns.transitiveUserPermissions.set(user, userPerms);
for (const entityTypeId in restrictedEntityTypes) {
userPerms[entityTypeId] = new Set(userPermsPair[1][entityTypeId]);
}
}
}
parentId = parent.namespace;
}
}
// This reads direct shares from DB, joins each with the permissions from namespaces and stores the permissions into DB
for (const entityTypeId in restrictedEntityTypes) {
const entityType = restrictedEntityTypes[entityTypeId];
const expungeQuery = tx(entityType.permissionsTable).del();
if (restriction.entityId) {
expungeQuery.where('entity', restriction.entityId);
}
if (restriction.userId) {
expungeQuery.where('user', restriction.userId);
}
await expungeQuery;
const extraColumns = entityType.dependentPermissions ? entityType.dependentPermissions.extraColumns : [];
const entitiesQuery = tx(entityType.entitiesTable).select(['id', 'namespace', ...extraColumns]);
const notToBeInserted = new Set();
if (restriction.entityId) {
if (restriction.parentId) {
notToBeInserted.add(restriction.parentId);
entitiesQuery.whereIn('id', [restriction.entityId, restriction.parentId]);
} else {
entitiesQuery.where('id', restriction.entityId);
}
}
const entities = await entitiesQuery;
const parentEntities = new Map();
let nonChildEntities;
if (entityType.dependentPermissions) {
nonChildEntities = [];
for (const entity of entities) {
const parent = entityType.dependentPermissions.getParent(entity);
if (parent) {
let childEntities;
if (parentEntities.has(parent)) {
childEntities = parentEntities.get(parent);
} else {
childEntities = [];
parentEntities.set(parent, childEntities);
}
childEntities.push(entity.id);
} else {
nonChildEntities.push(entity);
}
}
} else {
nonChildEntities = entities;
}
for (const entity of nonChildEntities) {
const permsPerUser = new Map();
if (entity.namespace) { // The root namespace has not parent namespace, thus the test
const transitiveUserPermissions = nsMap.get(entity.namespace).transitiveUserPermissions;
for (const transitivePermsPair of transitiveUserPermissions.entries()) {
permsPerUser.set(transitivePermsPair[0], new Set(transitivePermsPair[1][entityTypeId]));
}
}
const directSharesQuery = tx(entityType.sharesTable).select(['user', 'role']).where('entity', entity.id);
if (restriction.userId) {
directSharesQuery.andWhere('user', restriction.userId);
}
const directShares = await directSharesQuery;
for (const share of directShares) {
let userPerms;
if (permsPerUser.has(share.user)) {
userPerms = permsPerUser.get(share.user);
} else {
userPerms = new Set();
permsPerUser.set(share.user, userPerms);
}
if (config.roles[entityTypeId][share.role] &&
config.roles[entityTypeId][share.role].permissions) {
for (const perm of config.roles[entityTypeId][share.role].permissions) {
userPerms.add(perm);
}
}
}
if (!notToBeInserted.has(entity.id)) {
for (const userPermsPair of permsPerUser.entries()) {
const data = [];
for (const operation of userPermsPair[1]) {
data.push({user: userPermsPair[0], entity: entity.id, operation});
}
if (data.length > 0) {
await tx(entityType.permissionsTable).insert(data);
}
}
}
if (parentEntities.has(entity.id)) {
const childEntities = parentEntities.get(entity.id);
for (const childId of childEntities) {
for (const userPermsPair of permsPerUser.entries()) {
const data = [];
for (const operation of userPermsPair[1]) {
if (operation !== 'share') {
data.push({user: userPermsPair[0], entity: childId, operation});
}
}
if (data.length > 0) {
await tx(entityType.permissionsTable).insert(data);
}
}
}
}
}
}
2017-07-24 04:03:32 +00:00
}
async function rebuildPermissions(restriction) {
await knex.transaction(async tx => {
await rebuildPermissionsTx(tx, restriction);
});
}
async function regenerateRoleNamesTable() {
await knex.transaction(async tx => {
await tx('generated_role_names').del();
const entityTypeIds = ['global', ...Object.keys(entitySettings.getEntityTypesWithPermissions())];
for (const entityTypeId of entityTypeIds) {
const roles = config.roles[entityTypeId];
for (const role in roles) {
await tx('generated_role_names').insert({
entity_type: entityTypeId,
role,
name: roles[role].name,
description: roles[role].description,
});
}
}
});
}
function throwPermissionDenied() {
throw new interoperableErrors.PermissionDeniedError('Permission denied');
}
async function removeDefaultShares(tx, user) {
2018-08-03 11:35:55 +00:00
const namespaceEntityType = entitySettings.getEntityType('namespace');
const roleConf = config.roles.global[user.role];
if (roleConf) {
const desiredRole = roleConf.rootNamespaceRole;
if (roleConf.ownNamespaceRole) {
await tx(namespaceEntityType.sharesTable).where({ user: user.id, entity: user.namespace }).del();
}
if (roleConf.rootNamespaceRole) {
await tx(namespaceEntityType.sharesTable).where({ user: user.id, entity: getGlobalNamespaceId() }).del();
}
}
}
2017-08-13 18:11:58 +00:00
function checkGlobalPermission(context, requiredOperations) {
if (!context.user) {
return false;
}
if (typeof requiredOperations === 'string') {
requiredOperations = [ requiredOperations ];
}
if (context.user.restrictedAccessHandler) {
2018-07-18 17:41:18 +00:00
const originalRequiredOperations = requiredOperations;
const allowedPerms = context.user.restrictedAccessHandler.globalPermissions;
if (allowedPerms) {
requiredOperations = requiredOperations.filter(perm => allowedPerms.has(perm));
2018-07-18 17:41:18 +00:00
} else {
requiredOperations = [];
}
2018-07-18 17:41:18 +00:00
log.verbose('check global permissions with restrictedAccessHandler -- requiredOperations: [' + originalRequiredOperations + '] -> [' + requiredOperations + ']');
}
if (requiredOperations.length === 0) {
return false;
}
if (context.user.admin) { // This handles the getAdminContext() case
return true;
}
const roleSpec = config.roles.global[context.user.role];
2017-08-13 18:11:58 +00:00
let success = false;
if (roleSpec) {
for (const requiredOperation of requiredOperations) {
if (roleSpec.permissions.includes(requiredOperation)) {
2017-08-13 18:11:58 +00:00
success = true;
break;
}
}
}
2017-08-13 18:11:58 +00:00
return success;
}
function enforceGlobalPermission(context, requiredOperations) {
if (!checkGlobalPermission(context, requiredOperations)) {
throwPermissionDenied();
}
}
2017-08-11 06:51:30 +00:00
async function _checkPermissionTx(tx, context, entityTypeId, entityId, requiredOperations) {
if (!context.user) {
return false;
}
2018-08-03 11:35:55 +00:00
const entityType = entitySettings.getEntityType(entityTypeId);
if (typeof requiredOperations === 'string') {
requiredOperations = [ requiredOperations ];
}
2019-01-04 20:31:01 +00:00
requiredOperations = filterPermissionsByRestrictedAccessHandler(context, entityTypeId, entityId, requiredOperations, 'checkPermissions');
if (requiredOperations.length === 0) {
return false;
}
2017-08-11 06:51:30 +00:00
if (context.user.admin) { // This handles the getAdminContext() case. In this case we don't check the permission, but just the existence.
const existsQuery = tx(entityType.entitiesTable);
if (entityId) {
existsQuery.where('id', entityId);
}
2017-08-11 06:51:30 +00:00
const exists = await existsQuery.first();
2017-08-11 06:51:30 +00:00
return !!exists;
} else {
const permsQuery = tx(entityType.permissionsTable)
.where('user', context.user.id)
.whereIn('operation', requiredOperations);
2017-08-11 06:51:30 +00:00
if (entityId) {
permsQuery.andWhere('entity', entityId);
}
const perms = await permsQuery.first();
return !!perms;
}
}
async function checkEntityPermission(context, entityTypeId, entityId, requiredOperations) {
if (!entityId) {
return false;
}
2017-08-13 18:11:58 +00:00
return await knex.transaction(async tx => {
return await _checkPermissionTx(tx, context, entityTypeId, entityId, requiredOperations);
2017-08-11 06:51:30 +00:00
});
}
async function checkEntityPermissionTx(tx, context, entityTypeId, entityId, requiredOperations) {
if (!entityId) {
return false;
}
return await _checkPermissionTx(tx, context, entityTypeId, entityId, requiredOperations);
}
async function checkTypePermission(context, entityTypeId, requiredOperations) {
2017-08-13 18:11:58 +00:00
return await knex.transaction(async tx => {
return await _checkPermissionTx(tx, context, entityTypeId, null, requiredOperations);
2017-08-11 06:51:30 +00:00
});
}
async function enforceEntityPermission(context, entityTypeId, entityId, requiredOperations) {
2017-08-11 06:51:30 +00:00
if (!entityId) {
throwPermissionDenied();
}
await knex.transaction(async tx => {
const result = await _checkPermissionTx(tx, context, entityTypeId, entityId, requiredOperations);
if (!result) {
2018-05-21 17:41:10 +00:00
log.info(`Denying permission ${entityTypeId}.${entityId} ${requiredOperations}`);
2017-08-11 06:51:30 +00:00
throwPermissionDenied();
}
});
}
async function enforceEntityPermissionTx(tx, context, entityTypeId, entityId, requiredOperations) {
if (!entityId) {
throwPermissionDenied();
}
const result = await _checkPermissionTx(tx, context, entityTypeId, entityId, requiredOperations);
if (!result) {
2018-05-21 17:41:10 +00:00
log.info(`Denying permission ${entityTypeId}.${entityId} ${requiredOperations}`);
throwPermissionDenied();
}
}
async function enforceTypePermission(context, entityTypeId, requiredOperations) {
2017-08-11 06:51:30 +00:00
await knex.transaction(async tx => {
const result = await _checkPermissionTx(tx, context, entityTypeId, null, requiredOperations);
if (!result) {
2018-05-21 17:41:10 +00:00
log.info(`Denying permission ${entityTypeId} ${requiredOperations}`);
2017-08-11 06:51:30 +00:00
throwPermissionDenied();
}
});
}
async function enforceTypePermissionTx(tx, context, entityTypeId, requiredOperations) {
const result = await _checkPermissionTx(tx, context, entityTypeId, null, requiredOperations);
if (!result) {
2018-05-21 17:41:10 +00:00
log.info(`Denying permission ${entityTypeId} ${requiredOperations}`);
throwPermissionDenied();
}
}
2017-08-13 18:11:58 +00:00
function getGlobalPermissions(context) {
if (!context.user) {
return [];
}
2017-08-13 18:11:58 +00:00
enforce(!context.user.admin, 'getPermissions is not supposed to be called by assumed admin');
return (config.roles.global[context.user.role] || {}).permissions || [];
}
async function getPermissionsTx(tx, context, entityTypeId, entityId) {
if (!context.user) {
return [];
}
2017-08-13 18:11:58 +00:00
enforce(!context.user.admin, 'getPermissions is not supposed to be called by assumed admin');
2018-08-03 11:35:55 +00:00
const entityType = entitySettings.getEntityType(entityTypeId);
const rows = await tx(entityType.permissionsTable)
.select('operation')
.where('entity', entityId)
.where('user', context.user.id);
2019-01-04 20:31:01 +00:00
const operations = rows.map(x => x.operation);
return filterPermissionsByRestrictedAccessHandler(context, entityTypeId, entityId, operations, 'getPermissions');
}
// If entityId is null, it means that we require that restrictedAccessHandler does not differentiate based on entityId. This is used in ajaxListWithPermissionsTx.
function filterPermissionsByRestrictedAccessHandler(context, entityTypeId, entityId, permissions, operationMsg) {
if (context.user.restrictedAccessHandler) {
const originalOperations = permissions;
if (context.user.restrictedAccessHandler.permissions) {
const entityPerms = context.user.restrictedAccessHandler.permissions[entityTypeId];
if (!entityPerms) {
permissions = [];
} else if (entityPerms === true) {
// no change to operations
} else if (entityPerms instanceof Set) {
permissions = permissions.filter(perm => entityPerms.has(perm));
} else {
if (entityId) {
const allowedPerms = entityPerms[entityId];
if (allowedPerms) {
permissions = permissions.filter(perm => allowedPerms.has(perm));
} else {
const allowedPerms = entityPerms['default'];
if (allowedPerms) {
permissions = permissions.filter(perm => allowedPerms.has(perm));
} else {
permissions = [];
}
}
} else {
const allowedPerms = entityPerms['default'];
if (allowedPerms) {
permissions = permissions.filter(perm => allowedPerms.has(perm));
} else {
permissions = [];
}
}
}
} else {
permissions = [];
}
log.verbose(operationMsg + ' with restrictedAccessHandler -- entityTypeId: ' + entityTypeId + ' entityId: ' + entityId + ' operations: [' + originalOperations + '] -> [' + permissions + ']');
}
return permissions;
}
2019-01-04 20:31:01 +00:00
function isAccessibleByRestrictedAccessHandler(context, entityTypeId, entityId, permissions, operationMsg) {
return filterPermissionsByRestrictedAccessHandler(context, entityTypeId, entityId, permissions, operationMsg).length > 0;
}
module.exports.listByEntityDTAjax = listByEntityDTAjax;
module.exports.listByUserDTAjax = listByUserDTAjax;
module.exports.listUnassignedUsersDTAjax = listUnassignedUsersDTAjax;
module.exports.listRolesDTAjax = listRolesDTAjax;
module.exports.assign = assign;
module.exports.rebuildPermissionsTx = rebuildPermissionsTx;
module.exports.rebuildPermissions = rebuildPermissions;
module.exports.removeDefaultShares = removeDefaultShares;
module.exports.enforceEntityPermission = enforceEntityPermission;
module.exports.enforceEntityPermissionTx = enforceEntityPermissionTx;
module.exports.enforceTypePermission = enforceTypePermission;
module.exports.enforceTypePermissionTx = enforceTypePermissionTx;
module.exports.checkEntityPermissionTx = checkEntityPermissionTx;
module.exports.checkEntityPermission = checkEntityPermission;
module.exports.checkTypePermission = checkTypePermission;
module.exports.enforceGlobalPermission = enforceGlobalPermission;
module.exports.checkGlobalPermission = checkGlobalPermission;
module.exports.throwPermissionDenied = throwPermissionDenied;
module.exports.regenerateRoleNamesTable = regenerateRoleNamesTable;
module.exports.getGlobalPermissions = getGlobalPermissions;
module.exports.getPermissionsTx = getPermissionsTx;
2019-01-04 20:31:01 +00:00
module.exports.filterPermissionsByRestrictedAccessHandler = filterPermissionsByRestrictedAccessHandler;
module.exports.isAccessibleByRestrictedAccessHandler = isAccessibleByRestrictedAccessHandler;