@flusys/nestjs-iam 1.1.0-beta → 2.0.0
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 +285 -115
- package/cjs/controllers/action.controller.js +45 -2
- package/cjs/controllers/company-action-permission.controller.js +16 -10
- package/cjs/controllers/my-permission.controller.js +7 -3
- package/cjs/controllers/role-permission.controller.js +35 -17
- package/cjs/controllers/role.controller.js +46 -3
- package/cjs/controllers/user-action-permission.controller.js +26 -11
- package/cjs/dtos/action.dto.js +0 -27
- package/cjs/dtos/permission.dto.js +117 -27
- package/cjs/dtos/role.dto.js +0 -27
- package/cjs/entities/permission-base.entity.js +0 -12
- package/cjs/helpers/company-access.helper.js +19 -0
- package/cjs/helpers/index.js +1 -1
- package/cjs/interfaces/iam-module-options.interface.js +0 -14
- package/cjs/interfaces/index.js +0 -1
- package/cjs/modules/iam.module.js +50 -102
- package/cjs/services/action.service.js +30 -41
- package/cjs/services/iam-config.service.js +2 -5
- package/cjs/services/{iam-datasource.provider.js → iam-datasource.service.js} +33 -36
- package/cjs/services/index.js +1 -1
- package/cjs/services/permission-cache.service.js +31 -61
- package/cjs/services/permission.service.js +160 -188
- package/cjs/services/role.service.js +8 -8
- package/cjs/types/logic-node.type.js +0 -24
- package/controllers/company-action-permission.controller.d.ts +3 -3
- package/controllers/my-permission.controller.d.ts +2 -2
- package/controllers/role-permission.controller.d.ts +7 -5
- package/controllers/user-action-permission.controller.d.ts +6 -4
- package/dtos/action.dto.d.ts +0 -7
- package/dtos/permission.dto.d.ts +4 -0
- package/dtos/role.dto.d.ts +0 -7
- package/entities/permission-base.entity.d.ts +0 -4
- package/fesm/controllers/action.controller.js +47 -4
- package/fesm/controllers/company-action-permission.controller.js +18 -12
- package/fesm/controllers/index.js +1 -1
- package/fesm/controllers/my-permission.controller.js +7 -3
- package/fesm/controllers/role-permission.controller.js +37 -19
- package/fesm/controllers/role.controller.js +45 -2
- package/fesm/controllers/user-action-permission.controller.js +28 -13
- package/fesm/dtos/action.dto.js +0 -24
- package/fesm/dtos/permission.dto.js +117 -29
- package/fesm/dtos/role.dto.js +0 -24
- package/fesm/entities/permission-base.entity.js +0 -12
- package/fesm/helpers/company-access.helper.js +14 -0
- package/fesm/helpers/index.js +1 -1
- package/fesm/interfaces/iam-module-options.interface.js +3 -1
- package/fesm/interfaces/index.js +0 -1
- package/fesm/modules/iam.module.js +52 -104
- package/fesm/services/action.service.js +32 -43
- package/fesm/services/iam-config.service.js +2 -5
- package/fesm/services/{iam-datasource.provider.js → iam-datasource.service.js} +31 -34
- package/fesm/services/index.js +1 -1
- package/fesm/services/permission-cache.service.js +31 -61
- package/fesm/services/permission.service.js +161 -189
- package/fesm/services/role.service.js +8 -8
- package/fesm/types/logic-node.type.js +1 -10
- package/helpers/company-access.helper.d.ts +3 -0
- package/helpers/index.d.ts +1 -1
- package/interfaces/iam-module-options.interface.d.ts +9 -1
- package/interfaces/index.d.ts +0 -1
- package/modules/iam.module.d.ts +2 -2
- package/package.json +3 -3
- package/services/action.service.d.ts +6 -4
- package/services/iam-config.service.d.ts +2 -2
- package/services/{iam-datasource.provider.d.ts → iam-datasource.service.d.ts} +4 -5
- package/services/index.d.ts +1 -1
- package/services/permission-cache.service.d.ts +4 -6
- package/services/permission.service.d.ts +8 -4
- package/services/role.service.d.ts +3 -3
- package/types/logic-node.type.d.ts +0 -8
- package/cjs/helpers/permission-evaluator.helper.js +0 -175
- package/cjs/interfaces/iam-module-async-options.interface.js +0 -4
- package/fesm/helpers/permission-evaluator.helper.js +0 -165
- package/fesm/interfaces/iam-module-async-options.interface.js +0 -3
- package/helpers/permission-evaluator.helper.d.ts +0 -26
- package/interfaces/iam-module-async-options.interface.d.ts +0 -11
|
@@ -4,17 +4,16 @@ function _ts_decorate(decorators, target, key, desc) {
|
|
|
4
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
5
|
return c > 3 && r && Object.defineProperty(target, key, r), r;
|
|
6
6
|
}
|
|
7
|
+
import { PERMISSION_GUARD_CONFIG } from '@flusys/nestjs-shared';
|
|
7
8
|
import { CacheModule, UtilsModule } from '@flusys/nestjs-shared/modules';
|
|
8
|
-
import { Module
|
|
9
|
-
import { getRepositoryToken } from '@nestjs/typeorm';
|
|
9
|
+
import { Module } from '@nestjs/common';
|
|
10
10
|
import { IAM_MODULE_OPTIONS } from '../config/iam.constants';
|
|
11
11
|
import { ActionController, CompanyActionPermissionController, MyPermissionController, RoleController, RolePermissionController, UserActionPermissionController } from '../controllers';
|
|
12
|
-
import { Action, Role, RoleWithCompany, UserIamPermission, UserIamPermissionWithCompany } from '../entities';
|
|
13
12
|
import { IAMPermissionMode } from '../enums/permission-type.enum';
|
|
14
|
-
import {
|
|
13
|
+
import { PermissionModeHelper } from '../helpers';
|
|
15
14
|
import { ActionService, PermissionService, RoleService } from '../services';
|
|
16
15
|
import { IAMConfigService } from '../services/iam-config.service';
|
|
17
|
-
import {
|
|
16
|
+
import { IAMDataSourceService } from '../services/iam-datasource.service';
|
|
18
17
|
import { PermissionCacheService } from '../services/permission-cache.service';
|
|
19
18
|
export class IAMModule {
|
|
20
19
|
static getControllers(permissionMode, enableCompanyFeature) {
|
|
@@ -43,33 +42,11 @@ export class IAMModule {
|
|
|
43
42
|
}
|
|
44
43
|
return baseControllers;
|
|
45
44
|
}
|
|
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
45
|
static getServices(permissionMode) {
|
|
68
46
|
const services = [
|
|
69
47
|
ActionService,
|
|
70
48
|
PermissionService,
|
|
71
|
-
PermissionCacheService
|
|
72
|
-
PermissionEvaluatorHelper
|
|
49
|
+
PermissionCacheService
|
|
73
50
|
];
|
|
74
51
|
// RoleService - Only for RBAC or FULL mode
|
|
75
52
|
if (permissionMode === IAMPermissionMode.RBAC || permissionMode === IAMPermissionMode.FULL) {
|
|
@@ -77,30 +54,32 @@ export class IAMModule {
|
|
|
77
54
|
}
|
|
78
55
|
return services;
|
|
79
56
|
}
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
57
|
+
static getPermissionGuardConfigProvider(enableCompanyFeature) {
|
|
58
|
+
return {
|
|
59
|
+
provide: PERMISSION_GUARD_CONFIG,
|
|
60
|
+
useValue: {
|
|
61
|
+
enableCompanyFeature
|
|
62
|
+
}
|
|
63
|
+
};
|
|
64
|
+
}
|
|
65
|
+
static getExports(permissionMode) {
|
|
66
|
+
const baseExports = [
|
|
67
|
+
IAMConfigService,
|
|
68
|
+
IAMDataSourceService,
|
|
69
|
+
ActionService,
|
|
70
|
+
PermissionService,
|
|
71
|
+
PermissionCacheService,
|
|
72
|
+
PERMISSION_GUARD_CONFIG
|
|
73
|
+
];
|
|
74
|
+
if (permissionMode === IAMPermissionMode.RBAC || permissionMode === IAMPermissionMode.FULL) {
|
|
75
|
+
baseExports.push(RoleService);
|
|
76
|
+
}
|
|
77
|
+
return baseExports;
|
|
95
78
|
}
|
|
96
79
|
static forRoot(options = {}) {
|
|
97
80
|
const { global = false, includeController = false } = options;
|
|
98
|
-
const databaseMode = options.bootstrapAppConfig?.databaseMode;
|
|
99
81
|
const enableCompanyFeature = options.bootstrapAppConfig?.enableCompanyFeature ?? false;
|
|
100
|
-
// Read permissionMode from bootstrap config using helper
|
|
101
82
|
const permissionMode = PermissionModeHelper.fromString(options.bootstrapAppConfig?.permissionMode);
|
|
102
|
-
const isMultiTenant = databaseMode === 'multi-tenant';
|
|
103
|
-
const entities = this.getEntities(permissionMode, enableCompanyFeature);
|
|
104
83
|
const controllers = includeController ? this.getControllers(permissionMode, enableCompanyFeature) : [];
|
|
105
84
|
const providers = [
|
|
106
85
|
{
|
|
@@ -108,83 +87,52 @@ export class IAMModule {
|
|
|
108
87
|
useValue: options
|
|
109
88
|
},
|
|
110
89
|
IAMConfigService,
|
|
111
|
-
|
|
112
|
-
...this.getServices(permissionMode)
|
|
113
|
-
|
|
114
|
-
const imports = [
|
|
115
|
-
CacheModule,
|
|
116
|
-
UtilsModule
|
|
90
|
+
IAMDataSourceService,
|
|
91
|
+
...this.getServices(permissionMode),
|
|
92
|
+
this.getPermissionGuardConfigProvider(enableCompanyFeature)
|
|
117
93
|
];
|
|
118
94
|
const module = {
|
|
119
95
|
module: IAMModule,
|
|
120
|
-
imports
|
|
96
|
+
imports: [
|
|
97
|
+
CacheModule,
|
|
98
|
+
UtilsModule
|
|
99
|
+
],
|
|
121
100
|
controllers,
|
|
122
101
|
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
|
-
]
|
|
102
|
+
exports: this.getExports(permissionMode)
|
|
134
103
|
};
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
};
|
|
140
|
-
}
|
|
141
|
-
return module;
|
|
104
|
+
return global ? {
|
|
105
|
+
...module,
|
|
106
|
+
global: true
|
|
107
|
+
} : module;
|
|
142
108
|
}
|
|
143
109
|
static forRootAsync(asyncOptions) {
|
|
144
110
|
const { global = false, includeController = false, imports: externalImports = [] } = asyncOptions;
|
|
145
|
-
const databaseMode = asyncOptions.bootstrapAppConfig?.databaseMode;
|
|
146
111
|
const enableCompanyFeature = asyncOptions.bootstrapAppConfig?.enableCompanyFeature ?? false;
|
|
147
|
-
// Read permissionMode from bootstrap config using helper
|
|
148
112
|
const permissionMode = PermissionModeHelper.fromString(asyncOptions.bootstrapAppConfig?.permissionMode);
|
|
149
|
-
const isMultiTenant = databaseMode === 'multi-tenant';
|
|
150
|
-
const entities = this.getEntities(permissionMode, enableCompanyFeature);
|
|
151
113
|
const controllers = includeController ? this.getControllers(permissionMode, enableCompanyFeature) : [];
|
|
152
|
-
const asyncProviders = this.createAsyncProviders(asyncOptions);
|
|
153
114
|
const providers = [
|
|
154
|
-
...
|
|
115
|
+
...this.createAsyncProviders(asyncOptions),
|
|
155
116
|
IAMConfigService,
|
|
156
|
-
|
|
157
|
-
...this.getServices(permissionMode)
|
|
158
|
-
|
|
159
|
-
const imports = [
|
|
160
|
-
...externalImports,
|
|
161
|
-
CacheModule,
|
|
162
|
-
UtilsModule
|
|
117
|
+
IAMDataSourceService,
|
|
118
|
+
...this.getServices(permissionMode),
|
|
119
|
+
this.getPermissionGuardConfigProvider(enableCompanyFeature)
|
|
163
120
|
];
|
|
164
121
|
const module = {
|
|
165
122
|
module: IAMModule,
|
|
166
|
-
imports
|
|
123
|
+
imports: [
|
|
124
|
+
...externalImports,
|
|
125
|
+
CacheModule,
|
|
126
|
+
UtilsModule
|
|
127
|
+
],
|
|
167
128
|
controllers,
|
|
168
129
|
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
|
-
]
|
|
130
|
+
exports: this.getExports(permissionMode)
|
|
180
131
|
};
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
};
|
|
186
|
-
}
|
|
187
|
-
return module;
|
|
132
|
+
return global ? {
|
|
133
|
+
...module,
|
|
134
|
+
global: true
|
|
135
|
+
} : module;
|
|
188
136
|
}
|
|
189
137
|
static createAsyncProviders(options) {
|
|
190
138
|
if (options.useExisting || options.useFactory) {
|
|
@@ -25,13 +25,13 @@ function _ts_param(paramIndex, decorator) {
|
|
|
25
25
|
decorator(target, key, paramIndex);
|
|
26
26
|
};
|
|
27
27
|
}
|
|
28
|
-
import {
|
|
28
|
+
import { HybridCache, RequestScopedApiService } from '@flusys/nestjs-shared/classes';
|
|
29
29
|
import { UtilsService } from '@flusys/nestjs-shared/modules';
|
|
30
|
-
import { Inject, Injectable, Scope } from '@nestjs/common';
|
|
30
|
+
import { BadRequestException, Inject, Injectable, Scope } from '@nestjs/common';
|
|
31
31
|
import { In } from 'typeorm';
|
|
32
32
|
import { Action } from '../entities/action.entity';
|
|
33
33
|
import { IAMConfigService } from './iam-config.service';
|
|
34
|
-
import {
|
|
34
|
+
import { IAMDataSourceService } from './iam-datasource.service';
|
|
35
35
|
import { PermissionService } from './permission.service';
|
|
36
36
|
export class ActionService extends RequestScopedApiService {
|
|
37
37
|
resolveEntity() {
|
|
@@ -93,55 +93,33 @@ export class ActionService extends RequestScopedApiService {
|
|
|
93
93
|
deletedById: entity.deletedById
|
|
94
94
|
};
|
|
95
95
|
}
|
|
96
|
-
|
|
97
|
-
/** Get actions available for permission assignment (filtered by company whitelist) */ async getActionsForPermission(user) {
|
|
98
|
-
await this.ensureRepositoryInitialized();
|
|
96
|
+
requireUser(user, methodName) {
|
|
99
97
|
if (!user) {
|
|
100
|
-
throw new
|
|
98
|
+
throw new BadRequestException(`User is required for ${methodName}`);
|
|
101
99
|
}
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
'permissionLogic',
|
|
109
|
-
'isActive',
|
|
110
|
-
'parentId',
|
|
111
|
-
'serial'
|
|
112
|
-
];
|
|
113
|
-
const enableCompanyFeature = this.iamConfigService.isCompanyFeatureEnabled();
|
|
114
|
-
if (enableCompanyFeature && user.companyId) {
|
|
100
|
+
}
|
|
101
|
+
/** Get actions available for permission assignment (filtered by company whitelist) */ async getActionsForPermission(user) {
|
|
102
|
+
await this.ensureRepositoryInitialized();
|
|
103
|
+
this.requireUser(user, 'getActionsForPermission');
|
|
104
|
+
let whereClause = {};
|
|
105
|
+
if (this.iamConfigService.isCompanyFeatureEnabled() && user.companyId) {
|
|
115
106
|
const companyActionIds = await this.permissionService.getCompanyActionIds(user.companyId);
|
|
116
107
|
if (companyActionIds.length === 0) {
|
|
117
108
|
return [];
|
|
118
109
|
}
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
},
|
|
123
|
-
select: selectFields
|
|
124
|
-
});
|
|
125
|
-
return actions.map((action)=>this.convertEntityToResponseDto(action, false));
|
|
110
|
+
whereClause = {
|
|
111
|
+
id: In(companyActionIds)
|
|
112
|
+
};
|
|
126
113
|
}
|
|
127
114
|
const actions = await this.repository.find({
|
|
128
|
-
|
|
115
|
+
where: whereClause,
|
|
116
|
+
select: this.actionSelectFields
|
|
129
117
|
});
|
|
130
118
|
return actions.map((action)=>this.convertEntityToResponseDto(action, false));
|
|
131
119
|
}
|
|
132
|
-
/**
|
|
133
|
-
* Get actions in hierarchical tree structure
|
|
134
|
-
*
|
|
135
|
-
* @param user - Logged in user info for company filtering
|
|
136
|
-
* @param search - Optional search term (name or code)
|
|
137
|
-
* @param isActive - Optional filter by active status
|
|
138
|
-
* @param withDeleted - Include deleted actions (default: false)
|
|
139
|
-
* @returns Array of root actions with nested children
|
|
140
|
-
*/ async getActionTree(user, search, isActive, withDeleted = false) {
|
|
120
|
+
/** Get actions in hierarchical tree structure */ async getActionTree(user, search, isActive, withDeleted = false) {
|
|
141
121
|
await this.ensureRepositoryInitialized();
|
|
142
|
-
|
|
143
|
-
throw new Error('User is required for getActionTree');
|
|
144
|
-
}
|
|
122
|
+
this.requireUser(user, 'getActionTree');
|
|
145
123
|
const query = this.repository.createQueryBuilder('action');
|
|
146
124
|
if (!withDeleted) {
|
|
147
125
|
query.andWhere('action.deletedAt IS NULL');
|
|
@@ -189,7 +167,18 @@ export class ActionService extends RequestScopedApiService {
|
|
|
189
167
|
return rootNodes;
|
|
190
168
|
}
|
|
191
169
|
constructor(cacheManager, utilsService, iamConfigService, dataSourceProvider, permissionService){
|
|
192
|
-
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),
|
|
170
|
+
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), // Custom Methods
|
|
171
|
+
_define_property(this, "actionSelectFields", void 0), this.cacheManager = cacheManager, this.utilsService = utilsService, this.iamConfigService = iamConfigService, this.dataSourceProvider = dataSourceProvider, this.permissionService = permissionService, this.actionSelectFields = [
|
|
172
|
+
'id',
|
|
173
|
+
'code',
|
|
174
|
+
'name',
|
|
175
|
+
'description',
|
|
176
|
+
'actionType',
|
|
177
|
+
'permissionLogic',
|
|
178
|
+
'isActive',
|
|
179
|
+
'parentId',
|
|
180
|
+
'serial'
|
|
181
|
+
];
|
|
193
182
|
}
|
|
194
183
|
}
|
|
195
184
|
ActionService = _ts_decorate([
|
|
@@ -199,14 +188,14 @@ ActionService = _ts_decorate([
|
|
|
199
188
|
_ts_param(0, Inject('CACHE_INSTANCE')),
|
|
200
189
|
_ts_param(1, Inject(UtilsService)),
|
|
201
190
|
_ts_param(2, Inject(IAMConfigService)),
|
|
202
|
-
_ts_param(3, Inject(
|
|
191
|
+
_ts_param(3, Inject(IAMDataSourceService)),
|
|
203
192
|
_ts_param(4, Inject(PermissionService)),
|
|
204
193
|
_ts_metadata("design:type", Function),
|
|
205
194
|
_ts_metadata("design:paramtypes", [
|
|
206
195
|
typeof HybridCache === "undefined" ? Object : HybridCache,
|
|
207
196
|
typeof UtilsService === "undefined" ? Object : UtilsService,
|
|
208
197
|
typeof IAMConfigService === "undefined" ? Object : IAMConfigService,
|
|
209
|
-
typeof
|
|
198
|
+
typeof IAMDataSourceService === "undefined" ? Object : IAMDataSourceService,
|
|
210
199
|
typeof PermissionService === "undefined" ? Object : PermissionService
|
|
211
200
|
])
|
|
212
201
|
], ActionService);
|
|
@@ -38,12 +38,9 @@ export class IAMConfigService {
|
|
|
38
38
|
isMultiTenant() {
|
|
39
39
|
return this.getDatabaseMode() === 'multi-tenant';
|
|
40
40
|
}
|
|
41
|
-
//
|
|
42
|
-
getEnableCompanyFeature() {
|
|
43
|
-
return this.options.bootstrapAppConfig?.enableCompanyFeature ?? false;
|
|
44
|
-
}
|
|
41
|
+
// Feature Flags
|
|
45
42
|
isCompanyFeatureEnabled() {
|
|
46
|
-
return this.
|
|
43
|
+
return this.options.bootstrapAppConfig?.enableCompanyFeature ?? false;
|
|
47
44
|
}
|
|
48
45
|
// Permission Mode
|
|
49
46
|
getPermissionMode() {
|
|
@@ -29,9 +29,9 @@ import { MultiTenantDataSourceService } from '@flusys/nestjs-shared/modules';
|
|
|
29
29
|
import { Inject, Injectable, Logger, Optional, Scope } from '@nestjs/common';
|
|
30
30
|
import { REQUEST } from '@nestjs/core';
|
|
31
31
|
import { Request } from 'express';
|
|
32
|
-
import {
|
|
33
|
-
import {
|
|
34
|
-
export class
|
|
32
|
+
import { PermissionModeHelper } from '../helpers';
|
|
33
|
+
import { IAMConfigService } from './iam-config.service';
|
|
34
|
+
export class IAMDataSourceService extends MultiTenantDataSourceService {
|
|
35
35
|
// Factory Methods
|
|
36
36
|
static buildParentOptions(options) {
|
|
37
37
|
return {
|
|
@@ -42,20 +42,17 @@ export class IAMDataSourceProvider extends MultiTenantDataSourceService {
|
|
|
42
42
|
};
|
|
43
43
|
}
|
|
44
44
|
// Feature Flags
|
|
45
|
-
getEnableCompanyFeature() {
|
|
46
|
-
return this.iamOptions.bootstrapAppConfig?.enableCompanyFeature ?? false;
|
|
47
|
-
}
|
|
48
45
|
getEnableCompanyFeatureForTenant(tenant) {
|
|
49
|
-
return tenant?.enableCompanyFeature ?? this.
|
|
46
|
+
return tenant?.enableCompanyFeature ?? this.configService.isCompanyFeatureEnabled();
|
|
50
47
|
}
|
|
51
48
|
getEnableCompanyFeatureForCurrentTenant() {
|
|
52
49
|
return this.getEnableCompanyFeatureForTenant(this.getCurrentTenant() ?? undefined);
|
|
53
50
|
}
|
|
54
51
|
// Entity Management
|
|
55
52
|
async getIAMEntities() {
|
|
56
|
-
const {
|
|
53
|
+
const { getIAMEntitiesByConfig } = await import('../entities');
|
|
57
54
|
const enableCompanyFeature = this.getEnableCompanyFeatureForCurrentTenant();
|
|
58
|
-
const permissionMode = this.
|
|
55
|
+
const permissionMode = PermissionModeHelper.toString(this.configService.getPermissionMode());
|
|
59
56
|
return getIAMEntitiesByConfig(enableCompanyFeature, permissionMode);
|
|
60
57
|
}
|
|
61
58
|
// Overrides
|
|
@@ -64,9 +61,9 @@ export class IAMDataSourceProvider extends MultiTenantDataSourceService {
|
|
|
64
61
|
return super.createDataSourceFromConfig(config, entities);
|
|
65
62
|
}
|
|
66
63
|
async getSingleDataSource() {
|
|
67
|
-
if (!
|
|
68
|
-
if (
|
|
69
|
-
return
|
|
64
|
+
if (!IAMDataSourceService.singleDataSource) {
|
|
65
|
+
if (IAMDataSourceService.singleConnectionLock) {
|
|
66
|
+
return IAMDataSourceService.singleConnectionLock;
|
|
70
67
|
}
|
|
71
68
|
const lockPromise = (async ()=>{
|
|
72
69
|
const config = this.getDefaultDatabaseConfig();
|
|
@@ -74,63 +71,63 @@ export class IAMDataSourceProvider extends MultiTenantDataSourceService {
|
|
|
74
71
|
throw new Error('Default database config is not available');
|
|
75
72
|
}
|
|
76
73
|
const ds = await this.createDataSourceFromConfig(config);
|
|
77
|
-
|
|
78
|
-
|
|
74
|
+
IAMDataSourceService.singleDataSource = ds;
|
|
75
|
+
IAMDataSourceService.initialized = true;
|
|
79
76
|
return ds;
|
|
80
77
|
})();
|
|
81
|
-
|
|
78
|
+
IAMDataSourceService.singleConnectionLock = lockPromise;
|
|
82
79
|
try {
|
|
83
80
|
return await lockPromise;
|
|
84
81
|
} finally{
|
|
85
|
-
|
|
82
|
+
IAMDataSourceService.singleConnectionLock = null;
|
|
86
83
|
}
|
|
87
84
|
}
|
|
88
|
-
return
|
|
85
|
+
return IAMDataSourceService.singleDataSource;
|
|
89
86
|
}
|
|
90
87
|
async getOrCreateTenantConnection(tenant) {
|
|
91
88
|
// Return existing initialized connection from IAM-specific cache
|
|
92
|
-
const existing =
|
|
89
|
+
const existing = IAMDataSourceService.tenantConnections.get(tenant.id);
|
|
93
90
|
if (existing?.isInitialized) {
|
|
94
91
|
return existing;
|
|
95
92
|
}
|
|
96
93
|
// If another request is creating this tenant's connection, wait for it
|
|
97
|
-
const pendingConnection =
|
|
94
|
+
const pendingConnection = IAMDataSourceService.connectionLocks.get(tenant.id);
|
|
98
95
|
if (pendingConnection) {
|
|
99
96
|
return pendingConnection;
|
|
100
97
|
}
|
|
101
98
|
// Create connection with lock to prevent race conditions
|
|
102
99
|
const config = this.buildTenantDatabaseConfig(tenant);
|
|
103
100
|
const connectionPromise = this.createDataSourceFromConfig(config);
|
|
104
|
-
|
|
101
|
+
IAMDataSourceService.connectionLocks.set(tenant.id, connectionPromise);
|
|
105
102
|
try {
|
|
106
103
|
const dataSource = await connectionPromise;
|
|
107
|
-
|
|
104
|
+
IAMDataSourceService.tenantConnections.set(tenant.id, dataSource);
|
|
108
105
|
return dataSource;
|
|
109
106
|
} finally{
|
|
110
|
-
|
|
107
|
+
IAMDataSourceService.connectionLocks.delete(tenant.id);
|
|
111
108
|
}
|
|
112
109
|
}
|
|
113
|
-
constructor(
|
|
114
|
-
super(
|
|
110
|
+
constructor(configService, request){
|
|
111
|
+
super(IAMDataSourceService.buildParentOptions(configService.getOptions()), request), _define_property(this, "configService", void 0), _define_property(this, "logger", void 0), this.configService = configService, this.logger = new Logger(IAMDataSourceService.name);
|
|
115
112
|
}
|
|
116
113
|
}
|
|
117
114
|
// Override parent's static properties to have IAM-specific cache
|
|
118
|
-
_define_property(
|
|
119
|
-
_define_property(
|
|
120
|
-
_define_property(
|
|
121
|
-
_define_property(
|
|
122
|
-
_define_property(
|
|
123
|
-
_define_property(
|
|
124
|
-
|
|
115
|
+
_define_property(IAMDataSourceService, "tenantConnections", new Map());
|
|
116
|
+
_define_property(IAMDataSourceService, "singleDataSource", null);
|
|
117
|
+
_define_property(IAMDataSourceService, "tenantsRegistry", new Map());
|
|
118
|
+
_define_property(IAMDataSourceService, "initialized", false);
|
|
119
|
+
_define_property(IAMDataSourceService, "connectionLocks", new Map());
|
|
120
|
+
_define_property(IAMDataSourceService, "singleConnectionLock", null);
|
|
121
|
+
IAMDataSourceService = _ts_decorate([
|
|
125
122
|
Injectable({
|
|
126
123
|
scope: Scope.REQUEST
|
|
127
124
|
}),
|
|
128
|
-
_ts_param(0, Inject(
|
|
125
|
+
_ts_param(0, Inject(IAMConfigService)),
|
|
129
126
|
_ts_param(1, Optional()),
|
|
130
127
|
_ts_param(1, Inject(REQUEST)),
|
|
131
128
|
_ts_metadata("design:type", Function),
|
|
132
129
|
_ts_metadata("design:paramtypes", [
|
|
133
|
-
typeof
|
|
130
|
+
typeof IAMConfigService === "undefined" ? Object : IAMConfigService,
|
|
134
131
|
typeof Request === "undefined" ? Object : Request
|
|
135
132
|
])
|
|
136
|
-
],
|
|
133
|
+
], IAMDataSourceService);
|
package/fesm/services/index.js
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
export * from './action.service';
|
|
2
2
|
export * from './iam-config.service';
|
|
3
|
-
export * from './iam-datasource.
|
|
3
|
+
export * from './iam-datasource.service';
|
|
4
4
|
export * from './permission-cache.service';
|
|
5
5
|
export * from './permission.service';
|
|
6
6
|
export * from './role.service';
|