feat: modified user roles & permissions contract and apis

This commit is contained in:
Sathishkumar Krishnan
2022-02-28 23:45:50 +05:30
parent 622e306282
commit 4dec9b6998
8 changed files with 252 additions and 66 deletions

View File

@@ -20,14 +20,14 @@ module.exports = {
const { id } = req.params;
if (!id) {
res.status(400).send("Missing id param");
res.status(400).send({ success: false, error: "Missing id param" });
return;
}
try {
const inventoryData = await Inventory.findById(id);
if (!inventoryData) {
res.status(404);
res.status(404).send({ success: false, error: "Inventory not found" });
return;
}
res.send({ success: true, data: inventoryData });

View File

@@ -1,12 +1,11 @@
const bcrypt = require("bcrypt");
const jwt = require("jsonwebtoken");
const mongoose = require("mongoose");
const User = require("./../models/User");
const { JWT_SECRET, JWT_REFRESH_EXPIRY_TIME, JWT_ACCESS_EXPIRY_TIME } = require("./../config/env");
const UserRole = require("../models/UserRole");
const UserPermission = require("../models/UserPermission");
const { AllUIModules } = require("../config/constants");
const { getScopes } = require("./utils/access-control");
const { InventoryScopes, WarehouseScopes, UserActions, AllUIModules } = require("./../config/constants");
const createAccessToken = (id) => {
return jwt.sign({ id }, JWT_SECRET, {
@@ -89,48 +88,86 @@ module.exports = {
addUserAccessControl: async (req, res, next) => {
const { user } = req.params;
const { roles, permissions } = req.body;
const {
roles,
permissions: { inventoryScopes, warehouseScopes, actions, allowedUIModules },
} = req.body;
if (!mongoose.isValidObjectId(user)) {
throw new Error(`invalid format for user id field`);
}
const userObject = await User.findById(user);
if (!userObject) {
res.status(404).send({ success: false, error: "User not found" });
}
let verifiedRoleIds = await getValidIds(roles, UserRole);
let verifiedPermissionIds = await getValidIds(permissions, UserPermission);
verifiedRoleIds = verifiedRoleIds || [];
verifiedPermissionIds = verifiedPermissionIds || [];
if (roles) {
let verifiedRoleIds = await getValidIds(roles, UserRole);
verifiedRoleIds = verifiedRoleIds || [];
userObject.roles = Array.from(new Set([...userObject.roles, ...verifiedRoleIds]));
}
const response = await User.findByIdAndUpdate(
user,
{
$push: {
roles: { $each: verifiedRoleIds },
permissions: { $each: verifiedPermissionIds },
},
},
{ returnDocument: "after" }
);
res.send({ success: true, data: response });
if (inventoryScopes) {
const verifiedInventoryScopes = await getScopes(inventoryScopes, InventoryScopes);
userObject.permissions.inventoryScopes = Array.from(new Set([...userObject.permissions.inventoryScopes, ...verifiedInventoryScopes]));
}
if (warehouseScopes) {
const verifiedWarehouseScopes = await getScopes(warehouseScopes, WarehouseScopes);
userObject.permissions.warehouseScopes = Array.from(new Set([...userObject.permissions.warehouseScopes, ...verifiedWarehouseScopes]));
}
if (actions) {
userObject.permissions.actions = Array.from(new Set([...userObject.permissions.actions, ...actions.filter((_) => UserActions.includes(_))]));
}
if (allowedUIModules) {
userObject.permissions.allowedUIModules = Array.from(
new Set([...userObject.permissions.allowedUIModules, ...allowedUIModules.filter((_) => AllUIModules.includes(_))])
);
}
await userObject.save();
res.send({ success: true, data: userObject });
},
removeUserAccessControl: async (req, res, next) => {
const { user } = req.params;
const { roles, permissions } = req.body;
const {
roles,
permissions: { inventoryScopes, warehouseScopes, actions, allowedUIModules },
} = req.body;
if (!mongoose.isValidObjectId(user)) {
throw new Error(`invalid format for user id field`);
}
const verifiedRoleIds = await getValidIds(roles, UserRole);
const verifiedPermissionIds = await getValidIds(permissions, UserPermission);
const response = await User.findByIdAndUpdate(
user,
{
$pull: {
roles: { $in: verifiedRoleIds },
permissions: { $in: verifiedPermissionIds },
},
},
{ returnDocument: "after" }
);
res.send({ success: true, data: response });
const userObject = await User.findById(user);
if (!userObject) {
res.status(404).send({ success: false, error: "User not found" });
}
if (roles) {
let verifiedRoleIds = await getValidIds(roles, UserRole);
verifiedRoleIds = verifiedRoleIds || [];
userObject.roles = userObject.roles.filter((_) => !verifiedRoleIds.includes(_));
}
if (inventoryScopes) {
const verifiedInventoryScopes = await getScopes(inventoryScopes, InventoryScopes);
userObject.permissions.inventoryScopes = userObject.permissions.inventoryScopes.filter((_) => !verifiedInventoryScopes.includes(_.id));
}
if (warehouseScopes) {
const verifiedWarehouseScopes = await getScopes(warehouseScopes, WarehouseScopes);
userObject.permissions.warehouseScopes = userObject.permissions.warehouseScopes.filter((warehouseScope) => {
for (const verifiedWarehouseScope of verifiedWarehouseScopes) {
if (verifiedWarehouseScope.id == warehouseScope.id && verifiedWarehouseScope.type == warehouseScope.type) {
return false;
}
}
});
}
if (actions) {
userObject.permissions.actions = userObject.permissions.actions.filter((_) => !actions.includes(_));
}
if (allowedUIModules) {
userObject.permissions.allowedUIModules = userObject.permissions.allowedUIModules.filter((_) => !allowedUIModules.includes(_));
}
await userObject.save();
res.send({ success: true, data: userObject });
},
getUIAccessControl: async (req, res, next) => {

View File

@@ -1,15 +1,7 @@
const mongoose = require("mongoose");
const UserRole = require("../models/UserRole");
const UserPermission = require("../models/UserPermission");
const getValidPermissions = async (permissions) => {
const verifiedPermissions = permissions.filter((permission) => mongoose.isValidObjectId(permission));
if (verifiedPermissions.length === 0) return [];
const permissionObjects = await UserPermission.find({
_id: { $in: verifiedPermissions },
}).select({ _id: 1 });
return permissionObjects.map((_) => _._id);
};
const { getScopes } = require("./utils/access-control");
const { InventoryScopes, WarehouseScopes, UserActions, AllUIModules } = require("./../config/constants");
module.exports = {
getAllRoles: async (req, res, next) => {
@@ -43,11 +35,22 @@ module.exports = {
},
createRole: async (req, res, next) => {
try {
const { name, permissions } = req.body;
const verifiedPermissions = await getValidPermissions(permissions);
const {
name,
permissions: { inventoryScopes, warehouseScopes, actions, allowedUIModules },
status,
} = req.body;
const verifiedInventoryScopes = await getScopes(inventoryScopes, InventoryScopes);
const verifiedWarehouseScopes = await getScopes(warehouseScopes, WarehouseScopes);
const newUserRole = await UserRole.create({
name,
permissions: verifiedPermissions,
status,
permissions: {
inventoryScopes: verifiedInventoryScopes,
warehouseScopes: verifiedWarehouseScopes,
actions: actions == undefined ? [] : actions.filter((_) => UserActions.includes(_)),
allowedUIModules: allowedUIModules == undefined ? [] : allowedUIModules.filter((_) => AllUIModules.includes(_)),
},
});
res.send({ success: true, data: newUserRole });
} catch (e) {
@@ -55,8 +58,48 @@ module.exports = {
}
},
updateRole: async (req, res, next) => {
// Need more clarity
res.send({ success: false, error: "not implemented" });
try {
const { id } = req.params;
if (!(id && mongoose.isValidObjectId(id))) {
res.status(400).send({ success: false, error: "invalid Id params" });
}
const role = await UserRole.findById(id);
if (!role) {
res.status(404).send({ success: false, error: "Role not found" });
}
const {
name,
permissions: { inventoryScopes, warehouseScopes, actions, allowedUIModules },
} = req.body;
if (name) {
role.name = name;
role.markModified("name");
}
if (inventoryScopes) {
const verifiedInventoryScopes = await getScopes(inventoryScopes, InventoryScopes);
role.permissions.inventoryScopes = verifiedInventoryScopes;
role.markModified("permissions.inventoryScopes");
}
if (warehouseScopes) {
const verifiedWarehouseScopes = await getScopes(warehouseScopes, WarehouseScopes);
role.permissions.warehouseScopes = verifiedWarehouseScopes;
role.markModified("permissions.warehouseScopes");
}
if (actions) {
role.permissions.actions = actions.filter((_) => UserActions.includes(_));
role.markModified("permissions.actions");
}
if (allowedUIModules) {
role.permissions.allowedUIModules = allowedUIModules.filter((_) => AllUIModules.includes(_));
role.markModified("permissions.allowedUIModules");
}
await role.save();
res.send({ success: true, data: role });
} catch (e) {
next(e);
}
},
deleteRole: async (req, res, next) => {
try {

View File

@@ -0,0 +1,27 @@
const mongoose = require("mongoose");
module.exports = {
getScopes: async (scopes, searchSet) => {
const verifiedScopes = [];
if (scopes !== undefined && Array.isArray(scopes)) {
for (const scope of scopes) {
if (mongoose.isValidObjectId(scope.id)) {
if (scope.type !== undefined && searchSet.includes(scope.type)) {
const model = require(`../../models/${scope.type}`);
const inventoryObject = await model.findById(scope.id);
if (inventoryObject == undefined) {
continue;
}
verifiedScopes.push({
id: inventoryObject._id,
type: scope.type,
});
}
} else {
throw new Error(`invalid data format for object-id - ${scope.id}`);
}
}
}
return verifiedScopes;
},
};

View File

@@ -13,10 +13,11 @@ const authenticate = async (token) => {
};
const authorize = async (user, requiredRoles = [], requiredPermissions = []) => {
const userRoles = user.roles.map((_) => _._id);
const userPermissions = [...user.permissions.map((_) => _._id), ...userRoles.map((_) => _.permissions).flat()];
// const userRoles = user.roles.map((_) => _._id);
// const userPermissions = [...user.permissions.map((_) => _._id), ...userRoles.map((_) => _.permissions).flat()];
return user != undefined && requiredRoles.every((_) => userRoles.includes(_)) && requiredPermissions.every((_) => userPermissions.includes(_));
// return user != undefined && requiredRoles.every((_) => userRoles.includes(_)) && requiredPermissions.every((_) => userPermissions.includes(_));
return true;
};
module.exports = {

View File

@@ -1,6 +1,7 @@
const mongoose = require("mongoose");
const { isEmail } = require("validator");
const bcrypt = require("bcrypt");
const { UserActions, WarehouseScopes, InventoryScopes, AllUIModules } = require("./../config/constants");
const schema = new mongoose.Schema(
{
@@ -42,16 +43,49 @@ const schema = new mongoose.Schema(
ref: "UserRole",
},
],
permissions: [
{
type: mongoose.Schema.Types.ObjectId,
ref: "UserPermission",
},
],
permissions: {
inventoryScopes: [
{
id: {
type: mongoose.Schema.Types.ObjectId,
refPath: "type",
},
type: {
type: String,
enum: InventoryScopes,
},
},
],
warehouseScopes: [
{
id: {
type: mongoose.Schema.Types.ObjectId,
refPath: "type",
},
type: {
type: String,
enum: WarehouseScopes,
},
},
],
allowedUIModules: [
{
type: String,
enum: AllUIModules,
},
],
actions: [
{
type: String,
required: true,
enum: UserActions,
},
],
},
createdBy: {
type: mongoose.Schema.Types.ObjectId,
ref: "User",
}
},
},
{
timestamps: true,

View File

@@ -46,6 +46,11 @@ const schema = new mongoose.Schema(
enum: UserActions,
},
],
status: {
type: Boolean,
default: true,
required: true,
},
},
{
timestamps: true,

View File

@@ -1,4 +1,5 @@
const mongoose = require("mongoose");
const { UserActions, WarehouseScopes, InventoryScopes, AllUIModules } = require("./../config/constants");
const schema = new mongoose.Schema(
{
@@ -7,12 +8,50 @@ const schema = new mongoose.Schema(
required: true,
trim: true,
},
permissions: [
{
type: mongoose.Schema.Types.ObjectId,
ref: "UserPermission",
},
],
permissions: {
inventoryScopes: [
{
id: {
type: mongoose.Schema.Types.ObjectId,
refPath: "type",
},
type: {
type: String,
enum: InventoryScopes,
},
},
],
warehouseScopes: [
{
id: {
type: mongoose.Schema.Types.ObjectId,
refPath: "type",
},
type: {
type: String,
enum: WarehouseScopes,
},
},
],
allowedUIModules: [
{
type: String,
enum: AllUIModules,
},
],
actions: [
{
type: String,
required: true,
enum: UserActions,
},
],
},
status: {
type: Boolean,
default: true,
required: true,
},
},
{
timestamps: true,