data-primals-engine 1.6.2 → 1.6.5
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +56 -911
- package/client/package-lock.json +64 -50
- package/client/package.json +6 -0
- package/client/src/App.scss +29 -0
- package/client/src/Dashboard.jsx +1 -1
- package/client/src/WorkflowEditor.jsx +101 -0
- package/client/src/WorkflowEditor.scss +29 -4
- package/client/src/_variables.scss +3 -0
- package/doc/automation-workflows.md +102 -0
- package/doc/core-concepts.md +33 -0
- package/doc/custom-api-endpoints.md +40 -0
- package/doc/dashboards-kpis-charts.md +49 -0
- package/doc/data-management.md +120 -0
- package/doc/data-models.md +75 -0
- package/doc/index.md +14 -0
- package/doc/roles-permissions.md +43 -0
- package/doc/users.md +30 -0
- package/package.json +7 -5
- package/src/client.js +6 -4
- package/src/core.js +31 -12
- package/src/defaultModels +1628 -0
- package/src/defaultModels.js +14 -0
- package/src/filter.js +110 -42
- package/src/modules/data/data.history.js +5 -1
- package/src/modules/data/data.js +2 -1
- package/src/modules/data/data.operations.js +116 -72
- package/src/modules/data/data.relations.js +1 -1
- package/src/modules/mongodb.js +2 -1
- package/src/modules/swagger.js +25 -5
- package/src/modules/user.js +138 -76
- package/src/modules/workflow.js +187 -177
- package/src/providers.js +1 -1
- package/swagger-en.yml +2308 -472
- package/swagger-fr.yml +487 -3
- package/test/core.test.js +341 -0
- package/test/data.history.integration.test.js +140 -16
- package/test/data.integration.test.js +137 -2
- package/test/user.test.js +201 -280
- package/test/workflow.integration.test.js +8 -0
package/test/user.test.js
CHANGED
|
@@ -1,315 +1,236 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import {
|
|
3
|
-
import {
|
|
4
|
-
import {
|
|
5
|
-
import {
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
} from
|
|
9
|
-
|
|
10
|
-
let permRead, permWrite, permDelete, permManage;
|
|
11
|
-
let roleEditor;
|
|
12
|
-
let testUser, adminUser, roleViewer;
|
|
13
|
-
let currentTestUser;
|
|
14
|
-
let testModelsColInstance, testDatasColInstance;
|
|
15
|
-
// Cette fonction va remplacer la logique de votre beforeEach pour la création de contexte
|
|
16
|
-
async function setupTestContext() {
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
// Créer un utilisateur unique pour ce test
|
|
20
|
-
const username = generateUniqueName('testuserUserIntegration');
|
|
21
|
-
currentTestUser = {
|
|
22
|
-
username,
|
|
23
|
-
userPlan: 'free',
|
|
24
|
-
_model: 'user',
|
|
25
|
-
_user: username,
|
|
26
|
-
email: generateUniqueName('test') + '@example.com'
|
|
27
|
-
};
|
|
28
|
-
|
|
29
|
-
// Initialize collection instances after the engine is ready
|
|
30
|
-
testModelsColInstance = getAppModelsCollection;
|
|
31
|
-
testDatasColInstance = await getAppUserCollection(currentTestUser);
|
|
32
|
-
|
|
33
|
-
await testDatasColInstance.deleteMany({}); // Nettoyage de la base de test
|
|
34
|
-
// 1. Créer les permissions
|
|
35
|
-
const permissions = await testDatasColInstance.insertMany([
|
|
36
|
-
{ _model: 'permission', name: 'post:read', description: 'Lire les articles' },
|
|
37
|
-
{ _model: 'permission', name: 'post:write', description: 'Écrire des articles' },
|
|
38
|
-
{ _model: 'permission', name: 'post:delete', description: 'Supprimer des articles' },
|
|
39
|
-
{ _model: 'permission', name: 'user:manage', description: 'Gérer les utilisateurs' }
|
|
40
|
-
]);
|
|
41
|
-
permRead = permissions.insertedIds[0];
|
|
42
|
-
permWrite = permissions.insertedIds[1];
|
|
43
|
-
permDelete = permissions.insertedIds[2];
|
|
44
|
-
permManage = permissions.insertedIds[3];
|
|
45
|
-
|
|
46
|
-
// 2. Créer les rôles
|
|
47
|
-
const roles = await testDatasColInstance.insertMany([
|
|
48
|
-
{ _user: currentTestUser.username, _model: 'role', name: 'Viewer', permissions: [permRead.toString()] },
|
|
49
|
-
{ _user: currentTestUser.username, _model: 'role', name: 'Editor', permissions: [permRead.toString(), permWrite.toString()] }
|
|
50
|
-
]);
|
|
51
|
-
roleViewer = roles.insertedIds[0];
|
|
52
|
-
roleEditor = roles.insertedIds[1];
|
|
53
|
-
|
|
54
|
-
// 3. Créer les utilisateurs
|
|
55
|
-
const users = await testDatasColInstance.insertMany([
|
|
56
|
-
{ _user: currentTestUser.username, _model: 'user', roles: [roleEditor.toString()] },
|
|
57
|
-
{ _user: currentTestUser.username, _model: 'user', roles: [roleEditor.toString()] }
|
|
58
|
-
]);
|
|
59
|
-
testUser = {...currentTestUser, _id: users.insertedIds[0].toString(), roles: [roleEditor.toString()] };
|
|
60
|
-
adminUser = {...currentTestUser, _id: users.insertedIds[1].toString(), roles: [roleEditor.toString()] };
|
|
61
|
-
|
|
62
|
-
return testDatasColInstance;
|
|
63
|
-
};
|
|
1
|
+
import { vi, describe, it, expect, beforeAll, afterAll, beforeEach, afterEach } from 'vitest';
|
|
2
|
+
import { ObjectId } from 'mongodb';
|
|
3
|
+
import { hasPermission, onInit } from '../src/modules/user.js';
|
|
4
|
+
import { Config } from '../src/config.js';
|
|
5
|
+
import { initEngine, generateUniqueName } from '../src/setenv.js';
|
|
6
|
+
import { getCollection, getCollectionForUser } from '../src/modules/mongodb.js';
|
|
7
|
+
import { Logger } from '../src/gameObject.js';
|
|
8
|
+
import { insertData, deleteData, deleteModels, createModel } from '../src/index.js';
|
|
9
|
+
|
|
64
10
|
let engine;
|
|
65
|
-
|
|
11
|
+
let logger;
|
|
12
|
+
|
|
13
|
+
describe('User Permission System with Filters', () => {
|
|
14
|
+
let collection;
|
|
15
|
+
let testUser, testUserWithRole, systemUser;
|
|
16
|
+
let permNoFilter, permWithSimpleFilter, permWithUserFilter, permWithReqFilter, permOrderEditForManager;
|
|
17
|
+
let roleWithPerms;
|
|
18
|
+
|
|
19
|
+
// Le parent qui possède les définitions de rôles et permissions
|
|
20
|
+
const parentUser = { username: generateUniqueName('perm_parent_user') };
|
|
66
21
|
|
|
67
|
-
// --- SETUP : Création des données de test avant tous les tests ---
|
|
68
22
|
beforeAll(async () => {
|
|
69
|
-
Config.Set("modules", ["mongodb", "data", "file", "bucket", "workflow","user", "assistant"
|
|
23
|
+
Config.Set("modules", ["mongodb", "data", "file", "bucket", "workflow", "user", "assistant"]);
|
|
70
24
|
engine = await initEngine();
|
|
71
|
-
|
|
25
|
+
logger = engine.getComponent(Logger);
|
|
26
|
+
await onInit(engine);
|
|
27
|
+
// La collection est celle du parent, car c'est lui qui stocke les définitions
|
|
28
|
+
collection = await getCollectionForUser(parentUser);
|
|
72
29
|
});
|
|
73
30
|
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
});
|
|
31
|
+
beforeEach(async () => {
|
|
32
|
+
// Utiliser un utilisateur unique pour l'isolation des tests
|
|
33
|
+
const username = generateUniqueName('perm_test_user');
|
|
34
|
+
// L'utilisateur de test est un "enfant" de parentUser
|
|
35
|
+
testUser = { _id: new ObjectId(), username: username, _user: parentUser.username, roles: [], _model: 'user' };
|
|
36
|
+
testUserWithRole = { _id: new ObjectId(), username: username, _user: parentUser.username, roles: [], _model: 'user' };
|
|
37
|
+
systemUser = { roles: ['admin', 'product.view'] };
|
|
38
|
+
|
|
39
|
+
await collection.deleteMany({ _user: parentUser.username });
|
|
40
|
+
|
|
41
|
+
// --- Création des données de test ---
|
|
42
|
+
// Créer les modèles nécessaires pour les permissions et les rôles
|
|
43
|
+
await createModel({ name: 'permission', description: '', _user: parentUser.username, fields: [{ name: 'name', type: 'string' }, { name: 'filter', type: 'code' }] });
|
|
44
|
+
await createModel({ name: 'role', description: '', _user: parentUser.username, fields: [{ name: 'name', type: 'string' }, { name: 'permissions', type: 'relation', relation: 'permission', multiple: true }] });
|
|
45
|
+
await createModel({ name: 'userPermission', description: '', _user: parentUser.username, fields: [{ name: 'user', type: 'relation', relation: 'user' }, { name: 'permission', type: 'relation', relation: 'permission' }, { name: 'isGranted', type: 'boolean' }, { name: 'filter', type: 'code',language:'json' }] });
|
|
46
|
+
|
|
47
|
+
const permResult = await collection.insertMany([
|
|
48
|
+
{ _model: 'permission', name: 'product.view', _user: parentUser.username },
|
|
49
|
+
{ _model: 'permission', name: 'order.edit', filter: { status: 'pending' }, _user: parentUser.username },
|
|
50
|
+
{ _model: 'permission', name: 'document.edit', filter: { createdBy: '{user._id}' }, _user: parentUser.username },
|
|
51
|
+
{ _model: 'permission', name: 'login.attempt', filter: { "ip": "{req.ip}" }, _user: parentUser.username }
|
|
52
|
+
]);
|
|
53
|
+
permNoFilter = permResult.insertedIds[0];
|
|
54
|
+
permWithSimpleFilter = permResult.insertedIds[1];
|
|
55
|
+
permWithUserFilter = permResult.insertedIds[2];
|
|
56
|
+
permWithReqFilter = permResult.insertedIds[3];
|
|
57
|
+
|
|
58
|
+
const roleResult = await collection.insertOne({
|
|
59
|
+
_model: 'role',
|
|
60
|
+
name: 'Editor',
|
|
61
|
+
permissions: [permNoFilter.toString(), permWithSimpleFilter.toString()],
|
|
62
|
+
_user: parentUser.username
|
|
63
|
+
});
|
|
64
|
+
roleWithPerms = roleResult.insertedId;
|
|
65
|
+
|
|
66
|
+
// Assigner le rôle à l'utilisateur de test
|
|
67
|
+
testUserWithRole.roles = [roleWithPerms.toString()];
|
|
68
|
+
});
|
|
87
69
|
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
70
|
+
afterEach(async () => {
|
|
71
|
+
// Nettoyer les données créées
|
|
72
|
+
if (collection) {
|
|
73
|
+
await collection.deleteMany({ _user: parentUser.username });
|
|
74
|
+
}
|
|
75
|
+
await deleteModels({ _user: parentUser.username });
|
|
76
|
+
});
|
|
93
77
|
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
isGranted: true,
|
|
99
|
-
expiresAt: futureDate
|
|
100
|
-
});
|
|
78
|
+
it('should return true for a permission without filter granted by a role', async () => {
|
|
79
|
+
const result = await hasPermission('product.view', testUserWithRole);
|
|
80
|
+
expect(result).toBe(true);
|
|
81
|
+
});
|
|
101
82
|
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
83
|
+
it('should return a simple filter object for a permission with filter granted by a role', async () => {
|
|
84
|
+
const result = await hasPermission('order.edit', testUserWithRole);
|
|
85
|
+
expect(result).toEqual({ status: 'pending' });
|
|
86
|
+
});
|
|
105
87
|
|
|
106
|
-
|
|
107
|
-
|
|
88
|
+
it('should return false if user does not have the permission', async () => {
|
|
89
|
+
const result = await hasPermission('document.edit', testUserWithRole);
|
|
90
|
+
expect(result).toBe(false);
|
|
91
|
+
});
|
|
108
92
|
|
|
109
|
-
|
|
93
|
+
it('should grant a permission with a filter via userPermission exception', async () => {
|
|
94
|
+
await collection.insertOne({
|
|
95
|
+
_model: 'userPermission',
|
|
96
|
+
user: testUser._id.toString(),
|
|
97
|
+
permission: permWithUserFilter.toString(),
|
|
98
|
+
isGranted: true,
|
|
99
|
+
_user: parentUser.username
|
|
110
100
|
});
|
|
111
101
|
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
const pastDate = new Date();
|
|
116
|
-
pastDate.setDate(pastDate.getDate() - 1);
|
|
117
|
-
|
|
118
|
-
await coll.insertOne({
|
|
119
|
-
_model: 'userPermission',
|
|
120
|
-
user: testUser._id,
|
|
121
|
-
permission: permDelete,
|
|
122
|
-
isGranted: true,
|
|
123
|
-
expiresAt: pastDate
|
|
124
|
-
});
|
|
125
|
-
|
|
126
|
-
const permissions = await getUserActivePermissions(testUser);
|
|
127
|
-
expect(permissions.size).to.equal(2); // Revient la normale
|
|
128
|
-
expect(permissions.has('post:delete')).to.be.false;
|
|
129
|
-
|
|
130
|
-
await coll.deleteOne({ _model: 'userPermission', user: testUser._id });
|
|
131
|
-
await coll.drop();
|
|
132
|
-
});
|
|
102
|
+
const result = await hasPermission('document.edit', testUser);
|
|
103
|
+
expect(result).toEqual({ createdBy: testUser._id.toString() });
|
|
104
|
+
});
|
|
133
105
|
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
_model: 'userPermission',
|
|
142
|
-
user: testUser._id,
|
|
143
|
-
permission: permWrite,
|
|
144
|
-
isGranted: false, // Révocation
|
|
145
|
-
expiresAt: futureDate
|
|
146
|
-
});
|
|
147
|
-
|
|
148
|
-
const permissions = await getUserActivePermissions(testUser);
|
|
149
|
-
expect(permissions.size).to.equal(1);
|
|
150
|
-
expect(permissions.has('post:read')).to.be.true;
|
|
151
|
-
expect(permissions.has('post:write')).to.be.false; // La permission a été retirée
|
|
152
|
-
|
|
153
|
-
await coll.deleteOne({ _model: 'userPermission', user: testUser._id });
|
|
154
|
-
await coll.drop();
|
|
106
|
+
it('should substitute user._id in the filter', async () => {
|
|
107
|
+
await collection.insertOne({
|
|
108
|
+
_model: 'userPermission',
|
|
109
|
+
user: testUserWithRole._id.toString(),
|
|
110
|
+
permission: permWithUserFilter.toString(),
|
|
111
|
+
isGranted: true,
|
|
112
|
+
_user: parentUser.username
|
|
155
113
|
});
|
|
156
114
|
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
// La révocation a expiré, l'utilisateur devrait retrouver son droit d'écriture
|
|
160
|
-
const pastDate = new Date();
|
|
161
|
-
pastDate.setDate(pastDate.getDate() - 1);
|
|
162
|
-
|
|
163
|
-
await coll.insertOne({
|
|
164
|
-
_model: 'userPermission',
|
|
165
|
-
user: testUser._id,
|
|
166
|
-
permission: permWrite,
|
|
167
|
-
isGranted: false,
|
|
168
|
-
expiresAt: pastDate
|
|
169
|
-
});
|
|
170
|
-
|
|
171
|
-
const permissions = await getUserActivePermissions(testUser);
|
|
172
|
-
expect(permissions.size).to.equal(2);
|
|
173
|
-
expect(permissions.has('post:write')).to.be.true; // La permission est de retour
|
|
174
|
-
|
|
175
|
-
await coll.deleteOne({ _model: 'userPermission', user: testUser._id });
|
|
176
|
-
await coll.drop();
|
|
177
|
-
});
|
|
115
|
+
const result = await hasPermission('document.edit', testUserWithRole);
|
|
116
|
+
expect(result).toEqual({ createdBy: testUserWithRole._id.toString() });
|
|
178
117
|
});
|
|
179
118
|
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
119
|
+
it('should substitute request context variables like req.ip', async () => {
|
|
120
|
+
await collection.insertOne({
|
|
121
|
+
_model: 'userPermission',
|
|
122
|
+
user: testUser._id.toString(),
|
|
123
|
+
permission: permWithReqFilter.toString(),
|
|
124
|
+
isGranted: true,
|
|
125
|
+
_user: parentUser.username
|
|
187
126
|
});
|
|
188
127
|
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
}
|
|
128
|
+
const mockReq = {
|
|
129
|
+
ip: '192.168.1.100',
|
|
130
|
+
query: { from: 'test' },
|
|
131
|
+
body: { name: 'test-body' },
|
|
132
|
+
params: { id: '123' }
|
|
133
|
+
};
|
|
195
134
|
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
expect(result).to.be.true;
|
|
200
|
-
await coll.drop();
|
|
201
|
-
});
|
|
135
|
+
const result = await hasPermission('login.attempt', testUser, null, mockReq);
|
|
136
|
+
expect(result).toEqual({ ip: '192.168.1.100' });
|
|
137
|
+
});
|
|
202
138
|
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
139
|
+
it('should revoke a permission from a role via userPermission exception', async () => {
|
|
140
|
+
let result = await hasPermission('order.edit', testUserWithRole);
|
|
141
|
+
expect(result).toEqual({ status: 'pending' });
|
|
142
|
+
|
|
143
|
+
// Utiliser insertData pour déclencher l'invalidation du cache
|
|
144
|
+
await insertData('userPermission', {
|
|
145
|
+
user: testUserWithRole._id.toString(),
|
|
146
|
+
permission: permWithSimpleFilter.toString(),
|
|
147
|
+
isGranted: false,
|
|
148
|
+
}, {}, parentUser, true, true, false // le dernier false pour bypasser la vérification de permission pour le test
|
|
149
|
+
);
|
|
150
|
+
|
|
151
|
+
result = await hasPermission('order.edit', testUserWithRole);
|
|
152
|
+
expect(result).toBe(false);
|
|
209
153
|
});
|
|
210
154
|
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
// --- Setup ---
|
|
218
|
-
coll = await setupTestContext();
|
|
219
|
-
user = currentTestUser; // Récupère l'utilisateur du contexte
|
|
220
|
-
|
|
221
|
-
// 1. Créer les permissions
|
|
222
|
-
const perms = await coll.insertMany([
|
|
223
|
-
{ _model: 'permission', name: 'env:read', _user: user.username },
|
|
224
|
-
{ _model: 'permission', name: 'env:write', _user: user.username }
|
|
225
|
-
]);
|
|
226
|
-
permRead = perms.insertedIds[0];
|
|
227
|
-
permWrite = perms.insertedIds[1];
|
|
228
|
-
|
|
229
|
-
// 2. Créer les environnements
|
|
230
|
-
const envs = await coll.insertMany([
|
|
231
|
-
{ _model: 'env', name: 'prod', _user: user.username },
|
|
232
|
-
{ _model: 'env', name: 'staging', _user: user.username }
|
|
233
|
-
]);
|
|
234
|
-
envProdId = envs.insertedIds[0].toString();
|
|
235
|
-
envStagingId = envs.insertedIds[1].toString();
|
|
236
|
-
|
|
237
|
-
// 3. Créer un rôle de base et l'assigner à l'utilisateur
|
|
238
|
-
const role = await coll.insertOne({
|
|
239
|
-
_model: 'role',
|
|
240
|
-
name: 'EnvTester',
|
|
241
|
-
permissions: [permRead.toString()],
|
|
242
|
-
_user: user.username
|
|
243
|
-
});
|
|
244
|
-
await coll.updateOne(
|
|
245
|
-
{ _id: user._id },
|
|
246
|
-
{ $set: { roles: [role.insertedId.toString()] } }
|
|
247
|
-
);
|
|
248
|
-
// Mettre à jour l'objet utilisateur en mémoire
|
|
249
|
-
user.roles = [role.insertedId.toString()];
|
|
250
|
-
user.permissions = ['env:read'];
|
|
155
|
+
it('should override a role filter with a userPermission filter', async () => {
|
|
156
|
+
const permResult = await collection.insertOne({
|
|
157
|
+
_model: 'permission',
|
|
158
|
+
name: 'order.edit',
|
|
159
|
+
filter: { manager_id: '{user._id}' },
|
|
160
|
+
_user: parentUser.username
|
|
251
161
|
});
|
|
162
|
+
permOrderEditForManager = permResult.insertedId;
|
|
252
163
|
|
|
253
|
-
|
|
254
|
-
|
|
164
|
+
await collection.insertOne({
|
|
165
|
+
_model: 'userPermission',
|
|
166
|
+
user: testUserWithRole._id.toString(),
|
|
167
|
+
permission: permOrderEditForManager.toString(),
|
|
168
|
+
isGranted: true,
|
|
169
|
+
_user: parentUser.username
|
|
255
170
|
});
|
|
256
171
|
|
|
257
|
-
|
|
258
|
-
// Nettoyer les exceptions de permission après chaque test
|
|
259
|
-
await coll.deleteMany({ _model: 'userPermission', user: user._id });
|
|
260
|
-
});
|
|
172
|
+
const result = await hasPermission('order.edit', testUserWithRole);
|
|
261
173
|
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
_model: 'userPermission', user: user._id, permission: permWrite, isGranted: true
|
|
265
|
-
});
|
|
266
|
-
// L'utilisateur doit avoir la permission dans n'importe quel environnement
|
|
267
|
-
expect(await hasPermission(['env:write'], user, envProdId)).toBe(true);
|
|
268
|
-
expect(await hasPermission(['env:write'], user, envStagingId)).toBe(true);
|
|
269
|
-
expect(await hasPermission(['env:write'], user, null)).toBe(true); // Environnement par défaut
|
|
270
|
-
});
|
|
174
|
+
expect(result).toEqual({ manager_id: testUserWithRole._id.toString() });
|
|
175
|
+
});
|
|
271
176
|
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
177
|
+
it('should use the filter from userPermission when overriding a permission', async () => {
|
|
178
|
+
// Le rôle donne 'order.edit' avec le filtre { status: 'pending' }
|
|
179
|
+
let result = await hasPermission('order.edit', testUserWithRole);
|
|
180
|
+
expect(result).toEqual({ status: 'pending' });
|
|
181
|
+
|
|
182
|
+
// On ajoute une exception qui accorde la même permission mais avec un filtre différent
|
|
183
|
+
// Utiliser insertData pour s'assurer que le cache est invalidé
|
|
184
|
+
await insertData('userPermission', {
|
|
185
|
+
user: testUserWithRole._id.toString(),
|
|
186
|
+
permission: permWithSimpleFilter.toString(), // La permission 'order.edit'
|
|
187
|
+
isGranted: true,
|
|
188
|
+
filter: { "assignedTo": "{user._id}" }, // Le nouveau filtre
|
|
189
|
+
}, {}, parentUser, true, true, false);
|
|
190
|
+
|
|
191
|
+
// La fonction doit maintenant retourner le filtre de l'exception
|
|
192
|
+
result = await hasPermission('order.edit', testUserWithRole);
|
|
193
|
+
expect(result).toEqual({ assignedTo: testUserWithRole._id.toString() });
|
|
194
|
+
});
|
|
281
195
|
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
196
|
+
it('should fallback to base permission filter if userPermission has no filter', async () => {
|
|
197
|
+
// On accorde 'document.edit' (qui a un filtre { createdBy: '{user._id}' })
|
|
198
|
+
// via une exception qui n'a PAS de filtre personnalisé.
|
|
199
|
+
await collection.insertOne({
|
|
200
|
+
_model: 'userPermission',
|
|
201
|
+
user: testUser._id.toString(),
|
|
202
|
+
permission: permWithUserFilter.toString(), // La permission 'document.edit'
|
|
203
|
+
isGranted: true,
|
|
204
|
+
// Pas de champ 'filter' ici
|
|
205
|
+
_user: parentUser.username
|
|
206
|
+
});
|
|
207
|
+
|
|
208
|
+
// La fonction doit retourner le filtre de la permission de base.
|
|
209
|
+
const result = await hasPermission('document.edit', testUser);
|
|
210
|
+
expect(result).toEqual({ createdBy: testUser._id.toString() });
|
|
211
|
+
});
|
|
291
212
|
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
});
|
|
296
|
-
// La permission de lecture ne doit être révoquée qu'en 'staging'
|
|
297
|
-
expect(await hasPermission(['env:read'], user, envProdId)).toBe(true);
|
|
298
|
-
expect(await hasPermission(['env:read'], user, envStagingId)).toBe(false);
|
|
299
|
-
expect(await hasPermission(['env:read'], user, null)).toBe(true);
|
|
300
|
-
});
|
|
213
|
+
it('should handle non-local users correctly', async () => {
|
|
214
|
+
let result = await hasPermission('product.view', systemUser);
|
|
215
|
+
expect(result).toBe(true);
|
|
301
216
|
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
|
|
217
|
+
result = await hasPermission('order.edit', systemUser);
|
|
218
|
+
expect(result).toBe(false);
|
|
219
|
+
});
|
|
220
|
+
|
|
221
|
+
it('should return false and log an error if an exception occurs', async ({skip}) => {
|
|
222
|
+
// Pour simuler une erreur, on peut essayer de requêter avec un utilisateur invalide
|
|
223
|
+
// ou mocker une fonction interne pour qu'elle échoue.
|
|
224
|
+
const getCollectionForUserSpy = vi.spyOn(await import('../src/modules/mongodb.js'), 'getCollectionForUser');
|
|
225
|
+
getCollectionForUserSpy.mockRejectedValueOnce(new Error("DB connection failed"));
|
|
226
|
+
const errorSpy = vi.spyOn(logger, 'error');
|
|
227
|
+
|
|
228
|
+
const result = await hasPermission('product.view', testUserWithRole);
|
|
229
|
+
|
|
230
|
+
expect(result).toBe(false);
|
|
231
|
+
expect(errorSpy).toHaveBeenCalledWith("Erreur lors de la vérification des permissions :", expect.any(Error));
|
|
232
|
+
|
|
233
|
+
getCollectionForUserSpy.mockRestore();
|
|
234
|
+
errorSpy.mockRestore();
|
|
314
235
|
});
|
|
315
236
|
});
|
|
@@ -207,6 +207,14 @@ describe('Intégration des Workflows - triggerWorkflows', () => {
|
|
|
207
207
|
expect(workflowRun.contextData.triggerData._id.toString()).toBe(insertResult.insertedIds[0].toString());
|
|
208
208
|
expect(workflowRun.contextData.triggerData.projectName).toBe('New Corp Website');
|
|
209
209
|
|
|
210
|
+
// --- Vérification du champ 'history' ---
|
|
211
|
+
expect(workflowRun.history).toBeDefined();
|
|
212
|
+
expect(Array.isArray(workflowRun.history)).toBe(true);
|
|
213
|
+
expect(workflowRun.history.length).toBeGreaterThan(0);
|
|
214
|
+
const firstStepHistory = workflowRun.history[0];
|
|
215
|
+
expect(firstStepHistory.stepId).toBe(testStep._id.toString());
|
|
216
|
+
expect(firstStepHistory.status).toBe('success');
|
|
217
|
+
|
|
210
218
|
});
|
|
211
219
|
|
|
212
220
|
it('ne devrait PAS créer de workflowRun si le trigger est inactif', async () => {
|