@flusys/nestjs-iam 0.1.0-beta.1 → 0.1.0-beta.2
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 +665 -0
- package/cjs/config/iam.constants.js +11 -0
- package/cjs/config/index.js +18 -0
- package/cjs/controllers/action.controller.js +117 -0
- package/cjs/controllers/company-action-permission.controller.js +110 -0
- package/cjs/controllers/index.js +23 -0
- package/cjs/controllers/my-permission.controller.js +90 -0
- package/cjs/controllers/role-permission.controller.js +160 -0
- package/cjs/controllers/role.controller.js +58 -0
- package/cjs/controllers/user-action-permission.controller.js +110 -0
- package/cjs/docs/iam-swagger.config.js +202 -0
- package/cjs/docs/index.js +18 -0
- package/cjs/dtos/action.dto.js +347 -0
- package/cjs/dtos/index.js +21 -0
- package/cjs/dtos/permission.dto.js +554 -0
- package/cjs/dtos/role.dto.js +238 -0
- package/cjs/entities/action-base.entity.js +135 -0
- package/cjs/entities/action.entity.js +28 -0
- package/cjs/entities/index.js +81 -0
- package/cjs/entities/permission-base.entity.js +156 -0
- package/cjs/entities/permission-with-company.entity.js +99 -0
- package/cjs/entities/role-base.entity.js +86 -0
- package/cjs/entities/role-with-company.entity.js +55 -0
- package/cjs/entities/role.entity.js +25 -0
- package/cjs/entities/user-iam-permission.entity.js +57 -0
- package/cjs/enums/action-type.enum.js +22 -0
- package/cjs/enums/index.js +19 -0
- package/cjs/enums/permission-type.enum.js +16 -0
- package/cjs/helpers/index.js +19 -0
- package/cjs/helpers/permission-evaluator.helper.js +175 -0
- package/cjs/helpers/permission-mode.helper.js +49 -0
- package/cjs/index.js +28 -79
- package/cjs/interfaces/action.interface.js +4 -0
- package/cjs/interfaces/iam-module-async-options.interface.js +4 -0
- package/cjs/interfaces/iam-module-options.interface.js +18 -0
- package/cjs/interfaces/index.js +21 -0
- package/cjs/interfaces/role.interface.js +7 -0
- package/cjs/modules/iam.module.js +237 -0
- package/cjs/modules/index.js +18 -0
- package/cjs/services/action.service.js +253 -0
- package/cjs/services/iam-config.service.js +107 -0
- package/cjs/services/iam-datasource.provider.js +205 -0
- package/cjs/services/index.js +23 -0
- package/cjs/services/permission-cache.service.js +308 -0
- package/cjs/services/permission.service.js +1020 -0
- package/cjs/services/role.service.js +181 -0
- package/cjs/types/index.js +18 -0
- package/cjs/types/logic-node.type.js +54 -0
- package/fesm/config/iam.constants.js +1 -0
- package/fesm/config/index.js +1 -0
- package/fesm/controllers/action.controller.js +107 -0
- package/fesm/controllers/company-action-permission.controller.js +100 -0
- package/fesm/controllers/index.js +7 -0
- package/fesm/controllers/my-permission.controller.js +80 -0
- package/fesm/controllers/role-permission.controller.js +150 -0
- package/fesm/controllers/role.controller.js +48 -0
- package/fesm/controllers/user-action-permission.controller.js +100 -0
- package/fesm/docs/iam-swagger.config.js +192 -0
- package/fesm/docs/index.js +1 -0
- package/fesm/dtos/action.dto.js +317 -0
- package/fesm/dtos/index.js +4 -0
- package/fesm/dtos/permission.dto.js +490 -0
- package/fesm/dtos/role.dto.js +214 -0
- package/fesm/entities/action-base.entity.js +128 -0
- package/fesm/entities/action.entity.js +18 -0
- package/fesm/entities/index.js +56 -0
- package/fesm/entities/permission-base.entity.js +138 -0
- package/fesm/entities/permission-with-company.entity.js +89 -0
- package/fesm/entities/role-base.entity.js +79 -0
- package/fesm/entities/role-with-company.entity.js +45 -0
- package/fesm/entities/role.entity.js +15 -0
- package/fesm/entities/user-iam-permission.entity.js +38 -0
- package/fesm/enums/action-type.enum.js +12 -0
- package/fesm/enums/index.js +2 -0
- package/fesm/enums/permission-type.enum.js +6 -0
- package/fesm/helpers/index.js +2 -0
- package/fesm/helpers/permission-evaluator.helper.js +165 -0
- package/fesm/helpers/permission-mode.helper.js +49 -0
- package/fesm/index.js +11 -79
- package/fesm/interfaces/action.interface.js +3 -0
- package/fesm/interfaces/iam-module-async-options.interface.js +3 -0
- package/fesm/interfaces/iam-module-options.interface.js +1 -0
- package/fesm/interfaces/index.js +4 -0
- package/fesm/interfaces/role.interface.js +4 -0
- package/fesm/modules/iam.module.js +227 -0
- package/fesm/modules/index.js +1 -0
- package/fesm/services/action.service.js +243 -0
- package/fesm/services/iam-config.service.js +97 -0
- package/fesm/services/iam-datasource.provider.js +154 -0
- package/fesm/services/index.js +6 -0
- package/fesm/services/permission-cache.service.js +298 -0
- package/fesm/services/permission.service.js +1010 -0
- package/fesm/services/role.service.js +171 -0
- package/fesm/types/index.js +1 -0
- package/fesm/types/logic-node.type.js +36 -0
- package/package.json +25 -25
- package/cjs/config-index.js +0 -1
- package/cjs/controllers-index.js +0 -1
- package/cjs/docs-index.js +0 -79
- package/cjs/dtos-index.js +0 -1
- package/cjs/entities-index.js +0 -1
- package/cjs/enums-index.js +0 -1
- package/cjs/helpers-index.js +0 -1
- package/cjs/interfaces-index.js +0 -1
- package/cjs/modules-index.js +0 -1
- package/cjs/services-index.js +0 -1
- package/cjs/types-index.js +0 -1
- package/fesm/config-index.js +0 -1
- package/fesm/controllers-index.js +0 -1
- package/fesm/docs-index.js +0 -79
- package/fesm/dtos-index.js +0 -1
- package/fesm/entities-index.js +0 -1
- package/fesm/enums-index.js +0 -1
- package/fesm/helpers-index.js +0 -1
- package/fesm/interfaces-index.js +0 -0
- package/fesm/modules-index.js +0 -1
- package/fesm/services-index.js +0 -1
- package/fesm/types-index.js +0 -1
|
@@ -0,0 +1,227 @@
|
|
|
1
|
+
function _ts_decorate(decorators, target, key, desc) {
|
|
2
|
+
var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
|
|
3
|
+
if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
|
|
4
|
+
else for(var i = decorators.length - 1; i >= 0; i--)if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
|
|
5
|
+
return c > 3 && r && Object.defineProperty(target, key, r), r;
|
|
6
|
+
}
|
|
7
|
+
import { CacheModule, UtilsModule } from '@flusys/nestjs-shared/modules';
|
|
8
|
+
import { Module, Scope } from '@nestjs/common';
|
|
9
|
+
import { getRepositoryToken } from '@nestjs/typeorm';
|
|
10
|
+
import { IAM_MODULE_OPTIONS } from '../config/iam.constants';
|
|
11
|
+
import { ActionController, CompanyActionPermissionController, MyPermissionController, RoleController, RolePermissionController, UserActionPermissionController } from '../controllers';
|
|
12
|
+
import { Action, Role, RoleWithCompany, UserIamPermission, UserIamPermissionWithCompany } from '../entities';
|
|
13
|
+
import { IAMPermissionMode } from '../enums/permission-type.enum';
|
|
14
|
+
import { PermissionEvaluatorHelper, PermissionModeHelper } from '../helpers';
|
|
15
|
+
import { ActionService, PermissionService, RoleService } from '../services';
|
|
16
|
+
import { IAMConfigService } from '../services/iam-config.service';
|
|
17
|
+
import { IAMDataSourceProvider } from '../services/iam-datasource.provider';
|
|
18
|
+
import { PermissionCacheService } from '../services/permission-cache.service';
|
|
19
|
+
export class IAMModule {
|
|
20
|
+
static getControllers(permissionMode, enableCompanyFeature) {
|
|
21
|
+
const baseControllers = [
|
|
22
|
+
ActionController,
|
|
23
|
+
MyPermissionController
|
|
24
|
+
];
|
|
25
|
+
// Direct permission mode - user actions only
|
|
26
|
+
if (permissionMode === IAMPermissionMode.DIRECT) {
|
|
27
|
+
baseControllers.push(UserActionPermissionController);
|
|
28
|
+
}
|
|
29
|
+
// RBAC mode - role actions and user roles only
|
|
30
|
+
if (permissionMode === IAMPermissionMode.RBAC) {
|
|
31
|
+
baseControllers.push(RoleController);
|
|
32
|
+
baseControllers.push(RolePermissionController);
|
|
33
|
+
}
|
|
34
|
+
// FULL mode - all permission controllers
|
|
35
|
+
if (permissionMode === IAMPermissionMode.FULL) {
|
|
36
|
+
baseControllers.push(RoleController);
|
|
37
|
+
baseControllers.push(UserActionPermissionController);
|
|
38
|
+
baseControllers.push(RolePermissionController);
|
|
39
|
+
}
|
|
40
|
+
// Company action permissions - only if company feature enabled
|
|
41
|
+
if (enableCompanyFeature) {
|
|
42
|
+
baseControllers.push(CompanyActionPermissionController);
|
|
43
|
+
}
|
|
44
|
+
return baseControllers;
|
|
45
|
+
}
|
|
46
|
+
static getEntities(permissionMode, enableCompanyFeature) {
|
|
47
|
+
// Core entities
|
|
48
|
+
const entities = [];
|
|
49
|
+
// Action entity - always included
|
|
50
|
+
entities.push(Action);
|
|
51
|
+
// Permission entity is always needed
|
|
52
|
+
if (enableCompanyFeature) {
|
|
53
|
+
entities.push(UserIamPermissionWithCompany);
|
|
54
|
+
} else {
|
|
55
|
+
entities.push(UserIamPermission);
|
|
56
|
+
}
|
|
57
|
+
// Role entity - Only for RBAC or FULL mode
|
|
58
|
+
if (permissionMode === IAMPermissionMode.RBAC || permissionMode === IAMPermissionMode.FULL) {
|
|
59
|
+
if (enableCompanyFeature) {
|
|
60
|
+
entities.push(RoleWithCompany);
|
|
61
|
+
} else {
|
|
62
|
+
entities.push(Role);
|
|
63
|
+
}
|
|
64
|
+
}
|
|
65
|
+
return entities;
|
|
66
|
+
}
|
|
67
|
+
static getServices(permissionMode) {
|
|
68
|
+
const services = [
|
|
69
|
+
ActionService,
|
|
70
|
+
PermissionService,
|
|
71
|
+
PermissionCacheService,
|
|
72
|
+
PermissionEvaluatorHelper
|
|
73
|
+
];
|
|
74
|
+
// RoleService - Only for RBAC or FULL mode
|
|
75
|
+
if (permissionMode === IAMPermissionMode.RBAC || permissionMode === IAMPermissionMode.FULL) {
|
|
76
|
+
services.push(RoleService);
|
|
77
|
+
}
|
|
78
|
+
return services;
|
|
79
|
+
}
|
|
80
|
+
/**
|
|
81
|
+
* Create repository providers that use IAMDataSourceProvider
|
|
82
|
+
* This replaces TypeOrmModule.forFeature() functionality
|
|
83
|
+
*/ static getRepositoryProviders(permissionMode, enableCompanyFeature) {
|
|
84
|
+
const entities = this.getEntities(permissionMode, enableCompanyFeature);
|
|
85
|
+
return entities.map((entity)=>({
|
|
86
|
+
provide: getRepositoryToken(entity),
|
|
87
|
+
scope: Scope.REQUEST,
|
|
88
|
+
useFactory: async (dataSourceProvider)=>{
|
|
89
|
+
return await dataSourceProvider.getRepository(entity);
|
|
90
|
+
},
|
|
91
|
+
inject: [
|
|
92
|
+
IAMDataSourceProvider
|
|
93
|
+
]
|
|
94
|
+
}));
|
|
95
|
+
}
|
|
96
|
+
static forRoot(options = {}) {
|
|
97
|
+
const { global = false, includeController = false } = options;
|
|
98
|
+
const databaseMode = options.bootstrapAppConfig?.databaseMode;
|
|
99
|
+
const enableCompanyFeature = options.bootstrapAppConfig?.enableCompanyFeature ?? false;
|
|
100
|
+
// Read permissionMode from bootstrap config using helper
|
|
101
|
+
const permissionMode = PermissionModeHelper.fromString(options.bootstrapAppConfig?.permissionMode);
|
|
102
|
+
const isMultiTenant = databaseMode === 'multi-tenant';
|
|
103
|
+
const entities = this.getEntities(permissionMode, enableCompanyFeature);
|
|
104
|
+
const controllers = includeController ? this.getControllers(permissionMode, enableCompanyFeature) : [];
|
|
105
|
+
const providers = [
|
|
106
|
+
{
|
|
107
|
+
provide: IAM_MODULE_OPTIONS,
|
|
108
|
+
useValue: options
|
|
109
|
+
},
|
|
110
|
+
IAMConfigService,
|
|
111
|
+
IAMDataSourceProvider,
|
|
112
|
+
...this.getServices(permissionMode)
|
|
113
|
+
];
|
|
114
|
+
const imports = [
|
|
115
|
+
CacheModule,
|
|
116
|
+
UtilsModule
|
|
117
|
+
];
|
|
118
|
+
const module = {
|
|
119
|
+
module: IAMModule,
|
|
120
|
+
imports,
|
|
121
|
+
controllers,
|
|
122
|
+
providers,
|
|
123
|
+
exports: [
|
|
124
|
+
IAMConfigService,
|
|
125
|
+
IAMDataSourceProvider,
|
|
126
|
+
ActionService,
|
|
127
|
+
PermissionService,
|
|
128
|
+
PermissionCacheService,
|
|
129
|
+
PermissionEvaluatorHelper,
|
|
130
|
+
...permissionMode === IAMPermissionMode.RBAC || permissionMode === IAMPermissionMode.FULL ? [
|
|
131
|
+
RoleService
|
|
132
|
+
] : []
|
|
133
|
+
]
|
|
134
|
+
};
|
|
135
|
+
if (global) {
|
|
136
|
+
return {
|
|
137
|
+
...module,
|
|
138
|
+
global: true
|
|
139
|
+
};
|
|
140
|
+
}
|
|
141
|
+
return module;
|
|
142
|
+
}
|
|
143
|
+
static forRootAsync(asyncOptions) {
|
|
144
|
+
const { global = false, includeController = false, imports: externalImports = [] } = asyncOptions;
|
|
145
|
+
const databaseMode = asyncOptions.bootstrapAppConfig?.databaseMode;
|
|
146
|
+
const enableCompanyFeature = asyncOptions.bootstrapAppConfig?.enableCompanyFeature ?? false;
|
|
147
|
+
// Read permissionMode from bootstrap config using helper
|
|
148
|
+
const permissionMode = PermissionModeHelper.fromString(asyncOptions.bootstrapAppConfig?.permissionMode);
|
|
149
|
+
const isMultiTenant = databaseMode === 'multi-tenant';
|
|
150
|
+
const entities = this.getEntities(permissionMode, enableCompanyFeature);
|
|
151
|
+
const controllers = includeController ? this.getControllers(permissionMode, enableCompanyFeature) : [];
|
|
152
|
+
const asyncProviders = this.createAsyncProviders(asyncOptions);
|
|
153
|
+
const providers = [
|
|
154
|
+
...asyncProviders,
|
|
155
|
+
IAMConfigService,
|
|
156
|
+
IAMDataSourceProvider,
|
|
157
|
+
...this.getServices(permissionMode)
|
|
158
|
+
];
|
|
159
|
+
const imports = [
|
|
160
|
+
...externalImports,
|
|
161
|
+
CacheModule,
|
|
162
|
+
UtilsModule
|
|
163
|
+
];
|
|
164
|
+
const module = {
|
|
165
|
+
module: IAMModule,
|
|
166
|
+
imports,
|
|
167
|
+
controllers,
|
|
168
|
+
providers,
|
|
169
|
+
exports: [
|
|
170
|
+
IAMConfigService,
|
|
171
|
+
IAMDataSourceProvider,
|
|
172
|
+
ActionService,
|
|
173
|
+
PermissionService,
|
|
174
|
+
PermissionCacheService,
|
|
175
|
+
PermissionEvaluatorHelper,
|
|
176
|
+
...permissionMode === IAMPermissionMode.RBAC || permissionMode === IAMPermissionMode.FULL ? [
|
|
177
|
+
RoleService
|
|
178
|
+
] : []
|
|
179
|
+
]
|
|
180
|
+
};
|
|
181
|
+
if (global) {
|
|
182
|
+
return {
|
|
183
|
+
...module,
|
|
184
|
+
global: true
|
|
185
|
+
};
|
|
186
|
+
}
|
|
187
|
+
return module;
|
|
188
|
+
}
|
|
189
|
+
static createAsyncProviders(options) {
|
|
190
|
+
if (options.useExisting || options.useFactory) {
|
|
191
|
+
return [
|
|
192
|
+
this.createAsyncOptionsProvider(options)
|
|
193
|
+
];
|
|
194
|
+
}
|
|
195
|
+
const useClass = options.useClass;
|
|
196
|
+
return [
|
|
197
|
+
this.createAsyncOptionsProvider(options),
|
|
198
|
+
{
|
|
199
|
+
provide: useClass,
|
|
200
|
+
useClass
|
|
201
|
+
}
|
|
202
|
+
];
|
|
203
|
+
}
|
|
204
|
+
static createAsyncOptionsProvider(options) {
|
|
205
|
+
if (options.useFactory) {
|
|
206
|
+
return {
|
|
207
|
+
provide: IAM_MODULE_OPTIONS,
|
|
208
|
+
useFactory: options.useFactory,
|
|
209
|
+
inject: options.inject || []
|
|
210
|
+
};
|
|
211
|
+
}
|
|
212
|
+
const inject = [
|
|
213
|
+
options.useClass || options.useExisting
|
|
214
|
+
];
|
|
215
|
+
return {
|
|
216
|
+
provide: IAM_MODULE_OPTIONS,
|
|
217
|
+
useFactory: async (optionsFactory)=>optionsFactory.createIAMOptions(),
|
|
218
|
+
inject
|
|
219
|
+
};
|
|
220
|
+
}
|
|
221
|
+
static forFeature(options = {}) {
|
|
222
|
+
return this.forRoot(options);
|
|
223
|
+
}
|
|
224
|
+
}
|
|
225
|
+
IAMModule = _ts_decorate([
|
|
226
|
+
Module({})
|
|
227
|
+
], IAMModule);
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export * from './iam.module';
|
|
@@ -0,0 +1,243 @@
|
|
|
1
|
+
function _define_property(obj, key, value) {
|
|
2
|
+
if (key in obj) {
|
|
3
|
+
Object.defineProperty(obj, key, {
|
|
4
|
+
value: value,
|
|
5
|
+
enumerable: true,
|
|
6
|
+
configurable: true,
|
|
7
|
+
writable: true
|
|
8
|
+
});
|
|
9
|
+
} else {
|
|
10
|
+
obj[key] = value;
|
|
11
|
+
}
|
|
12
|
+
return obj;
|
|
13
|
+
}
|
|
14
|
+
function _ts_decorate(decorators, target, key, desc) {
|
|
15
|
+
var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
|
|
16
|
+
if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
|
|
17
|
+
else for(var i = decorators.length - 1; i >= 0; i--)if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
|
|
18
|
+
return c > 3 && r && Object.defineProperty(target, key, r), r;
|
|
19
|
+
}
|
|
20
|
+
function _ts_metadata(k, v) {
|
|
21
|
+
if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v);
|
|
22
|
+
}
|
|
23
|
+
function _ts_param(paramIndex, decorator) {
|
|
24
|
+
return function(target, key) {
|
|
25
|
+
decorator(target, key, paramIndex);
|
|
26
|
+
};
|
|
27
|
+
}
|
|
28
|
+
import { RequestScopedApiService, HybridCache } from '@flusys/nestjs-shared/classes';
|
|
29
|
+
import { UtilsService } from '@flusys/nestjs-shared/modules';
|
|
30
|
+
import { Inject, Injectable, Logger, NotFoundException, Scope } from '@nestjs/common';
|
|
31
|
+
import { In } from 'typeorm';
|
|
32
|
+
import { Action } from '../entities/action.entity';
|
|
33
|
+
import { IAMConfigService } from './iam-config.service';
|
|
34
|
+
import { IAMDataSourceProvider } from './iam-datasource.provider';
|
|
35
|
+
import { PermissionService } from './permission.service';
|
|
36
|
+
export class ActionService extends RequestScopedApiService {
|
|
37
|
+
/**
|
|
38
|
+
* Resolve entity class for this service
|
|
39
|
+
* @returns Action entity class
|
|
40
|
+
*/ resolveEntity() {
|
|
41
|
+
return Action;
|
|
42
|
+
}
|
|
43
|
+
/**
|
|
44
|
+
* Get DataSource provider for this service
|
|
45
|
+
* @returns IAMDataSourceProvider instance
|
|
46
|
+
*/ getDataSourceProvider() {
|
|
47
|
+
return this.dataSourceProvider;
|
|
48
|
+
}
|
|
49
|
+
// ==================== Entity Conversion ====================
|
|
50
|
+
async convertSingleDtoToEntity(dto, _user) {
|
|
51
|
+
if (!('id' in dto) || !dto.id) {
|
|
52
|
+
return dto;
|
|
53
|
+
}
|
|
54
|
+
const existingAction = await this.repository.findOne({
|
|
55
|
+
where: {
|
|
56
|
+
id: dto.id
|
|
57
|
+
}
|
|
58
|
+
});
|
|
59
|
+
if (!existingAction) {
|
|
60
|
+
throw new NotFoundException(`Action with ID ${dto.id} not found`);
|
|
61
|
+
}
|
|
62
|
+
return {
|
|
63
|
+
...existingAction,
|
|
64
|
+
...dto
|
|
65
|
+
};
|
|
66
|
+
}
|
|
67
|
+
async getSelectQuery(query, _user, select) {
|
|
68
|
+
if (!select || !select.length) {
|
|
69
|
+
select = [
|
|
70
|
+
'id',
|
|
71
|
+
'name',
|
|
72
|
+
'code',
|
|
73
|
+
'description',
|
|
74
|
+
'actionType',
|
|
75
|
+
'permissionLogic',
|
|
76
|
+
'isActive',
|
|
77
|
+
'parentId',
|
|
78
|
+
'serial',
|
|
79
|
+
'createdAt'
|
|
80
|
+
];
|
|
81
|
+
}
|
|
82
|
+
const selectFields = select.map((field)=>`${this.entityName}.${field}`);
|
|
83
|
+
query.select(selectFields);
|
|
84
|
+
return {
|
|
85
|
+
query,
|
|
86
|
+
isRaw: false
|
|
87
|
+
};
|
|
88
|
+
}
|
|
89
|
+
async getGlobalSearchQuery(query, search, _user) {
|
|
90
|
+
query.andWhere('(action.name LIKE :search OR action.code LIKE :search OR action.description LIKE :search)', {
|
|
91
|
+
search: `%${search}%`
|
|
92
|
+
});
|
|
93
|
+
return {
|
|
94
|
+
query,
|
|
95
|
+
isRaw: false
|
|
96
|
+
};
|
|
97
|
+
}
|
|
98
|
+
/**
|
|
99
|
+
* Override: Convert entity to response DTO
|
|
100
|
+
*/ convertEntityToResponseDto(entity, _isRaw) {
|
|
101
|
+
return {
|
|
102
|
+
id: entity.id,
|
|
103
|
+
readOnly: entity.readOnly,
|
|
104
|
+
name: entity.name,
|
|
105
|
+
description: entity.description,
|
|
106
|
+
code: entity.code,
|
|
107
|
+
actionType: entity.actionType,
|
|
108
|
+
permissionLogic: entity.permissionLogic,
|
|
109
|
+
serial: entity.serial,
|
|
110
|
+
isActive: entity.isActive,
|
|
111
|
+
parentId: entity.parentId,
|
|
112
|
+
metadata: entity.metadata,
|
|
113
|
+
createdAt: entity.createdAt,
|
|
114
|
+
updatedAt: entity.updatedAt,
|
|
115
|
+
deletedAt: entity.deletedAt,
|
|
116
|
+
createdById: entity.createdById,
|
|
117
|
+
updatedById: entity.updatedById,
|
|
118
|
+
deletedById: entity.deletedById
|
|
119
|
+
};
|
|
120
|
+
}
|
|
121
|
+
/**
|
|
122
|
+
* Get actions available for permission assignment (filtered by company whitelist)
|
|
123
|
+
*
|
|
124
|
+
* @param user - Logged in user info
|
|
125
|
+
* @returns Array of actions with id, code, and name fields
|
|
126
|
+
*/ async getActionsForPermission(user) {
|
|
127
|
+
await this.ensureRepositoryInitialized();
|
|
128
|
+
if (!user) {
|
|
129
|
+
throw new Error('User is required for getActionsForPermission');
|
|
130
|
+
}
|
|
131
|
+
const selectFields = [
|
|
132
|
+
'id',
|
|
133
|
+
'code',
|
|
134
|
+
'name',
|
|
135
|
+
'description',
|
|
136
|
+
'actionType',
|
|
137
|
+
'permissionLogic',
|
|
138
|
+
'isActive',
|
|
139
|
+
'parentId',
|
|
140
|
+
'serial'
|
|
141
|
+
];
|
|
142
|
+
const enableCompanyFeature = this.iamConfigService.isCompanyFeatureEnabled();
|
|
143
|
+
if (enableCompanyFeature && user.companyId) {
|
|
144
|
+
const companyActionIds = await this.permissionService.getCompanyActionIds(user.companyId);
|
|
145
|
+
if (companyActionIds.length === 0) {
|
|
146
|
+
return [];
|
|
147
|
+
}
|
|
148
|
+
const actions = await this.repository.find({
|
|
149
|
+
where: {
|
|
150
|
+
id: In(companyActionIds)
|
|
151
|
+
},
|
|
152
|
+
select: selectFields
|
|
153
|
+
});
|
|
154
|
+
return actions.map((action)=>this.convertEntityToResponseDto(action, false));
|
|
155
|
+
}
|
|
156
|
+
const actions = await this.repository.find({
|
|
157
|
+
select: selectFields
|
|
158
|
+
});
|
|
159
|
+
return actions.map((action)=>this.convertEntityToResponseDto(action, false));
|
|
160
|
+
}
|
|
161
|
+
/**
|
|
162
|
+
* Get actions in hierarchical tree structure
|
|
163
|
+
*
|
|
164
|
+
* @param user - Logged in user info for company filtering
|
|
165
|
+
* @param search - Optional search term (name or code)
|
|
166
|
+
* @param isActive - Optional filter by active status
|
|
167
|
+
* @param withDeleted - Include deleted actions (default: false)
|
|
168
|
+
* @returns Array of root actions with nested children
|
|
169
|
+
*/ async getActionTree(user, search, isActive, withDeleted = false) {
|
|
170
|
+
await this.ensureRepositoryInitialized();
|
|
171
|
+
if (!user) {
|
|
172
|
+
throw new Error('User is required for getActionTree');
|
|
173
|
+
}
|
|
174
|
+
const query = this.repository.createQueryBuilder('action');
|
|
175
|
+
if (!withDeleted) {
|
|
176
|
+
query.andWhere('action.deletedAt IS NULL');
|
|
177
|
+
}
|
|
178
|
+
if (isActive !== undefined) {
|
|
179
|
+
query.andWhere('action.isActive = :isActive', {
|
|
180
|
+
isActive
|
|
181
|
+
});
|
|
182
|
+
}
|
|
183
|
+
if (search?.trim()) {
|
|
184
|
+
query.andWhere('(action.name LIKE :search OR action.code LIKE :search)', {
|
|
185
|
+
search: `%${search.trim()}%`
|
|
186
|
+
});
|
|
187
|
+
}
|
|
188
|
+
const actions = await query.orderBy('action.serial', 'ASC').getMany();
|
|
189
|
+
return this.buildActionTree(actions);
|
|
190
|
+
}
|
|
191
|
+
buildActionTree(actions) {
|
|
192
|
+
if (!actions?.length) {
|
|
193
|
+
return [];
|
|
194
|
+
}
|
|
195
|
+
const map = new Map();
|
|
196
|
+
const rootNodes = [];
|
|
197
|
+
for (const action of actions){
|
|
198
|
+
const treeNode = {
|
|
199
|
+
...this.convertEntityToResponseDto(action, false),
|
|
200
|
+
children: []
|
|
201
|
+
};
|
|
202
|
+
map.set(action.id, treeNode);
|
|
203
|
+
}
|
|
204
|
+
for (const action of actions){
|
|
205
|
+
const node = map.get(action.id);
|
|
206
|
+
if (!node) {
|
|
207
|
+
continue;
|
|
208
|
+
}
|
|
209
|
+
if (action.parentId && map.has(action.parentId)) {
|
|
210
|
+
const parent = map.get(action.parentId);
|
|
211
|
+
if (parent?.children) {
|
|
212
|
+
parent.children.push(node);
|
|
213
|
+
}
|
|
214
|
+
} else {
|
|
215
|
+
rootNodes.push(node);
|
|
216
|
+
}
|
|
217
|
+
}
|
|
218
|
+
return rootNodes;
|
|
219
|
+
}
|
|
220
|
+
// NOTE: @Inject() required for bundled code - type metadata may be lost during esbuild
|
|
221
|
+
constructor(cacheManager, utilsService, iamConfigService, dataSourceProvider, permissionService){
|
|
222
|
+
// Repository will be set asynchronously by RequestScopedApiService
|
|
223
|
+
super('action', null, cacheManager, utilsService, ActionService.name, true), _define_property(this, "cacheManager", void 0), _define_property(this, "utilsService", void 0), _define_property(this, "iamConfigService", void 0), _define_property(this, "dataSourceProvider", void 0), _define_property(this, "permissionService", void 0), _define_property(this, "logger", void 0), this.cacheManager = cacheManager, this.utilsService = utilsService, this.iamConfigService = iamConfigService, this.dataSourceProvider = dataSourceProvider, this.permissionService = permissionService, this.logger = new Logger(ActionService.name);
|
|
224
|
+
}
|
|
225
|
+
}
|
|
226
|
+
ActionService = _ts_decorate([
|
|
227
|
+
Injectable({
|
|
228
|
+
scope: Scope.REQUEST
|
|
229
|
+
}),
|
|
230
|
+
_ts_param(0, Inject('CACHE_INSTANCE')),
|
|
231
|
+
_ts_param(1, Inject(UtilsService)),
|
|
232
|
+
_ts_param(2, Inject(IAMConfigService)),
|
|
233
|
+
_ts_param(3, Inject(IAMDataSourceProvider)),
|
|
234
|
+
_ts_param(4, Inject(PermissionService)),
|
|
235
|
+
_ts_metadata("design:type", Function),
|
|
236
|
+
_ts_metadata("design:paramtypes", [
|
|
237
|
+
typeof HybridCache === "undefined" ? Object : HybridCache,
|
|
238
|
+
typeof UtilsService === "undefined" ? Object : UtilsService,
|
|
239
|
+
typeof IAMConfigService === "undefined" ? Object : IAMConfigService,
|
|
240
|
+
typeof IAMDataSourceProvider === "undefined" ? Object : IAMDataSourceProvider,
|
|
241
|
+
typeof PermissionService === "undefined" ? Object : PermissionService
|
|
242
|
+
])
|
|
243
|
+
], ActionService);
|
|
@@ -0,0 +1,97 @@
|
|
|
1
|
+
function _define_property(obj, key, value) {
|
|
2
|
+
if (key in obj) {
|
|
3
|
+
Object.defineProperty(obj, key, {
|
|
4
|
+
value: value,
|
|
5
|
+
enumerable: true,
|
|
6
|
+
configurable: true,
|
|
7
|
+
writable: true
|
|
8
|
+
});
|
|
9
|
+
} else {
|
|
10
|
+
obj[key] = value;
|
|
11
|
+
}
|
|
12
|
+
return obj;
|
|
13
|
+
}
|
|
14
|
+
function _ts_decorate(decorators, target, key, desc) {
|
|
15
|
+
var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
|
|
16
|
+
if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
|
|
17
|
+
else for(var i = decorators.length - 1; i >= 0; i--)if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
|
|
18
|
+
return c > 3 && r && Object.defineProperty(target, key, r), r;
|
|
19
|
+
}
|
|
20
|
+
function _ts_metadata(k, v) {
|
|
21
|
+
if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v);
|
|
22
|
+
}
|
|
23
|
+
function _ts_param(paramIndex, decorator) {
|
|
24
|
+
return function(target, key) {
|
|
25
|
+
decorator(target, key, paramIndex);
|
|
26
|
+
};
|
|
27
|
+
}
|
|
28
|
+
import { Inject, Injectable, Optional } from '@nestjs/common';
|
|
29
|
+
import { IAM_MODULE_OPTIONS } from '../config/iam.constants';
|
|
30
|
+
import { IAMPermissionMode } from '../enums/permission-type.enum';
|
|
31
|
+
import { PermissionModeHelper } from '../helpers';
|
|
32
|
+
import { IAMModuleOptions } from '../interfaces/iam-module-options.interface';
|
|
33
|
+
export class IAMConfigService {
|
|
34
|
+
// ==================== Database Mode ====================
|
|
35
|
+
/**
|
|
36
|
+
* Get database mode (single or multi-tenant)
|
|
37
|
+
*/ getDatabaseMode() {
|
|
38
|
+
return this.options.bootstrapAppConfig?.databaseMode ?? 'single';
|
|
39
|
+
}
|
|
40
|
+
/**
|
|
41
|
+
* Check if running in multi-tenant mode
|
|
42
|
+
*/ isMultiTenant() {
|
|
43
|
+
return this.getDatabaseMode() === 'multi-tenant';
|
|
44
|
+
}
|
|
45
|
+
// ==================== Company Feature ====================
|
|
46
|
+
/**
|
|
47
|
+
* Get enable company feature flag
|
|
48
|
+
*/ getEnableCompanyFeature() {
|
|
49
|
+
return this.options.bootstrapAppConfig?.enableCompanyFeature ?? false;
|
|
50
|
+
}
|
|
51
|
+
/**
|
|
52
|
+
* Check if company feature is enabled (alias for getEnableCompanyFeature)
|
|
53
|
+
*/ isCompanyFeatureEnabled() {
|
|
54
|
+
return this.getEnableCompanyFeature();
|
|
55
|
+
}
|
|
56
|
+
// ==================== Permission Mode ====================
|
|
57
|
+
/**
|
|
58
|
+
* Get permission mode from bootstrap config
|
|
59
|
+
*
|
|
60
|
+
* **Note:** Reads from bootstrapAppConfig (not runtime config)
|
|
61
|
+
* because permissionMode affects entity/controller registration.
|
|
62
|
+
*/ getPermissionMode() {
|
|
63
|
+
return PermissionModeHelper.fromString(this.options.bootstrapAppConfig?.permissionMode);
|
|
64
|
+
}
|
|
65
|
+
/**
|
|
66
|
+
* Check if RBAC (role-based) permissions are enabled
|
|
67
|
+
*/ isRbacEnabled() {
|
|
68
|
+
const mode = this.getPermissionMode();
|
|
69
|
+
return mode === IAMPermissionMode.RBAC || mode === IAMPermissionMode.FULL;
|
|
70
|
+
}
|
|
71
|
+
/**
|
|
72
|
+
* Check if direct (user-level) permissions are enabled
|
|
73
|
+
*/ isDirectPermissionEnabled() {
|
|
74
|
+
const mode = this.getPermissionMode();
|
|
75
|
+
return mode === IAMPermissionMode.DIRECT || mode === IAMPermissionMode.FULL;
|
|
76
|
+
}
|
|
77
|
+
// ==================== Options ====================
|
|
78
|
+
getOptions() {
|
|
79
|
+
return this.options;
|
|
80
|
+
}
|
|
81
|
+
constructor(injectedOptions){
|
|
82
|
+
_define_property(this, "options", void 0);
|
|
83
|
+
this.options = injectedOptions ?? {
|
|
84
|
+
global: false,
|
|
85
|
+
includeController: false
|
|
86
|
+
};
|
|
87
|
+
}
|
|
88
|
+
}
|
|
89
|
+
IAMConfigService = _ts_decorate([
|
|
90
|
+
Injectable(),
|
|
91
|
+
_ts_param(0, Optional()),
|
|
92
|
+
_ts_param(0, Inject(IAM_MODULE_OPTIONS)),
|
|
93
|
+
_ts_metadata("design:type", Function),
|
|
94
|
+
_ts_metadata("design:paramtypes", [
|
|
95
|
+
typeof IAMModuleOptions === "undefined" ? Object : IAMModuleOptions
|
|
96
|
+
])
|
|
97
|
+
], IAMConfigService);
|