feat: modified user roles & permissions contract and apis
This commit is contained in:
@@ -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 });
|
||||
|
||||
@@ -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) => {
|
||||
|
||||
@@ -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 {
|
||||
|
||||
27
src/controller/utils/access-control.js
Normal file
27
src/controller/utils/access-control.js
Normal 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;
|
||||
},
|
||||
};
|
||||
@@ -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 = {
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -46,6 +46,11 @@ const schema = new mongoose.Schema(
|
||||
enum: UserActions,
|
||||
},
|
||||
],
|
||||
status: {
|
||||
type: Boolean,
|
||||
default: true,
|
||||
required: true,
|
||||
},
|
||||
},
|
||||
{
|
||||
timestamps: true,
|
||||
|
||||
@@ -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,
|
||||
|
||||
Reference in New Issue
Block a user