Add permission settings model and related functionality
All checks were successful
farmcontrol/farmcontrol-ws/pipeline/head This commit looks good
All checks were successful
farmcontrol/farmcontrol-ws/pipeline/head This commit looks good
- Introduced a new permission settings schema and model to manage user permissions effectively. - Updated user schema to include a reference to permission settings, allowing for dynamic permission management. - Enhanced the editObject function to conditionally recalculate permissions based on the new recalculate parameter. - Added utility functions for applying and retrieving permission settings, improving permission handling across the application.
This commit is contained in:
parent
7e8879164a
commit
3742839737
@ -425,7 +425,8 @@ export const editObject = async ({
|
||||
ownerType = undefined,
|
||||
populate = [],
|
||||
auditLog = true,
|
||||
notify = true
|
||||
notify = true,
|
||||
recalculate = true
|
||||
}) => {
|
||||
try {
|
||||
// Determine parentType from model name
|
||||
@ -494,7 +495,7 @@ export const editObject = async ({
|
||||
populate
|
||||
});
|
||||
|
||||
if (model.recalculate) {
|
||||
if (model.recalculate && recalculate == true) {
|
||||
logger.debug(`Recalculating ${model.modelName}`);
|
||||
await model.recalculate(newExpandedObject, owner, ownerType);
|
||||
}
|
||||
|
||||
38
src/database/permissions.js
Normal file
38
src/database/permissions.js
Normal file
@ -0,0 +1,38 @@
|
||||
export const applyPermissionSettingsList = (settingsList = []) => {
|
||||
const permissions = {};
|
||||
|
||||
for (const setting of settingsList) {
|
||||
const matrix = setting?.permissions;
|
||||
if (!matrix || typeof matrix !== 'object' || Array.isArray(matrix)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
for (const [modelName, actions] of Object.entries(matrix)) {
|
||||
if (!actions || typeof actions !== 'object' || Array.isArray(actions)) {
|
||||
continue;
|
||||
}
|
||||
if (!permissions[modelName]) {
|
||||
permissions[modelName] = {};
|
||||
}
|
||||
for (const [actionName, value] of Object.entries(actions)) {
|
||||
if (value === true || value === false) {
|
||||
permissions[modelName][actionName] = value;
|
||||
}
|
||||
}
|
||||
if (Object.keys(permissions[modelName]).length === 0) {
|
||||
delete permissions[modelName];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return permissions;
|
||||
};
|
||||
|
||||
export const getPermissionSettingsId = (value) => {
|
||||
if (!value) return null;
|
||||
if (typeof value === 'string') return value;
|
||||
if (value._id) {
|
||||
return value._id._id || value._id;
|
||||
}
|
||||
return value;
|
||||
};
|
||||
59
src/database/schemas/management/permissionsettings.schema.js
Normal file
59
src/database/schemas/management/permissionsettings.schema.js
Normal file
@ -0,0 +1,59 @@
|
||||
import mongoose from 'mongoose';
|
||||
import { generateId } from '../../utils.js';
|
||||
import { getPermissionSettingsId } from '../../permissions.js';
|
||||
|
||||
const { Schema } = mongoose;
|
||||
|
||||
const permissionSettingsSchema = new Schema(
|
||||
{
|
||||
_reference: { type: String, default: () => generateId()() },
|
||||
name: { type: String, required: true },
|
||||
permissions: { type: Schema.Types.Mixed, required: false, default: () => ({}) },
|
||||
},
|
||||
{ timestamps: true }
|
||||
);
|
||||
|
||||
permissionSettingsSchema.index({ name: 'text' });
|
||||
|
||||
permissionSettingsSchema.virtual('id').get(function () {
|
||||
return this._id;
|
||||
});
|
||||
|
||||
permissionSettingsSchema.set('toJSON', { virtuals: true });
|
||||
|
||||
permissionSettingsSchema.statics.recalculate = async function (permissionSettings, user) {
|
||||
const permissionSettingsId = getPermissionSettingsId(permissionSettings);
|
||||
if (!permissionSettingsId) {
|
||||
return;
|
||||
}
|
||||
|
||||
const userModel = mongoose.model('user');
|
||||
const users = await userModel
|
||||
.find({ permissionSettings: permissionSettingsId })
|
||||
.lean();
|
||||
|
||||
const stillExists = await this.exists({ _id: permissionSettingsId });
|
||||
if (!stillExists) {
|
||||
await userModel.updateMany(
|
||||
{ permissionSettings: permissionSettingsId },
|
||||
{ $pull: { permissionSettings: permissionSettingsId } }
|
||||
);
|
||||
}
|
||||
|
||||
for (const assignedUser of users) {
|
||||
const nextUser = stillExists
|
||||
? assignedUser
|
||||
: {
|
||||
...assignedUser,
|
||||
permissionSettings: (assignedUser.permissionSettings || []).filter(
|
||||
(item) => String(getPermissionSettingsId(item)) !== String(permissionSettingsId)
|
||||
),
|
||||
};
|
||||
await userModel.recalculate(nextUser, user);
|
||||
}
|
||||
};
|
||||
|
||||
export const permissionSettingsModel = mongoose.model(
|
||||
'permissionSettings',
|
||||
permissionSettingsSchema
|
||||
);
|
||||
@ -1,5 +1,8 @@
|
||||
import mongoose from 'mongoose';
|
||||
import { generateId } from '../../utils.js';
|
||||
import { applyPermissionSettingsList, getPermissionSettingsId } from '../../permissions.js';
|
||||
|
||||
const { Schema } = mongoose;
|
||||
|
||||
const userSchema = new mongoose.Schema(
|
||||
{
|
||||
@ -11,6 +14,10 @@ const userSchema = new mongoose.Schema(
|
||||
email: { required: true, type: String },
|
||||
profileImage: { type: mongoose.SchemaTypes.ObjectId, ref: 'file', required: false },
|
||||
appPasswordHash: { type: String, required: false, select: false },
|
||||
permissionSettings: [
|
||||
{ type: Schema.Types.ObjectId, ref: 'permissionSettings', required: false },
|
||||
],
|
||||
permissions: { type: Schema.Types.Mixed, required: false, default: () => ({}) },
|
||||
},
|
||||
{ timestamps: true }
|
||||
);
|
||||
@ -23,4 +30,40 @@ userSchema.virtual('id').get(function () {
|
||||
|
||||
userSchema.set('toJSON', { virtuals: true });
|
||||
|
||||
const resolvePermissionSettings = async (user) => {
|
||||
const permissionSettingsModel = mongoose.model('permissionSettings');
|
||||
const items = user?.permissionSettings || [];
|
||||
const ids = items.map(getPermissionSettingsId).filter(Boolean);
|
||||
if (ids.length === 0) {
|
||||
return [];
|
||||
}
|
||||
|
||||
const docs = await permissionSettingsModel
|
||||
.find({ _id: { $in: ids } })
|
||||
.lean();
|
||||
const docsById = new Map(docs.map((doc) => [String(doc._id), doc]));
|
||||
|
||||
return ids.map((id) => docsById.get(String(id))).filter(Boolean);
|
||||
};
|
||||
|
||||
userSchema.statics.recalculate = async function (user, actingUser) {
|
||||
const userId = user?._id || user;
|
||||
if (!userId) {
|
||||
return;
|
||||
}
|
||||
|
||||
const settings = await resolvePermissionSettings(user);
|
||||
const permissions = applyPermissionSettingsList(settings);
|
||||
const { editObject } = await import('../../database.js');
|
||||
|
||||
await editObject({
|
||||
model: this,
|
||||
id: userId,
|
||||
updateData: { permissions },
|
||||
user: actingUser,
|
||||
populate: ['profileImage', 'permissionSettings'],
|
||||
recalculate: false,
|
||||
});
|
||||
};
|
||||
|
||||
export const userModel = mongoose.model('user', userSchema);
|
||||
|
||||
@ -24,6 +24,7 @@ import { stockLocationModel } from './inventory/stocklocation.schema.js';
|
||||
import { stockTransferModel } from './inventory/stocktransfer.schema.js';
|
||||
import { auditLogModel } from './management/auditlog.schema.js';
|
||||
import { userModel } from './management/user.schema.js';
|
||||
import { permissionSettingsModel } from './management/permissionsettings.schema.js';
|
||||
import { appPasswordModel } from './management/apppassword.schema.js';
|
||||
import { noteTypeModel } from './management/notetype.schema.js';
|
||||
import { noteModel } from './misc/note.schema.js';
|
||||
@ -212,6 +213,13 @@ export const models = {
|
||||
referenceField: '_reference',
|
||||
label: 'User',
|
||||
},
|
||||
PMS: {
|
||||
model: permissionSettingsModel,
|
||||
idField: '_id',
|
||||
type: 'permissionSettings',
|
||||
referenceField: '_reference',
|
||||
label: 'Permission Settings',
|
||||
},
|
||||
APP: {
|
||||
model: appPasswordModel,
|
||||
idField: '_id',
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user