@flusys/nestjs-shared 3.0.0-rc → 3.0.1
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 +851 -275
- package/cjs/classes/api-controller.class.js +4 -4
- package/cjs/constants/permissions.js +34 -11
- package/cjs/decorators/require-permission.decorator.js +7 -3
- package/cjs/enums/index.js +20 -0
- package/cjs/enums/notification-type.enum.js +17 -0
- package/cjs/enums/participant-status.enum.js +17 -0
- package/cjs/enums/recurrence-type.enum.js +18 -0
- package/cjs/exceptions/permission.exception.js +2 -2
- package/cjs/guards/permission.guard.js +66 -65
- package/cjs/index.js +1 -0
- package/cjs/interfaces/event-manager-adapter.interface.js +11 -0
- package/cjs/interfaces/index.js +2 -0
- package/cjs/interfaces/notification-adapter.interface.js +11 -0
- package/cjs/interfaces/permission.interface.js +1 -1
- package/cjs/utils/html-sanitizer.util.js +9 -9
- package/cjs/utils/request.util.js +2 -1
- package/classes/api-controller.class.d.ts +3 -3
- package/constants/permissions.d.ts +36 -0
- package/decorators/require-permission.decorator.d.ts +3 -2
- package/enums/index.d.ts +3 -0
- package/enums/notification-type.enum.d.ts +6 -0
- package/enums/participant-status.enum.d.ts +6 -0
- package/enums/recurrence-type.enum.d.ts +7 -0
- package/exceptions/permission.exception.d.ts +1 -1
- package/fesm/classes/api-controller.class.js +5 -5
- package/fesm/constants/permissions.js +28 -14
- package/fesm/decorators/require-permission.decorator.js +18 -65
- package/fesm/enums/index.js +3 -0
- package/fesm/enums/notification-type.enum.js +7 -0
- package/fesm/enums/participant-status.enum.js +7 -0
- package/fesm/enums/recurrence-type.enum.js +8 -0
- package/fesm/exceptions/permission.exception.js +2 -2
- package/fesm/guards/permission.guard.js +66 -65
- package/fesm/index.js +1 -0
- package/fesm/interfaces/event-manager-adapter.interface.js +1 -0
- package/fesm/interfaces/index.js +2 -0
- package/fesm/interfaces/notification-adapter.interface.js +1 -0
- package/fesm/interfaces/permission.interface.js +1 -3
- package/fesm/utils/request.util.js +2 -1
- package/guards/permission.guard.d.ts +2 -4
- package/index.d.ts +1 -0
- package/interfaces/event-manager-adapter.interface.d.ts +43 -0
- package/interfaces/index.d.ts +2 -0
- package/interfaces/notification-adapter.interface.d.ts +22 -0
- package/interfaces/permission.interface.d.ts +13 -8
- package/package.json +7 -2
|
@@ -72,11 +72,11 @@ function _ts_param(paramIndex, decorator) {
|
|
|
72
72
|
// Apply PermissionGuard to check permissions from cache
|
|
73
73
|
decorators.push((0, _common.UseGuards)(_guards.JwtAuthGuard, _guards.PermissionGuard));
|
|
74
74
|
decorators.push((0, _swagger.ApiBearerAuth)());
|
|
75
|
-
// Check for complex
|
|
76
|
-
if (security.
|
|
77
|
-
decorators.push((0, _decorators.
|
|
75
|
+
// Check for complex logic first
|
|
76
|
+
if (security.logic) {
|
|
77
|
+
decorators.push((0, _decorators.RequirePermissionLogic)(security.logic));
|
|
78
78
|
} else if (security.permissions && security.permissions.length > 0) {
|
|
79
|
-
if (security.operator === '
|
|
79
|
+
if (security.operator === 'OR') {
|
|
80
80
|
decorators.push((0, _decorators.RequireAnyPermission)(...security.permissions));
|
|
81
81
|
} else {
|
|
82
82
|
// AND is default and most secure
|
|
@@ -1,13 +1,4 @@
|
|
|
1
|
-
|
|
2
|
-
* Centralized Permission Codes
|
|
3
|
-
*
|
|
4
|
-
* Single source of truth for all permission codes used across the application.
|
|
5
|
-
* Use these constants instead of hardcoded strings to prevent typos and enable easy refactoring.
|
|
6
|
-
*
|
|
7
|
-
* Naming Convention: <entity>.<action>
|
|
8
|
-
* - entity: The resource being accessed (e.g., user, role, company)
|
|
9
|
-
* - action: The operation being performed (create, read, update, delete, assign)
|
|
10
|
-
*/ // ==================== AUTH MODULE ====================
|
|
1
|
+
// ==================== AUTH MODULE ====================
|
|
11
2
|
"use strict";
|
|
12
3
|
Object.defineProperty(exports, "__esModule", {
|
|
13
4
|
value: true
|
|
@@ -37,6 +28,12 @@ _export(exports, {
|
|
|
37
28
|
get EMAIL_TEMPLATE_PERMISSIONS () {
|
|
38
29
|
return EMAIL_TEMPLATE_PERMISSIONS;
|
|
39
30
|
},
|
|
31
|
+
get EVENT_PARTICIPANT_PERMISSIONS () {
|
|
32
|
+
return EVENT_PARTICIPANT_PERMISSIONS;
|
|
33
|
+
},
|
|
34
|
+
get EVENT_PERMISSIONS () {
|
|
35
|
+
return EVENT_PERMISSIONS;
|
|
36
|
+
},
|
|
40
37
|
get FILE_PERMISSIONS () {
|
|
41
38
|
return FILE_PERMISSIONS;
|
|
42
39
|
},
|
|
@@ -49,6 +46,9 @@ _export(exports, {
|
|
|
49
46
|
get FORM_RESULT_PERMISSIONS () {
|
|
50
47
|
return FORM_RESULT_PERMISSIONS;
|
|
51
48
|
},
|
|
49
|
+
get NOTIFICATION_PERMISSIONS () {
|
|
50
|
+
return NOTIFICATION_PERMISSIONS;
|
|
51
|
+
},
|
|
52
52
|
get PERMISSIONS () {
|
|
53
53
|
return PERMISSIONS;
|
|
54
54
|
},
|
|
@@ -159,6 +159,24 @@ const FORM_RESULT_PERMISSIONS = {
|
|
|
159
159
|
UPDATE: 'form-result.update',
|
|
160
160
|
DELETE: 'form-result.delete'
|
|
161
161
|
};
|
|
162
|
+
const EVENT_PERMISSIONS = {
|
|
163
|
+
CREATE: 'event.create',
|
|
164
|
+
READ: 'event.read',
|
|
165
|
+
UPDATE: 'event.update',
|
|
166
|
+
DELETE: 'event.delete'
|
|
167
|
+
};
|
|
168
|
+
const EVENT_PARTICIPANT_PERMISSIONS = {
|
|
169
|
+
CREATE: 'event-participant.create',
|
|
170
|
+
READ: 'event-participant.read',
|
|
171
|
+
UPDATE: 'event-participant.update',
|
|
172
|
+
DELETE: 'event-participant.delete'
|
|
173
|
+
};
|
|
174
|
+
const NOTIFICATION_PERMISSIONS = {
|
|
175
|
+
CREATE: 'notification.create',
|
|
176
|
+
READ: 'notification.read',
|
|
177
|
+
UPDATE: 'notification.update',
|
|
178
|
+
DELETE: 'notification.delete'
|
|
179
|
+
};
|
|
162
180
|
const PERMISSIONS = {
|
|
163
181
|
// Auth
|
|
164
182
|
USER: USER_PERMISSIONS,
|
|
@@ -180,5 +198,10 @@ const PERMISSIONS = {
|
|
|
180
198
|
EMAIL_TEMPLATE: EMAIL_TEMPLATE_PERMISSIONS,
|
|
181
199
|
// Form Builder
|
|
182
200
|
FORM: FORM_PERMISSIONS,
|
|
183
|
-
FORM_RESULT: FORM_RESULT_PERMISSIONS
|
|
201
|
+
FORM_RESULT: FORM_RESULT_PERMISSIONS,
|
|
202
|
+
// Event Manager
|
|
203
|
+
EVENT: EVENT_PERMISSIONS,
|
|
204
|
+
EVENT_PARTICIPANT: EVENT_PARTICIPANT_PERMISSIONS,
|
|
205
|
+
// Notification
|
|
206
|
+
NOTIFICATION: NOTIFICATION_PERMISSIONS
|
|
184
207
|
};
|
|
@@ -17,16 +17,20 @@ _export(exports, {
|
|
|
17
17
|
},
|
|
18
18
|
get RequirePermissionCondition () {
|
|
19
19
|
return RequirePermissionCondition;
|
|
20
|
+
},
|
|
21
|
+
get RequirePermissionLogic () {
|
|
22
|
+
return RequirePermissionLogic;
|
|
20
23
|
}
|
|
21
24
|
});
|
|
22
25
|
const _common = require("@nestjs/common");
|
|
23
26
|
const _constants = require("../constants");
|
|
24
27
|
const RequirePermission = (...permissions)=>(0, _common.SetMetadata)(_constants.PERMISSIONS_KEY, {
|
|
25
28
|
permissions,
|
|
26
|
-
operator: '
|
|
29
|
+
operator: 'AND'
|
|
27
30
|
});
|
|
28
31
|
const RequireAnyPermission = (...permissions)=>(0, _common.SetMetadata)(_constants.PERMISSIONS_KEY, {
|
|
29
32
|
permissions,
|
|
30
|
-
operator: '
|
|
33
|
+
operator: 'OR'
|
|
31
34
|
});
|
|
32
|
-
const
|
|
35
|
+
const RequirePermissionLogic = (logic)=>(0, _common.SetMetadata)(_constants.PERMISSIONS_KEY, logic);
|
|
36
|
+
const RequirePermissionCondition = RequirePermissionLogic;
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", {
|
|
3
|
+
value: true
|
|
4
|
+
});
|
|
5
|
+
_export_star(require("./notification-type.enum"), exports);
|
|
6
|
+
_export_star(require("./participant-status.enum"), exports);
|
|
7
|
+
_export_star(require("./recurrence-type.enum"), exports);
|
|
8
|
+
function _export_star(from, to) {
|
|
9
|
+
Object.keys(from).forEach(function(k) {
|
|
10
|
+
if (k !== "default" && !Object.prototype.hasOwnProperty.call(to, k)) {
|
|
11
|
+
Object.defineProperty(to, k, {
|
|
12
|
+
enumerable: true,
|
|
13
|
+
get: function() {
|
|
14
|
+
return from[k];
|
|
15
|
+
}
|
|
16
|
+
});
|
|
17
|
+
}
|
|
18
|
+
});
|
|
19
|
+
return from;
|
|
20
|
+
}
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", {
|
|
3
|
+
value: true
|
|
4
|
+
});
|
|
5
|
+
Object.defineProperty(exports, "NotificationType", {
|
|
6
|
+
enumerable: true,
|
|
7
|
+
get: function() {
|
|
8
|
+
return NotificationType;
|
|
9
|
+
}
|
|
10
|
+
});
|
|
11
|
+
var NotificationType = /*#__PURE__*/ function(NotificationType) {
|
|
12
|
+
NotificationType["INFO"] = "info";
|
|
13
|
+
NotificationType["WARNING"] = "warning";
|
|
14
|
+
NotificationType["SUCCESS"] = "success";
|
|
15
|
+
NotificationType["ERROR"] = "error";
|
|
16
|
+
return NotificationType;
|
|
17
|
+
}({});
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", {
|
|
3
|
+
value: true
|
|
4
|
+
});
|
|
5
|
+
Object.defineProperty(exports, "ParticipantStatus", {
|
|
6
|
+
enumerable: true,
|
|
7
|
+
get: function() {
|
|
8
|
+
return ParticipantStatus;
|
|
9
|
+
}
|
|
10
|
+
});
|
|
11
|
+
var ParticipantStatus = /*#__PURE__*/ function(ParticipantStatus) {
|
|
12
|
+
ParticipantStatus["PENDING"] = "pending";
|
|
13
|
+
ParticipantStatus["ACCEPTED"] = "accepted";
|
|
14
|
+
ParticipantStatus["DECLINED"] = "declined";
|
|
15
|
+
ParticipantStatus["TENTATIVE"] = "tentative";
|
|
16
|
+
return ParticipantStatus;
|
|
17
|
+
}({});
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", {
|
|
3
|
+
value: true
|
|
4
|
+
});
|
|
5
|
+
Object.defineProperty(exports, "RecurrenceType", {
|
|
6
|
+
enumerable: true,
|
|
7
|
+
get: function() {
|
|
8
|
+
return RecurrenceType;
|
|
9
|
+
}
|
|
10
|
+
});
|
|
11
|
+
var RecurrenceType = /*#__PURE__*/ function(RecurrenceType) {
|
|
12
|
+
RecurrenceType["NONE"] = "none";
|
|
13
|
+
RecurrenceType["DAILY"] = "daily";
|
|
14
|
+
RecurrenceType["WEEKLY"] = "weekly";
|
|
15
|
+
RecurrenceType["BIWEEKLY"] = "biweekly";
|
|
16
|
+
RecurrenceType["MONTHLY"] = "monthly";
|
|
17
|
+
return RecurrenceType;
|
|
18
|
+
}({});
|
|
@@ -30,8 +30,8 @@ let PermissionSystemUnavailableException = class PermissionSystemUnavailableExce
|
|
|
30
30
|
}
|
|
31
31
|
};
|
|
32
32
|
let InsufficientPermissionsException = class InsufficientPermissionsException extends _common.ForbiddenException {
|
|
33
|
-
constructor(missingPermissions, operator = '
|
|
34
|
-
const message = operator === '
|
|
33
|
+
constructor(missingPermissions, operator = 'AND'){
|
|
34
|
+
const message = operator === 'OR' ? `Requires at least one of: ${missingPermissions.join(', ')}` : `Missing required permissions: ${missingPermissions.join(', ')}`;
|
|
35
35
|
super({
|
|
36
36
|
success: false,
|
|
37
37
|
message,
|
|
@@ -55,8 +55,6 @@ let PermissionGuard = class PermissionGuard {
|
|
|
55
55
|
context.getClass()
|
|
56
56
|
]);
|
|
57
57
|
if (!permissionConfig) return true;
|
|
58
|
-
const { permissions: requiredPermissions, operator } = this.normalizePermissionConfig(permissionConfig);
|
|
59
|
-
if (!requiredPermissions || requiredPermissions.length === 0) return true;
|
|
60
58
|
const request = context.switchToHttp().getRequest();
|
|
61
59
|
const user = request.user;
|
|
62
60
|
if (!user) throw new _common.UnauthorizedException('Authentication required');
|
|
@@ -69,90 +67,93 @@ let PermissionGuard = class PermissionGuard {
|
|
|
69
67
|
this.logger.warn(`No permissions found (userId: ${user.id})`, 'PermissionGuard');
|
|
70
68
|
throw new _permissionexception.NoPermissionsFoundException();
|
|
71
69
|
}
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
} else {
|
|
79
|
-
this.validateSimplePermissions(requiredPermissions, userPermissions, operator);
|
|
70
|
+
const logicNode = this.normalizeToLogicNode(permissionConfig);
|
|
71
|
+
if (!logicNode) return true;
|
|
72
|
+
const result = this.evaluateLogicNode(logicNode, userPermissions);
|
|
73
|
+
if (!result.passed) {
|
|
74
|
+
this.logger.warn(`Permission denied (userId: ${user.id})`, 'PermissionGuard');
|
|
75
|
+
throw new _permissionexception.InsufficientPermissionsException(result.missingPermissions, result.operator);
|
|
80
76
|
}
|
|
81
77
|
return true;
|
|
82
78
|
}
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
operator: config.operator || 'and'
|
|
91
|
-
};
|
|
92
|
-
}
|
|
93
|
-
validateSimplePermissions(requiredPermissions, userPermissions, operator) {
|
|
94
|
-
if (operator === 'or') {
|
|
95
|
-
const hasAny = requiredPermissions.some((p)=>this.hasPermission(userPermissions, p));
|
|
96
|
-
if (!hasAny) throw new _permissionexception.InsufficientPermissionsException(requiredPermissions, 'or');
|
|
97
|
-
} else {
|
|
98
|
-
const hasAll = requiredPermissions.every((p)=>this.hasPermission(userPermissions, p));
|
|
99
|
-
if (!hasAll) {
|
|
100
|
-
const missing = requiredPermissions.filter((p)=>!this.hasPermission(userPermissions, p));
|
|
101
|
-
throw new _permissionexception.InsufficientPermissionsException(missing, 'and');
|
|
102
|
-
}
|
|
79
|
+
normalizeToLogicNode(config) {
|
|
80
|
+
// string - single permission
|
|
81
|
+
if (typeof config === 'string') {
|
|
82
|
+
return config ? {
|
|
83
|
+
type: 'action',
|
|
84
|
+
actionId: config
|
|
85
|
+
} : null;
|
|
103
86
|
}
|
|
87
|
+
// string[] - treat as AND of multiple permissions
|
|
88
|
+
if (Array.isArray(config)) {
|
|
89
|
+
if (config.length === 0) return null;
|
|
90
|
+
if (config.length === 1) return {
|
|
91
|
+
type: 'action',
|
|
92
|
+
actionId: config[0]
|
|
93
|
+
};
|
|
94
|
+
return {
|
|
95
|
+
type: 'group',
|
|
96
|
+
operator: 'AND',
|
|
97
|
+
children: config.map((p)=>({
|
|
98
|
+
type: 'action',
|
|
99
|
+
actionId: p
|
|
100
|
+
}))
|
|
101
|
+
};
|
|
102
|
+
}
|
|
103
|
+
// SimplePermissionConfig - { permissions: string[], operator: 'AND'|'OR' }
|
|
104
|
+
if ('permissions' in config && Array.isArray(config.permissions)) {
|
|
105
|
+
const simple = config;
|
|
106
|
+
if (simple.permissions.length === 0) return null;
|
|
107
|
+
if (simple.permissions.length === 1) return {
|
|
108
|
+
type: 'action',
|
|
109
|
+
actionId: simple.permissions[0]
|
|
110
|
+
};
|
|
111
|
+
return {
|
|
112
|
+
type: 'group',
|
|
113
|
+
operator: simple.operator || 'AND',
|
|
114
|
+
children: simple.permissions.map((p)=>({
|
|
115
|
+
type: 'action',
|
|
116
|
+
actionId: p
|
|
117
|
+
}))
|
|
118
|
+
};
|
|
119
|
+
}
|
|
120
|
+
// ILogicNode
|
|
121
|
+
return config;
|
|
104
122
|
}
|
|
105
|
-
|
|
106
|
-
if (
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
123
|
+
evaluateLogicNode(node, userPermissions) {
|
|
124
|
+
if (node.type === 'action') {
|
|
125
|
+
const passed = this.hasPermission(userPermissions, node.actionId);
|
|
126
|
+
return {
|
|
127
|
+
passed,
|
|
128
|
+
missingPermissions: passed ? [] : [
|
|
129
|
+
node.actionId
|
|
130
|
+
],
|
|
131
|
+
operator: 'AND'
|
|
132
|
+
};
|
|
133
|
+
}
|
|
134
|
+
// Group node
|
|
135
|
+
const { operator, children } = node;
|
|
136
|
+
// SECURITY: Fail-closed - deny access when no children configured
|
|
137
|
+
if (!children || children.length === 0) {
|
|
113
138
|
return {
|
|
114
139
|
passed: false,
|
|
115
|
-
message: 'No permissions configured - access denied by default',
|
|
116
140
|
missingPermissions: [],
|
|
117
141
|
operator
|
|
118
142
|
};
|
|
119
143
|
}
|
|
120
144
|
const results = [];
|
|
121
|
-
const failureDetails = [];
|
|
122
145
|
const missingPermissions = [];
|
|
123
|
-
if (permissions.length > 0) {
|
|
124
|
-
if (operator === 'or') {
|
|
125
|
-
const hasAny = permissions.some((p)=>this.hasPermission(userPermissions, p));
|
|
126
|
-
results.push(hasAny);
|
|
127
|
-
if (!hasAny) {
|
|
128
|
-
failureDetails.push(`needs one of: [${permissions.join(', ')}]`);
|
|
129
|
-
missingPermissions.push(...permissions);
|
|
130
|
-
}
|
|
131
|
-
} else {
|
|
132
|
-
const hasAll = permissions.every((p)=>this.hasPermission(userPermissions, p));
|
|
133
|
-
results.push(hasAll);
|
|
134
|
-
if (!hasAll) {
|
|
135
|
-
const missing = permissions.filter((p)=>!this.hasPermission(userPermissions, p));
|
|
136
|
-
failureDetails.push(`missing: [${missing.join(', ')}]`);
|
|
137
|
-
missingPermissions.push(...missing);
|
|
138
|
-
}
|
|
139
|
-
}
|
|
140
|
-
}
|
|
141
146
|
for (const child of children){
|
|
142
|
-
const childResult = this.
|
|
147
|
+
const childResult = this.evaluateLogicNode(child, userPermissions);
|
|
143
148
|
results.push(childResult.passed);
|
|
144
149
|
if (!childResult.passed) {
|
|
145
|
-
failureDetails.push(`(${childResult.message})`);
|
|
146
150
|
missingPermissions.push(...childResult.missingPermissions);
|
|
147
151
|
}
|
|
148
152
|
}
|
|
149
|
-
|
|
150
|
-
const passed = operator === 'or' ? results.some((r)=>r) : results.every((r)=>r);
|
|
151
|
-
const message = passed ? 'OK' : `Denied: ${failureDetails.join(` ${operator.toUpperCase()} `)}`;
|
|
153
|
+
const passed = operator === 'OR' ? results.some((r)=>r) : results.every((r)=>r);
|
|
152
154
|
return {
|
|
153
155
|
passed,
|
|
154
|
-
|
|
155
|
-
missingPermissions,
|
|
156
|
+
missingPermissions: passed ? [] : missingPermissions,
|
|
156
157
|
operator
|
|
157
158
|
};
|
|
158
159
|
}
|
package/cjs/index.js
CHANGED
|
@@ -7,6 +7,7 @@ _export_star(require("./constants"), exports);
|
|
|
7
7
|
_export_star(require("./decorators"), exports);
|
|
8
8
|
_export_star(require("./dtos"), exports);
|
|
9
9
|
_export_star(require("./entities"), exports);
|
|
10
|
+
_export_star(require("./enums"), exports);
|
|
10
11
|
_export_star(require("./exceptions"), exports);
|
|
11
12
|
_export_star(require("./guards"), exports);
|
|
12
13
|
_export_star(require("./interceptors"), exports);
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", {
|
|
3
|
+
value: true
|
|
4
|
+
});
|
|
5
|
+
Object.defineProperty(exports, "EVENT_MANAGER_ADAPTER", {
|
|
6
|
+
enumerable: true,
|
|
7
|
+
get: function() {
|
|
8
|
+
return EVENT_MANAGER_ADAPTER;
|
|
9
|
+
}
|
|
10
|
+
});
|
|
11
|
+
const EVENT_MANAGER_ADAPTER = 'EVENT_MANAGER_ADAPTER';
|
package/cjs/interfaces/index.js
CHANGED
|
@@ -4,10 +4,12 @@ Object.defineProperty(exports, "__esModule", {
|
|
|
4
4
|
});
|
|
5
5
|
_export_star(require("./api.interface"), exports);
|
|
6
6
|
_export_star(require("./datasource.interface"), exports);
|
|
7
|
+
_export_star(require("./event-manager-adapter.interface"), exports);
|
|
7
8
|
_export_star(require("./identity.interface"), exports);
|
|
8
9
|
_export_star(require("./logged-user-info.interface"), exports);
|
|
9
10
|
_export_star(require("./logger.interface"), exports);
|
|
10
11
|
_export_star(require("./module-config.interface"), exports);
|
|
12
|
+
_export_star(require("./notification-adapter.interface"), exports);
|
|
11
13
|
_export_star(require("./permission.interface"), exports);
|
|
12
14
|
function _export_star(from, to) {
|
|
13
15
|
Object.keys(from).forEach(function(k) {
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", {
|
|
3
|
+
value: true
|
|
4
|
+
});
|
|
5
|
+
Object.defineProperty(exports, "NOTIFICATION_ADAPTER", {
|
|
6
|
+
enumerable: true,
|
|
7
|
+
get: function() {
|
|
8
|
+
return NOTIFICATION_ADAPTER;
|
|
9
|
+
}
|
|
10
|
+
});
|
|
11
|
+
const NOTIFICATION_ADAPTER = 'NOTIFICATION_ADAPTER';
|
|
@@ -1,11 +1,4 @@
|
|
|
1
|
-
|
|
2
|
-
* HTML Sanitizer Utilities
|
|
3
|
-
*
|
|
4
|
-
* Provides functions for escaping HTML content to prevent XSS attacks.
|
|
5
|
-
* Use these utilities when interpolating user-provided variables into HTML content.
|
|
6
|
-
*/ /**
|
|
7
|
-
* HTML entity mapping for escaping special characters
|
|
8
|
-
*/ "use strict";
|
|
1
|
+
"use strict";
|
|
9
2
|
Object.defineProperty(exports, "__esModule", {
|
|
10
3
|
value: true
|
|
11
4
|
});
|
|
@@ -23,7 +16,14 @@ _export(exports, {
|
|
|
23
16
|
return escapeHtmlVariables;
|
|
24
17
|
}
|
|
25
18
|
});
|
|
26
|
-
|
|
19
|
+
/**
|
|
20
|
+
* HTML Sanitizer Utilities
|
|
21
|
+
*
|
|
22
|
+
* Provides functions for escaping HTML content to prevent XSS attacks.
|
|
23
|
+
* Use these utilities when interpolating user-provided variables into HTML content.
|
|
24
|
+
*/ /**
|
|
25
|
+
* HTML entity mapping for escaping special characters
|
|
26
|
+
*/ const HTML_ESCAPE_MAP = {
|
|
27
27
|
'&': '&',
|
|
28
28
|
'<': '<',
|
|
29
29
|
'>': '>',
|
|
@@ -19,6 +19,7 @@ _export(exports, {
|
|
|
19
19
|
return parseDurationToMs;
|
|
20
20
|
}
|
|
21
21
|
});
|
|
22
|
+
const _config = require("@flusys/nestjs-core/config");
|
|
22
23
|
const _constants = require("../constants");
|
|
23
24
|
/** Time unit multipliers in milliseconds */ const TIME_UNIT_MS = {
|
|
24
25
|
s: 1000,
|
|
@@ -45,7 +46,7 @@ function isBrowserRequest(req) {
|
|
|
45
46
|
function buildCookieOptions(req) {
|
|
46
47
|
const hostname = req.hostname || '';
|
|
47
48
|
const origin = req.headers.origin || '';
|
|
48
|
-
const isProduction =
|
|
49
|
+
const isProduction = _config.envConfig.isProduction();
|
|
49
50
|
const forwardedProto = req.headers['x-forwarded-proto'];
|
|
50
51
|
const isHttps = isProduction || forwardedProto === 'https' || origin.startsWith('https://') || req.secure;
|
|
51
52
|
let domain;
|
|
@@ -1,14 +1,14 @@
|
|
|
1
1
|
import { BulkResponseDto, DeleteDto, FilterAndPaginationDto, GetByIdBodyDto, ListResponseDto, MessageResponseDto, SingleResponseDto } from '../dtos';
|
|
2
2
|
import { Identity } from '../entities';
|
|
3
|
-
import { ILoggedUserInfo,
|
|
3
|
+
import { ILoggedUserInfo, IPermissionLogic, IService } from '../interfaces';
|
|
4
4
|
import { Type } from '@nestjs/common';
|
|
5
5
|
export type ApiEndpoint = 'insert' | 'insertMany' | 'getById' | 'getAll' | 'update' | 'updateMany' | 'delete';
|
|
6
6
|
export type SecurityLevel = 'public' | 'jwt' | 'permission';
|
|
7
7
|
export interface EndpointSecurity {
|
|
8
8
|
level: SecurityLevel;
|
|
9
9
|
permissions?: string[];
|
|
10
|
-
operator?:
|
|
11
|
-
|
|
10
|
+
operator?: 'AND' | 'OR';
|
|
11
|
+
logic?: IPermissionLogic;
|
|
12
12
|
}
|
|
13
13
|
export type ApiSecurityConfig = {
|
|
14
14
|
[K in ApiEndpoint]?: EndpointSecurity | SecurityLevel;
|
|
@@ -86,6 +86,24 @@ export declare const FORM_RESULT_PERMISSIONS: {
|
|
|
86
86
|
readonly UPDATE: "form-result.update";
|
|
87
87
|
readonly DELETE: "form-result.delete";
|
|
88
88
|
};
|
|
89
|
+
export declare const EVENT_PERMISSIONS: {
|
|
90
|
+
readonly CREATE: "event.create";
|
|
91
|
+
readonly READ: "event.read";
|
|
92
|
+
readonly UPDATE: "event.update";
|
|
93
|
+
readonly DELETE: "event.delete";
|
|
94
|
+
};
|
|
95
|
+
export declare const EVENT_PARTICIPANT_PERMISSIONS: {
|
|
96
|
+
readonly CREATE: "event-participant.create";
|
|
97
|
+
readonly READ: "event-participant.read";
|
|
98
|
+
readonly UPDATE: "event-participant.update";
|
|
99
|
+
readonly DELETE: "event-participant.delete";
|
|
100
|
+
};
|
|
101
|
+
export declare const NOTIFICATION_PERMISSIONS: {
|
|
102
|
+
readonly CREATE: "notification.create";
|
|
103
|
+
readonly READ: "notification.read";
|
|
104
|
+
readonly UPDATE: "notification.update";
|
|
105
|
+
readonly DELETE: "notification.delete";
|
|
106
|
+
};
|
|
89
107
|
export declare const PERMISSIONS: {
|
|
90
108
|
readonly USER: {
|
|
91
109
|
readonly CREATE: "user.create";
|
|
@@ -175,5 +193,23 @@ export declare const PERMISSIONS: {
|
|
|
175
193
|
readonly UPDATE: "form-result.update";
|
|
176
194
|
readonly DELETE: "form-result.delete";
|
|
177
195
|
};
|
|
196
|
+
readonly EVENT: {
|
|
197
|
+
readonly CREATE: "event.create";
|
|
198
|
+
readonly READ: "event.read";
|
|
199
|
+
readonly UPDATE: "event.update";
|
|
200
|
+
readonly DELETE: "event.delete";
|
|
201
|
+
};
|
|
202
|
+
readonly EVENT_PARTICIPANT: {
|
|
203
|
+
readonly CREATE: "event-participant.create";
|
|
204
|
+
readonly READ: "event-participant.read";
|
|
205
|
+
readonly UPDATE: "event-participant.update";
|
|
206
|
+
readonly DELETE: "event-participant.delete";
|
|
207
|
+
};
|
|
208
|
+
readonly NOTIFICATION: {
|
|
209
|
+
readonly CREATE: "notification.create";
|
|
210
|
+
readonly READ: "notification.read";
|
|
211
|
+
readonly UPDATE: "notification.update";
|
|
212
|
+
readonly DELETE: "notification.delete";
|
|
213
|
+
};
|
|
178
214
|
};
|
|
179
215
|
export type PermissionCode = (typeof PERMISSIONS)[keyof typeof PERMISSIONS][keyof (typeof PERMISSIONS)[keyof typeof PERMISSIONS]];
|
|
@@ -1,4 +1,5 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { IPermissionLogic } from '../interfaces/permission.interface';
|
|
2
2
|
export declare const RequirePermission: (...permissions: string[]) => import("@nestjs/common").CustomDecorator<string>;
|
|
3
3
|
export declare const RequireAnyPermission: (...permissions: string[]) => import("@nestjs/common").CustomDecorator<string>;
|
|
4
|
-
export declare const
|
|
4
|
+
export declare const RequirePermissionLogic: (logic: IPermissionLogic) => import("@nestjs/common").CustomDecorator<string>;
|
|
5
|
+
export declare const RequirePermissionCondition: (logic: IPermissionLogic) => import("@nestjs/common").CustomDecorator<string>;
|
package/enums/index.d.ts
ADDED
|
@@ -3,7 +3,7 @@ export declare class PermissionSystemUnavailableException extends InternalServer
|
|
|
3
3
|
constructor(message?: string);
|
|
4
4
|
}
|
|
5
5
|
export declare class InsufficientPermissionsException extends ForbiddenException {
|
|
6
|
-
constructor(missingPermissions: string[], operator?: '
|
|
6
|
+
constructor(missingPermissions: string[], operator?: 'AND' | 'OR');
|
|
7
7
|
}
|
|
8
8
|
export declare class NoPermissionsFoundException extends ForbiddenException {
|
|
9
9
|
constructor();
|
|
@@ -25,7 +25,7 @@ function _ts_param(paramIndex, decorator) {
|
|
|
25
25
|
decorator(target, key, paramIndex);
|
|
26
26
|
};
|
|
27
27
|
}
|
|
28
|
-
import { CurrentUser, Public, RequirePermission, RequireAnyPermission,
|
|
28
|
+
import { CurrentUser, Public, RequirePermission, RequireAnyPermission, RequirePermissionLogic } from '../decorators';
|
|
29
29
|
import { DeleteDto, FilterAndPaginationDto, GetByIdBodyDto } from '../dtos';
|
|
30
30
|
import { JwtAuthGuard, PermissionGuard } from '../guards';
|
|
31
31
|
import { IdempotencyInterceptor, SetCreatedByOnBody, SetDeletedByOnBody, SetUpdateByOnBody, Slug } from '../interceptors';
|
|
@@ -62,11 +62,11 @@ import { ApiResponseDto } from '../decorators/api-response.decorator';
|
|
|
62
62
|
// Apply PermissionGuard to check permissions from cache
|
|
63
63
|
decorators.push(UseGuards(JwtAuthGuard, PermissionGuard));
|
|
64
64
|
decorators.push(ApiBearerAuth());
|
|
65
|
-
// Check for complex
|
|
66
|
-
if (security.
|
|
67
|
-
decorators.push(
|
|
65
|
+
// Check for complex logic first
|
|
66
|
+
if (security.logic) {
|
|
67
|
+
decorators.push(RequirePermissionLogic(security.logic));
|
|
68
68
|
} else if (security.permissions && security.permissions.length > 0) {
|
|
69
|
-
if (security.operator === '
|
|
69
|
+
if (security.operator === 'OR') {
|
|
70
70
|
decorators.push(RequireAnyPermission(...security.permissions));
|
|
71
71
|
} else {
|
|
72
72
|
// AND is default and most secure
|