From 6f88edf950f49dd5cda220d0af0f3865c4289a41 Mon Sep 17 00:00:00 2001 From: Foyzul Karim Date: Thu, 31 Mar 2022 22:13:22 +1100 Subject: [PATCH] work in progress --- server/setup/migrate.js | 6 + server/setup/permissions.js | 27 +++- server/setup/permissions.jsonc | 176 ++++++++++++++++++++- server/setup/resources.js | 34 +++- server/setup/roles.js | 22 ++- server/src/modules/auth/index.js | 15 +- server/src/modules/auth/model.js | 5 +- server/src/modules/auth/service.js | 40 +---- server/src/modules/auth/user-controller.js | 104 ++---------- server/src/modules/customer/model.js | 10 ++ server/src/modules/permission/model.js | 33 ++-- server/src/modules/product/model.js | 10 ++ server/src/modules/resource/model.js | 10 ++ server/src/modules/role/model.js | 10 ++ 14 files changed, 354 insertions(+), 148 deletions(-) diff --git a/server/setup/migrate.js b/server/setup/migrate.js index 52ca1f4..88b8fa8 100644 --- a/server/setup/migrate.js +++ b/server/setup/migrate.js @@ -3,6 +3,9 @@ require("dotenv").config(); const logger = require("../src/core/logger"); const { migrate: userMigrate } = require("./users"); +const { migrate: resourceMigrate } = require("./resources"); +const { migrate: roleMigrate } = require("./roles"); +const { migrate: permissionMigrate } = require("./permissions"); logger.info("Migration starting"); const isMongoDbUrl = JSON.parse( @@ -18,6 +21,9 @@ const migrate = async () => { await mongoose.connect(uri, options); logger.info("Connected to MongoDB"); await userMigrate(logger); + await resourceMigrate(logger); + await roleMigrate(logger); + await permissionMigrate(logger); logger.info(`Migration finished`); process.exit(0); }; diff --git a/server/setup/permissions.js b/server/setup/permissions.js index 3e5f930..20611f9 100644 --- a/server/setup/permissions.js +++ b/server/setup/permissions.js @@ -3,7 +3,12 @@ const parser = require("jsonc-parser"); const dataStr = fs.readFileSync("./setup/permissions.jsonc", "utf8"); -const { save, searchOne, update } = require("../src/core/repository"); +const { + save, + searchOne, + update, + updateAll, +} = require("../src/core/repository"); const { name: permissionModel } = require("../src/modules/permission/model"); const { name: resourceModel } = require("../src/modules/resource/model"); @@ -51,4 +56,22 @@ const seed = async (logger) => { logger.info(`Seeding users finished`); }; -module.exports = { seed }; +const migrate = async (logger) => { + logger.info(`Starting migration of permissions`); + const superadminUser = await searchOne({ username: "superadmin" }, "User"); + if (!superadminUser) { + throw new Error("Superadmin user not found"); + } + + await updateAll( + {}, + { + createdBy: superadminUser._id, + updatedBy: superadminUser._id, + }, + permissionModel + ); + logger.info(`Migration of permissions finished`); +}; + +module.exports = { seed, migrate }; diff --git a/server/setup/permissions.jsonc b/server/setup/permissions.jsonc index dfa0fa3..4a09c27 100644 --- a/server/setup/permissions.jsonc +++ b/server/setup/permissions.jsonc @@ -238,6 +238,120 @@ // Admin users api permissions + // admin roles api permissions + + // /api/roles/detail for Admin + { + "resourceName": "/api/roles/detail", + "resourceAlias": "Roles detail API", + "roleName": "admin", + "roleAlias": "Admin", + "isAllowed": true, + "isDisabled": false + }, + // /api/roles/create for Admin + { + "resourceName": "/api/roles/create", + "resourceAlias": "Roles create API", + "roleName": "admin", + "roleAlias": "Admin", + "isAllowed": true, + "isDisabled": false + }, + // /api/roles/update for Admin + { + "resourceName": "/api/roles/update", + "resourceAlias": "Roles update API", + "roleName": "admin", + "roleAlias": "Admin", + "isAllowed": true, + "isDisabled": false + }, + // /api/roles/search for Admin + { + "resourceName": "/api/roles/search", + "resourceAlias": "Roles search API", + "roleName": "admin", + "roleAlias": "Admin", + "isAllowed": true, + "isDisabled": false + }, + // /api/roles/count for Admin + { + "resourceName": "/api/roles/count", + "resourceAlias": "Roles count API", + "roleName": "admin", + "roleAlias": "Admin", + "isAllowed": true, + "isDisabled": false + }, + // /api/roles/delete for Admin + { + "resourceName": "/api/roles/delete", + "resourceAlias": "Roles delete API", + "roleName": "admin", + "roleAlias": "Admin", + "isAllowed": true, + "isDisabled": false + }, + + // admin permissions api permissions + + // /api/permissions/detail for Admin + { + "resourceName": "/api/permissions/detail", + "resourceAlias": "Permissions detail API", + "roleName": "admin", + "roleAlias": "Admin", + "isAllowed": true, + "isDisabled": false + }, + // /api/permissions/create for Admin + { + "resourceName": "/api/permissions/create", + "resourceAlias": "Permissions create API", + "roleName": "admin", + "roleAlias": "Admin", + "isAllowed": true, + "isDisabled": false + }, + // /api/permissions/update for admin + { + "resourceName": "/api/permissions/update", + "resourceAlias": "Permissions update API", + "roleName": "admin", + "roleAlias": "Admin", + "isAllowed": true, + "isDisabled": false + }, + // /api/permissions/search for admin + { + "resourceName": "/api/permissions/search", + "resourceAlias": "Permissions search API", + "roleName": "admin", + "roleAlias": "Admin", + "isAllowed": true, + "isDisabled": false + }, + // /api/permissions/count for admin + { + "resourceName": "/api/permissions/count", + "resourceAlias": "Permissions count API", + "roleName": "admin", + "roleAlias": "Admin", + "isAllowed": true, + "isDisabled": false + }, + // /api/permissions/delete for admin + { + "resourceName": "/api/permissions/delete", + "resourceAlias": "Permissions delete API", + "roleName": "admin", + "roleAlias": "Admin", + "isAllowed": true, + "isDisabled": false + }, + // /api/users/detail for admin { "resourceName": "/api/users/detail", @@ -301,7 +415,7 @@ "isAllowed": true, "isDisabled": false }, - // /api/roles/search for superadmin + // /api/roles/search for admin { "resourceName": "/api/roles/search", "resourceAlias": "Roles search API", @@ -460,6 +574,36 @@ "isDisabled": false }, + // admin permissions for roles pages + + // /roles permissions for admin + { + "resourceName": "/roles", + "resourceAlias": "Roles menu", + "roleName": "admin", + "roleAlias": "Admin", + "isAllowed": true, + "isDisabled": false + }, + // /roles/list permissions for admin + { + "resourceName": "/roles/list", + "resourceAlias": "Roles list", + "roleName": "admin", + "roleAlias": "Admin", + "isAllowed": true, + "isDisabled": false + }, + // /roles/new permission for admin + { + "resourceName": "/roles/new", + "resourceAlias": "Roles new", + "roleName": "admin", + "roleAlias": "Admin", + "isAllowed": true, + "isDisabled": false + }, + // superadmin permissions for resources pages // /resources permissions for superadmin @@ -520,6 +664,36 @@ "isDisabled": false }, + // admin permissions for permissions pages + + // /permissions permissions for admin + { + "resourceName": "/permissions", + "resourceAlias": "Permissions menu", + "roleName": "admin", + "roleAlias": "Admin", + "isAllowed": true, + "isDisabled": false + }, + // /permissions/list permissions for admin + { + "resourceName": "/permissions/list", + "resourceAlias": "Permissions list", + "roleName": "admin", + "roleAlias": "Admin", + "isAllowed": true, + "isDisabled": false + }, + // /permissions/new permission for admin + { + "resourceName": "/permissions/new", + "resourceAlias": "Permissions new", + "roleName": "admin", + "roleAlias": "Admin", + "isAllowed": true, + "isDisabled": false + }, + // user permissions for products pages // /products permissions for user diff --git a/server/setup/resources.js b/server/setup/resources.js index 960ebc2..1bfa638 100644 --- a/server/setup/resources.js +++ b/server/setup/resources.js @@ -2,25 +2,43 @@ const fs = require("fs"); const parser = require("jsonc-parser"); const dataStr = fs.readFileSync("./setup/resources.jsonc", "utf8"); -const { save, searchOne } = require("../src/core/repository"); -const { name: model } = require("../src/modules/resource/model"); +const { save, searchOne, updateAll } = require("../src/core/repository"); +const { name: modelName } = require("../src/modules/resource/model"); // const model = "Resource"; const seed = async (logger) => { const data = parser.parse(dataStr); await Promise.all( data.map(async (item) => { - logger.info(`Checking if ${model} ${item.name} exists`); - const itemExists = await searchOne({ name: item.name }, model); + logger.info(`Checking if ${modelName} ${item.name} exists`); + const itemExists = await searchOne({ name: item.name }, modelName); if (!itemExists) { - const savedItem = await save(item, model); + const savedItem = await save(item, modelName); logger.info(`Saved role id: ${savedItem._id}`); } else { - logger.info(`${model} ${item.name} already exists`); + logger.info(`${modelName} ${item.name} already exists`); } }) ); - logger.info(`${model} seeding finished`); + logger.info(`${modelName} seeding finished`); }; -module.exports = { seed }; +const migrate = async (logger) => { + logger.info(`${modelName} starting`); + const superadminUser = await searchOne({ username: "superadmin" }, "User"); + if (!superadminUser) { + throw new Error("Superadmin user not found"); + } + + await updateAll( + {}, + { + createdBy: superadminUser._id, + updatedBy: superadminUser._id, + }, + modelName + ); + logger.info(`${modelName} seeding finished`); +}; + +module.exports = { seed, migrate }; diff --git a/server/setup/roles.js b/server/setup/roles.js index f0343de..4075819 100644 --- a/server/setup/roles.js +++ b/server/setup/roles.js @@ -1,5 +1,5 @@ const data = require("./roles.json"); -const { save, searchOne } = require("../src/core/repository"); +const { save, searchOne, updateAll } = require("../src/core/repository"); const { modelName } = require("../src/modules/role/service"); const seed = async (logger) => { @@ -18,4 +18,22 @@ const seed = async (logger) => { logger.info(`Seeding ${modelName} finished`); }; -module.exports = { seed }; +const migrate = async (logger) => { + logger.info(`Starting migration of ${modelName}`); + const superadminUser = await searchOne({ name: "superadmin" }, modelName); + if (!superadminUser) { + throw new Error(`Superadmin user not found`); + } + + await updateAll( + {}, + { + createdBy: superadminUser._id, + updatedBy: superadminUser._id, + }, + modelName + ); + logger.info(`Migration of ${modelName} finished`); +}; + +module.exports = { seed, migrate }; diff --git a/server/src/modules/auth/index.js b/server/src/modules/auth/index.js index a313e77..d62bd2b 100644 --- a/server/src/modules/auth/index.js +++ b/server/src/modules/auth/index.js @@ -5,9 +5,22 @@ const { authorizeRequest, } = require("../../common/middlewares"); +const { name: ModelName } = require("./model"); + +const processRequest = async (req, res, next) => { + req.modelName = ModelName; + return next(); +}; + const init = async (app) => { app.use("/api/auth", authRoutes); - app.use("/api/users", authenticateRequest, authorizeRequest, userRoutes); + app.use( + "/api/users", + authenticateRequest, + authorizeRequest, + processRequest, + userRoutes + ); return app; }; diff --git a/server/src/modules/auth/model.js b/server/src/modules/auth/model.js index b96af94..49ffd22 100644 --- a/server/src/modules/auth/model.js +++ b/server/src/modules/auth/model.js @@ -51,7 +51,8 @@ userSchema.post("save", (error, doc, next) => { } }); -const User = mongoose.model("User", userSchema); +const ModelName = "User"; +const User = mongoose.model(ModelName, userSchema); async function getPasswordHash(password) { const hash = await bcrypt.hash(password, 10); @@ -75,4 +76,4 @@ User.setPassword = async (model, newPassword) => { return { passwordHash, ...model }; }; -module.exports = User; +module.exports = { Model: User, name: ModelName }; diff --git a/server/src/modules/auth/service.js b/server/src/modules/auth/service.js index 4400f0e..9b2a269 100644 --- a/server/src/modules/auth/service.js +++ b/server/src/modules/auth/service.js @@ -4,15 +4,13 @@ const { NotFound } = require("../../common/errors"); const { save, getById, - update, searchOne, dynamicSearch, updateAll, + update, } = require("../../core/repository"); -const Model = require("./model"); - -const ModelName = "User"; +const { Model, name: ModelName } = require("./model"); const changePassword = async (user, newPassword) => { const id = user._id; @@ -75,7 +73,7 @@ const tryCreateUser = async (user) => { return id; }; -const prepareQuery = (payload) => { +const getQuery = (payload) => { const createdBySubQuery = { $or: [ { createdBy: ObjectId(payload.userId) }, @@ -102,20 +100,6 @@ const prepareQuery = (payload) => { return query; }; -const search = async (payload) => { - const query = prepareQuery(payload); - const data = await Model.collection.find(query).skip(0).limit(20); - const items = { data: await data.toArray(), total: 200 }; - return items; -}; - -const count = async (payload) => { - const query = prepareQuery(payload); - const t = await Model.collection.find(query).count(); - const items = { total: t }; - return items; -}; - const searchPermissions = async (roleId) => { const permissions = await dynamicSearch( { @@ -127,18 +111,6 @@ const searchPermissions = async (roleId) => { return permissions; }; -// const searchClientPermissions = async (roleId) => { -// const clientResources = await dynamicSearch({ type: "client" }, "Resource"); -// const permissions = await dynamicSearch( -// { -// roleId: ObjectId(roleId), -// isAllowed: true, -// }, -// "Permission" -// ); -// return permissions; -// }; - const getPermittedUserById = async (id, userId) => { const user = await getById(id, ModelName); if (user) { @@ -154,16 +126,16 @@ const getPermittedUserById = async (id, userId) => { module.exports = { save, - update, getById: getPermittedUserById, searchOne, changePassword, checkUser, createUser, getByUsername, - search, - count, tryCreateUser, searchPermissions, updateAll, + update, + getQuery, + ModelName, }; diff --git a/server/src/modules/auth/user-controller.js b/server/src/modules/auth/user-controller.js index 5f00f36..f895fe3 100644 --- a/server/src/modules/auth/user-controller.js +++ b/server/src/modules/auth/user-controller.js @@ -1,52 +1,19 @@ /* eslint-disable no-undef */ const express = require("express"); +const { tryCreateUser, searchOne, getQuery, ModelName } = require("./service"); const { - update, - deleteById, - getById, - search, - count, - tryCreateUser, - searchOne, -} = require("./service"); + getByIdHandler, + updateHandler, + searchHandler: baseSearchHandler, + countHandler: baseCountHandler, + deleteHandler, +} = require("../../core/controller"); const { validateUserUpdate, validateUserCreate } = require("./request"); const { handleValidation } = require("../../common/middlewares"); -const { NotFound } = require("../../common/errors"); const router = express.Router(); -const ModelName = "User"; -const getHandler = async (req, res, next) => { - try { - const items = [ - { id: 1, name: "User 1" }, - { id: 2, name: "User 2" }, - ]; - const result = { - data: items, - total: items.length, - success: true, - }; - return res.status(200).send(result); - } catch (error) { - return next(error, req, res); - } -}; - -const getByIdHandler = async (req, res, next) => { - try { - const { id } = req.query; - const item = await getById(id, req.user.id); - if (item) { - return res.status(200).send(item); - } - throw new NotFound(`${ModelName} not found by the id: ${id}`); - } catch (error) { - return next(error, req, res); - } -}; - -const postHandler = async (req, res, next) => { +const saveHandler = async (req, res, next) => { try { const user = req.body; const id = await tryCreateUser(user); @@ -65,53 +32,15 @@ const postHandler = async (req, res, next) => { }; const searchHandler = async (req, res, next) => { - try { - if (!req.body.pageSize) { - req.body.pageSize = 10; - } - if (!req.body.current) { - req.body.current = 1; - } - const query = { ...req.body, userId: req.user.id }; - const result = await search(query); - const response = { success: true, ...result }; - return res.status(200).send(response); - } catch (error) { - return next(error, req, res); - } + const query = { ...req.body, userId: req.user.id }; + req.searchQuery = getQuery(query); + return baseSearchHandler(req, res, next); }; const countHandler = async (req, res, next) => { - try { - const query = { ...req.body, userId: req.user.id }; - const result = await count(query); - const response = { success: true, ...result }; - return res.status(200).send(response); - } catch (error) { - return next(error, req, res); - } -}; - -const putHandler = async (req, res, next) => { - try { - const { body } = req; - const id = await update(body, ModelName); - return res.status(200).send(id); - } catch (error) { - return next(error, req, res); - } -}; - -const deleteHandler = async (req, res, next) => { - try { - const { id } = req.query; - await deleteById(id, ModelName); - return res - .status(200) - .send({ success: true, message: `${ModelName} deleted` }); - } catch (error) { - return next(error, req, res); - } + const query = { ...req.body, userId: req.user.id }; + req.searchQuery = getQuery(query); + return baseCountHandler(req, res, next); }; const checkUserHandler = async (req, res) => { @@ -124,10 +53,9 @@ const checkUserHandler = async (req, res) => { return res.status(200).send({ status: "error", message: "User not found" }); }; -router.get("/", getHandler); router.get("/detail", getByIdHandler); -router.post("/create", handleValidation(validateUserCreate), postHandler); -router.put("/update", handleValidation(validateUserUpdate), putHandler); +router.post("/create", handleValidation(validateUserCreate), saveHandler); +router.put("/update", handleValidation(validateUserUpdate), updateHandler); router.post("/search", searchHandler); router.post("/count", countHandler); router.delete("/delete", deleteHandler); diff --git a/server/src/modules/customer/model.js b/server/src/modules/customer/model.js index 51fe0e9..9fb54b8 100644 --- a/server/src/modules/customer/model.js +++ b/server/src/modules/customer/model.js @@ -11,6 +11,16 @@ const customerSchema = new mongoose.Schema( state: { type: String, required: true }, postcode: { type: String, required: true }, country: { type: String, required: true }, + createdBy: { + type: mongoose.Schema.Types.ObjectId, + required: true, + default: "000000000000", + }, + updatedBy: { + type: mongoose.Schema.Types.ObjectId, + required: true, + default: "000000000000", + }, }, { timestamps: true } ); diff --git a/server/src/modules/permission/model.js b/server/src/modules/permission/model.js index dfd1f1f..30a8617 100644 --- a/server/src/modules/permission/model.js +++ b/server/src/modules/permission/model.js @@ -2,16 +2,29 @@ const mongoose = require("mongoose"); // Schema -const permissionSchema = new mongoose.Schema({ - roleId: { type: mongoose.Schema.Types.ObjectId, ref: "Role" }, - roleName: { type: String, required: true }, - roleAlias: { type: String, required: true }, - resourceId: { type: mongoose.Schema.Types.ObjectId, ref: "Resource" }, - resourceName: { type: String, required: true }, - resourceAlias: { type: String, required: true }, - isAllowed: { type: Boolean, required: true }, - isDisabled: { type: Boolean, required: true }, -}); +const permissionSchema = new mongoose.Schema( + { + roleId: { type: mongoose.Schema.Types.ObjectId, ref: "Role" }, + roleName: { type: String, required: true }, + roleAlias: { type: String, required: true }, + resourceId: { type: mongoose.Schema.Types.ObjectId, ref: "Resource" }, + resourceName: { type: String, required: true }, + resourceAlias: { type: String, required: true }, + isAllowed: { type: Boolean, required: true }, + isDisabled: { type: Boolean, required: true }, + createdBy: { + type: mongoose.Schema.Types.ObjectId, + required: true, + default: "000000000000", + }, + updatedBy: { + type: mongoose.Schema.Types.ObjectId, + required: true, + default: "000000000000", + }, + }, + { timestamps: true } +); // reference model const ModelName = "Permission"; diff --git a/server/src/modules/product/model.js b/server/src/modules/product/model.js index bb8a23e..9b10c8e 100644 --- a/server/src/modules/product/model.js +++ b/server/src/modules/product/model.js @@ -11,6 +11,16 @@ const schema = new mongoose.Schema( size: { type: Number, required: true }, manufacturingDate: { type: Date, required: true }, expiryDate: { type: Date, required: true }, + createdBy: { + type: mongoose.Schema.Types.ObjectId, + required: true, + default: "000000000000", + }, + updatedBy: { + type: mongoose.Schema.Types.ObjectId, + required: true, + default: "000000000000", + }, }, { timestamps: true } ); diff --git a/server/src/modules/resource/model.js b/server/src/modules/resource/model.js index ced0b23..524368a 100644 --- a/server/src/modules/resource/model.js +++ b/server/src/modules/resource/model.js @@ -6,6 +6,16 @@ const schema = new mongoose.Schema( name: { type: String, unique: true, required: true }, alias: { type: String, unique: true, required: true }, type: { type: String, required: true }, + createdBy: { + type: mongoose.Schema.Types.ObjectId, + required: true, + default: "000000000000", + }, + updatedBy: { + type: mongoose.Schema.Types.ObjectId, + required: true, + default: "000000000000", + }, }, { timestamps: true } ); diff --git a/server/src/modules/role/model.js b/server/src/modules/role/model.js index e162171..f371286 100644 --- a/server/src/modules/role/model.js +++ b/server/src/modules/role/model.js @@ -8,6 +8,16 @@ const schema = new mongoose.Schema( isAdmin: { type: Boolean }, alias: { type: String, unique: true, required: true }, permissions: [{ type: mongoose.Schema.Types.ObjectId, ref: "Permission" }], + createdBy: { + type: mongoose.Schema.Types.ObjectId, + required: true, + default: "000000000000", + }, + updatedBy: { + type: mongoose.Schema.Types.ObjectId, + required: true, + default: "000000000000", + }, }, { timestamps: true } );