@illustrisinteractive/sentinel-nest 0.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.
Files changed (167) hide show
  1. package/.prettierrc +4 -0
  2. package/README.md +98 -0
  3. package/config/config.json +24 -0
  4. package/dist/bin/commit.d.ts +1 -0
  5. package/dist/bin/commit.js +136 -0
  6. package/dist/bin/commit.js.map +1 -0
  7. package/dist/bin/init.d.ts +2 -0
  8. package/dist/bin/init.js +179 -0
  9. package/dist/bin/init.js.map +1 -0
  10. package/dist/bin/migrations/1-create-permission.d.ts +6 -0
  11. package/dist/bin/migrations/1-create-permission.js +30 -0
  12. package/dist/bin/migrations/1-create-permission.js.map +1 -0
  13. package/dist/bin/migrations/2-create-role.d.ts +2 -0
  14. package/dist/bin/migrations/2-create-role.js +29 -0
  15. package/dist/bin/migrations/2-create-role.js.map +1 -0
  16. package/dist/bin/migrations/3-create-rolepermissions.d.ts +2 -0
  17. package/dist/bin/migrations/3-create-rolepermissions.js +46 -0
  18. package/dist/bin/migrations/3-create-rolepermissions.js.map +1 -0
  19. package/dist/bin/migrations/4-create-model-roles.d.ts +2 -0
  20. package/dist/bin/migrations/4-create-model-roles.js +46 -0
  21. package/dist/bin/migrations/4-create-model-roles.js.map +1 -0
  22. package/dist/bin/resource.d.ts +1 -0
  23. package/dist/bin/resource.js +91 -0
  24. package/dist/bin/resource.js.map +1 -0
  25. package/dist/bin/sentinel.d.ts +2 -0
  26. package/dist/bin/sentinel.js +52 -0
  27. package/dist/bin/sentinel.js.map +1 -0
  28. package/dist/models/SecuredResource.d.ts +8 -0
  29. package/dist/models/SecuredResource.js +9 -0
  30. package/dist/models/SecuredResource.js.map +1 -0
  31. package/dist/models/SentinelConfig.d.ts +7 -0
  32. package/dist/models/SentinelConfig.js +3 -0
  33. package/dist/models/SentinelConfig.js.map +1 -0
  34. package/dist/models/sequelize/PermissionKey.d.ts +7 -0
  35. package/dist/models/sequelize/PermissionKey.js +39 -0
  36. package/dist/models/sequelize/PermissionKey.js.map +1 -0
  37. package/dist/prisma.config.d.ts +3 -0
  38. package/dist/prisma.config.js +14 -0
  39. package/dist/prisma.config.js.map +1 -0
  40. package/dist/src/can.decorator.d.ts +2 -0
  41. package/dist/src/can.decorator.js +6 -0
  42. package/dist/src/can.decorator.js.map +1 -0
  43. package/dist/src/generated/prisma/browser.d.ts +10 -0
  44. package/dist/src/generated/prisma/browser.js +44 -0
  45. package/dist/src/generated/prisma/browser.js.map +1 -0
  46. package/dist/src/generated/prisma/client.d.ts +14 -0
  47. package/dist/src/generated/prisma/client.js +46 -0
  48. package/dist/src/generated/prisma/client.js.map +1 -0
  49. package/dist/src/generated/prisma/commonInputTypes.d.ts +263 -0
  50. package/dist/src/generated/prisma/commonInputTypes.js +3 -0
  51. package/dist/src/generated/prisma/commonInputTypes.js.map +1 -0
  52. package/dist/src/generated/prisma/enums.d.ts +1 -0
  53. package/dist/src/generated/prisma/enums.js +3 -0
  54. package/dist/src/generated/prisma/enums.js.map +1 -0
  55. package/dist/src/generated/prisma/internal/class.d.ts +50 -0
  56. package/dist/src/generated/prisma/internal/class.js +75 -0
  57. package/dist/src/generated/prisma/internal/class.js.map +1 -0
  58. package/dist/src/generated/prisma/internal/prismaNamespace.d.ts +778 -0
  59. package/dist/src/generated/prisma/internal/prismaNamespace.js +128 -0
  60. package/dist/src/generated/prisma/internal/prismaNamespace.js.map +1 -0
  61. package/dist/src/generated/prisma/internal/prismaNamespaceBrowser.d.ts +88 -0
  62. package/dist/src/generated/prisma/internal/prismaNamespaceBrowser.js +112 -0
  63. package/dist/src/generated/prisma/internal/prismaNamespaceBrowser.js.map +1 -0
  64. package/dist/src/generated/prisma/models/ModelHasRoles.d.ts +691 -0
  65. package/dist/src/generated/prisma/models/ModelHasRoles.js +3 -0
  66. package/dist/src/generated/prisma/models/ModelHasRoles.js.map +1 -0
  67. package/dist/src/generated/prisma/models/PermissionKeys.d.ts +547 -0
  68. package/dist/src/generated/prisma/models/PermissionKeys.js +3 -0
  69. package/dist/src/generated/prisma/models/PermissionKeys.js.map +1 -0
  70. package/dist/src/generated/prisma/models/RoleHasPermissions.d.ts +675 -0
  71. package/dist/src/generated/prisma/models/RoleHasPermissions.js +3 -0
  72. package/dist/src/generated/prisma/models/RoleHasPermissions.js.map +1 -0
  73. package/dist/src/generated/prisma/models/Roles.d.ts +582 -0
  74. package/dist/src/generated/prisma/models/Roles.js +3 -0
  75. package/dist/src/generated/prisma/models/Roles.js.map +1 -0
  76. package/dist/src/generated/prisma/models/SequelizeMeta.d.ts +289 -0
  77. package/dist/src/generated/prisma/models/SequelizeMeta.js +3 -0
  78. package/dist/src/generated/prisma/models/SequelizeMeta.js.map +1 -0
  79. package/dist/src/generated/prisma/models/Users.d.ts +572 -0
  80. package/dist/src/generated/prisma/models/Users.js +3 -0
  81. package/dist/src/generated/prisma/models/Users.js.map +1 -0
  82. package/dist/src/generated/prisma/models.d.ts +7 -0
  83. package/dist/src/generated/prisma/models.js +3 -0
  84. package/dist/src/generated/prisma/models.js.map +1 -0
  85. package/dist/src/main.d.ts +6 -0
  86. package/dist/src/main.js +23 -0
  87. package/dist/src/main.js.map +1 -0
  88. package/dist/src/models/SecuredResource.d.ts +8 -0
  89. package/dist/src/models/SecuredResource.js +9 -0
  90. package/dist/src/models/SecuredResource.js.map +1 -0
  91. package/dist/src/models/SentinelConfig.d.ts +7 -0
  92. package/dist/src/models/SentinelConfig.js +3 -0
  93. package/dist/src/models/SentinelConfig.js.map +1 -0
  94. package/dist/src/models/SentinelModel.d.ts +35 -0
  95. package/dist/src/models/SentinelModel.js +3 -0
  96. package/dist/src/models/SentinelModel.js.map +1 -0
  97. package/dist/src/models/SentinelModuleOptions.d.ts +7 -0
  98. package/dist/src/models/SentinelModuleOptions.js +3 -0
  99. package/dist/src/models/SentinelModuleOptions.js.map +1 -0
  100. package/dist/src/prisma.service.d.ts +4 -0
  101. package/dist/src/prisma.service.js +29 -0
  102. package/dist/src/prisma.service.js.map +1 -0
  103. package/dist/src/sentinel.guard.d.ts +9 -0
  104. package/dist/src/sentinel.guard.js +73 -0
  105. package/dist/src/sentinel.guard.js.map +1 -0
  106. package/dist/src/sentinel.module-definition.d.ts +2 -0
  107. package/dist/src/sentinel.module-definition.js +7 -0
  108. package/dist/src/sentinel.module-definition.js.map +1 -0
  109. package/dist/src/sentinel.module.d.ts +3 -0
  110. package/dist/src/sentinel.module.js +40 -0
  111. package/dist/src/sentinel.module.js.map +1 -0
  112. package/dist/src/sentinel.service.d.ts +39 -0
  113. package/dist/src/sentinel.service.js +146 -0
  114. package/dist/src/sentinel.service.js.map +1 -0
  115. package/dist/tsconfig.build.tsbuildinfo +1 -0
  116. package/dist/tsconfig.tsbuildinfo +1 -0
  117. package/eslint.config.mjs +34 -0
  118. package/models/index.js +43 -0
  119. package/models/permissionkey.js +31 -0
  120. package/models/role.js +23 -0
  121. package/models/rolehaspermission.js +43 -0
  122. package/nest-cli.json +8 -0
  123. package/package.json +103 -0
  124. package/prisma/migrations/20260227023704_init/migration.sql +74 -0
  125. package/prisma/migrations/migration_lock.toml +3 -0
  126. package/prisma/schema.prisma +62 -0
  127. package/prisma.config.ts +14 -0
  128. package/src/bin/commit.ts +186 -0
  129. package/src/bin/init.ts +251 -0
  130. package/src/bin/migrations/1-create-permission.js +32 -0
  131. package/src/bin/migrations/2-create-role.js +29 -0
  132. package/src/bin/migrations/3-create-rolepermissions.js +46 -0
  133. package/src/bin/migrations/4-create-model-roles.js +46 -0
  134. package/src/bin/resource.ts +107 -0
  135. package/src/bin/sentinel.ts +115 -0
  136. package/src/bin/tsconfig.json +30 -0
  137. package/src/can.decorator.ts +4 -0
  138. package/src/generated/prisma/browser.ts +49 -0
  139. package/src/generated/prisma/client.ts +69 -0
  140. package/src/generated/prisma/commonInputTypes.ts +302 -0
  141. package/src/generated/prisma/enums.ts +15 -0
  142. package/src/generated/prisma/internal/class.ts +250 -0
  143. package/src/generated/prisma/internal/prismaNamespace.ts +1213 -0
  144. package/src/generated/prisma/internal/prismaNamespaceBrowser.ts +163 -0
  145. package/src/generated/prisma/models/ModelHasRoles.ts +1521 -0
  146. package/src/generated/prisma/models/PermissionKeys.ts +1362 -0
  147. package/src/generated/prisma/models/RoleHasPermissions.ts +1503 -0
  148. package/src/generated/prisma/models/Roles.ts +1437 -0
  149. package/src/generated/prisma/models/SequelizeMeta.ts +1032 -0
  150. package/src/generated/prisma/models/Users.ts +1402 -0
  151. package/src/generated/prisma/models.ts +17 -0
  152. package/src/main.ts +24 -0
  153. package/src/models/SecuredResource.d.ts +8 -0
  154. package/src/models/SecuredResource.ts +9 -0
  155. package/src/models/SentinelConfig.d.ts +7 -0
  156. package/src/models/SentinelConfig.ts +8 -0
  157. package/src/models/SentinelModel.ts +39 -0
  158. package/src/models/SentinelModuleOptions.ts +11 -0
  159. package/src/models/sequelize/PermissionKey.ts +22 -0
  160. package/src/models/tsconfig.json +25 -0
  161. package/src/prisma.service.ts +13 -0
  162. package/src/sentinel.guard.ts +63 -0
  163. package/src/sentinel.module-definition.ts +5 -0
  164. package/src/sentinel.module.ts +27 -0
  165. package/src/sentinel.service.ts +188 -0
  166. package/tsconfig.build.json +11 -0
  167. package/tsconfig.json +25 -0
@@ -0,0 +1,17 @@
1
+
2
+ /* !!! This is code generated by Prisma. Do not edit directly. !!! */
3
+ /* eslint-disable */
4
+ // biome-ignore-all lint: generated file
5
+ // @ts-nocheck
6
+ /*
7
+ * This is a barrel export file for all models and their related types.
8
+ *
9
+ * 🟢 You can import this file directly.
10
+ */
11
+ export type * from './models/ModelHasRoles.js'
12
+ export type * from './models/PermissionKeys.js'
13
+ export type * from './models/RoleHasPermissions.js'
14
+ export type * from './models/Roles.js'
15
+ export type * from './models/SequelizeMeta.js'
16
+ export type * from './models/Users.js'
17
+ export type * from './commonInputTypes.js'
package/src/main.ts ADDED
@@ -0,0 +1,24 @@
1
+ export * from './sentinel.module';
2
+ export * from './sentinel.service';
3
+ export * from './sentinel.guard';
4
+ export * from './can.decorator';
5
+ export * from './models/SecuredResource';
6
+ export * from './models/SentinelConfig';
7
+
8
+ // import { Logger } from '@nestjs/common';
9
+ // import { NestFactory } from '@nestjs/core';
10
+ // import { SentinelModule } from './sentinel.module';
11
+
12
+ // async function bootstrap() {
13
+ // const app = await NestFactory.create(SentinelModule);
14
+ // const globalPrefix = 'api';
15
+ // app.setGlobalPrefix(globalPrefix);
16
+ // app.enableCors();
17
+ // const port = 8080;
18
+ // await app.listen(port);
19
+ // Logger.log(
20
+ // `🚀Main Backend Application is running on: http://localhost:${port}/${globalPrefix}`,
21
+ // );
22
+ // }
23
+
24
+ // bootstrap();
@@ -0,0 +1,8 @@
1
+ export interface SecuredResource {
2
+ name: string;
3
+ actions: SecuredResourceAction[];
4
+ }
5
+ export interface SecuredResourceAction {
6
+ key: string;
7
+ description?: string;
8
+ }
@@ -0,0 +1,9 @@
1
+ export abstract class SecuredResource {
2
+ name: string;
3
+ actions: Record<string, SecuredResourceAction>;
4
+ }
5
+
6
+ export interface SecuredResourceAction {
7
+ description?: string;
8
+ source?: string;
9
+ }
@@ -0,0 +1,7 @@
1
+ import { SecuredResource } from './SecuredResource';
2
+ export interface SentinelConfig {
3
+ name: string;
4
+ sourceRoot: string;
5
+ sentinelConfigPath: string;
6
+ resources?: SecuredResource[];
7
+ }
@@ -0,0 +1,8 @@
1
+ import { SecuredResource } from './SecuredResource';
2
+
3
+ export interface SentinelConfig {
4
+ name: string;
5
+ sourceRoot: string;
6
+ sentinelConfigPath: string;
7
+ resources?: SecuredResource[];
8
+ }
@@ -0,0 +1,39 @@
1
+ import { PermissionKeysModel } from '../generated/prisma/models';
2
+ export interface SentinelPermissionKey {
3
+ id: string;
4
+ resource: string;
5
+ action: string;
6
+ createdAt: string | Date;
7
+ updatedAt: string | Date;
8
+ description: string;
9
+ }
10
+
11
+ export interface SentinelModelRoleHasPermissions {
12
+ id: number;
13
+ role: number;
14
+ permission: string;
15
+ createdAt: string | Date;
16
+ updatedAt: string | Date;
17
+ PermissionKeys: SentinelPermissionKey;
18
+ }
19
+
20
+ export interface SentinelModelRole {
21
+ id: number;
22
+ name: string;
23
+ createdAt: string | Date;
24
+ updatedAt: string | Date;
25
+ RoleHasPermissions: SentinelModelRoleHasPermissions[];
26
+ }
27
+
28
+ export interface SentinelModelHasPermissions {
29
+ id: number;
30
+ role: number;
31
+ model: number;
32
+ createdAt: string | Date;
33
+ updatedAt: string | Date;
34
+ Roles: SentinelModelRole;
35
+ }
36
+
37
+ export interface SentinelModel {
38
+ permissions: PermissionKeysModel[];
39
+ }
@@ -0,0 +1,11 @@
1
+ import { SecuredResource } from './SecuredResource';
2
+
3
+ export interface SentinelModuleOptions {
4
+ resources: SecuredResource[];
5
+ modelTable: string;
6
+ modelKey: string;
7
+ /**
8
+ * Defines which custom header Sentinel should look for when validating gRPC calls via the SentinelGuard
9
+ */
10
+ header: string;
11
+ }
@@ -0,0 +1,22 @@
1
+ import {
2
+ Column,
3
+ DataType,
4
+ Model,
5
+ PrimaryKey,
6
+ Table,
7
+ } from 'sequelize-typescript';
8
+
9
+ @Table({ tableName: 'PermissionKeys' })
10
+ export class PermissionKey extends Model {
11
+ @Column({ primaryKey: true, type: DataType.STRING })
12
+ declare id: string;
13
+
14
+ @Column({ type: DataType.STRING })
15
+ resource: string;
16
+
17
+ @Column({ type: DataType.STRING })
18
+ action: string;
19
+
20
+ @Column({ type: DataType.STRING })
21
+ description: string;
22
+ }
@@ -0,0 +1,25 @@
1
+ {
2
+ "compilerOptions": {
3
+ "module": "nodenext",
4
+ "moduleResolution": "nodenext",
5
+ "resolvePackageJsonExports": true,
6
+ "esModuleInterop": true,
7
+ "isolatedModules": true,
8
+ "declaration": true,
9
+ "removeComments": true,
10
+ "emitDecoratorMetadata": true,
11
+ "experimentalDecorators": true,
12
+ "allowSyntheticDefaultImports": true,
13
+ "target": "ES2023",
14
+ "sourceMap": true,
15
+ "outDir": "../../dist/models/sentinel",
16
+ "baseUrl": "./",
17
+ "incremental": true,
18
+ "skipLibCheck": true,
19
+ "strictNullChecks": true,
20
+ "forceConsistentCasingInFileNames": true,
21
+ "noImplicitAny": false,
22
+ "strictBindCallApply": false,
23
+ "noFallthroughCasesInSwitch": false
24
+ }
25
+ }
@@ -0,0 +1,13 @@
1
+ import { Injectable } from '@nestjs/common';
2
+ import { PrismaClient } from './generated/prisma/client';
3
+ import { PrismaPg } from '@prisma/adapter-pg';
4
+
5
+ @Injectable()
6
+ export class PrismaService extends PrismaClient {
7
+ constructor() {
8
+ const adapter = new PrismaPg({
9
+ connectionString: `postgres://${process.env.SENTINEL_DB_USER}:${process.env.SENTINEL_DB_PASS}@${process.env.SENTINEL_DB_HOST}:${process.env.SENTINEL_DB_PORT}/${process.env.SENTINEL_DB_DATABASE}`,
10
+ });
11
+ super({ adapter });
12
+ }
13
+ }
@@ -0,0 +1,63 @@
1
+ import {
2
+ CanActivate,
3
+ ExecutionContext,
4
+ Inject,
5
+ Injectable,
6
+ } from '@nestjs/common';
7
+ import { SentinelService } from './sentinel.service';
8
+ import { type Metadata } from '@grpc/grpc-js';
9
+ import { type Request } from 'express';
10
+ import { SentinelModel } from './models/SentinelModel';
11
+ import { Reflector } from '@nestjs/core';
12
+ import { Can, SecuredResourceAction } from './main';
13
+
14
+ @Injectable()
15
+ export class SentinelGuard implements CanActivate {
16
+ constructor(
17
+ private readonly sentinelService: SentinelService,
18
+ @Inject(Reflector) private reflector: Reflector,
19
+ ) {}
20
+ canActivate(context: ExecutionContext): boolean {
21
+ const requiredActions = this.reflector.getAllAndOverride<
22
+ SecuredResourceAction[]
23
+ >(Can, [context.getHandler(), context.getClass()]);
24
+
25
+ if (requiredActions.length == 0) return true;
26
+
27
+ let sentinelAuth = '';
28
+ if (context.getType() == 'rpc') {
29
+ const rpcContext: Metadata = context.switchToRpc().getContext();
30
+ sentinelAuth = rpcContext.get(
31
+ this.sentinelService.options.header || 'x-sentinel-key',
32
+ )[0] as string;
33
+ } else if (context.getType() == 'http') {
34
+ const httpContext: Request = context.switchToHttp().getRequest();
35
+ const header =
36
+ httpContext.headers[
37
+ this.sentinelService.options.header || 'x-sentinel-key'
38
+ ];
39
+ if (header && typeof header == 'string') {
40
+ sentinelAuth = header;
41
+ }
42
+ } else return false;
43
+
44
+ if (!sentinelAuth) return false;
45
+
46
+ try {
47
+ const payload: SentinelModel = this.sentinelService.verify(sentinelAuth);
48
+ if (!payload) return false;
49
+
50
+ const abilities = this.sentinelService.parseModelAbilities(payload);
51
+ for (const action of requiredActions) {
52
+ if (!action.source) {
53
+ return false;
54
+ }
55
+
56
+ if (abilities.can(Object.keys(action)[0], action.source)) return true;
57
+ }
58
+ return false;
59
+ } catch {
60
+ return false;
61
+ }
62
+ }
63
+ }
@@ -0,0 +1,5 @@
1
+ import { ConfigurableModuleBuilder } from '@nestjs/common';
2
+ import { SentinelModuleOptions } from './models/SentinelModuleOptions';
3
+
4
+ export const { ConfigurableModuleClass, MODULE_OPTIONS_TOKEN } =
5
+ new ConfigurableModuleBuilder<SentinelModuleOptions>().build();
@@ -0,0 +1,27 @@
1
+ import { Module } from '@nestjs/common';
2
+ import { SentinelService } from './sentinel.service';
3
+ import { ConfigModule, ConfigService } from '@nestjs/config';
4
+ import { PrismaService } from './prisma.service';
5
+ import { ConfigurableModuleClass } from './sentinel.module-definition';
6
+ import { JwtModule } from '@nestjs/jwt';
7
+ import { SentinelGuard } from './sentinel.guard';
8
+
9
+ @Module({
10
+ imports: [
11
+ ConfigModule.forRoot({ isGlobal: true }),
12
+ JwtModule.registerAsync({
13
+ imports: [ConfigModule],
14
+ inject: [ConfigService],
15
+ useFactory: (configService: ConfigService) => ({
16
+ secret: configService.getOrThrow('SENTINEL_KEY'),
17
+ signOptions: {
18
+ expiresIn: '30m',
19
+ },
20
+ }),
21
+ }),
22
+ ],
23
+ controllers: [],
24
+ providers: [PrismaService, SentinelService, SentinelGuard],
25
+ exports: [PrismaService, SentinelService, SentinelGuard],
26
+ })
27
+ export class SentinelModule extends ConfigurableModuleClass {}
@@ -0,0 +1,188 @@
1
+ import {
2
+ Inject,
3
+ Injectable,
4
+ Logger,
5
+ NotFoundException,
6
+ OnModuleInit,
7
+ } from '@nestjs/common';
8
+ import { Prisma } from './generated/prisma/client';
9
+ import { PrismaService } from './prisma.service';
10
+ import { MODULE_OPTIONS_TOKEN } from './sentinel.module-definition';
11
+ import type { SentinelModuleOptions } from './models/SentinelModuleOptions';
12
+ import { SecuredResource } from './main';
13
+ import { PermissionKeysModel } from './generated/prisma/models';
14
+ import {
15
+ AbilityBuilder,
16
+ createMongoAbility,
17
+ ExtractSubjectType,
18
+ InferSubjects,
19
+ MongoAbility,
20
+ } from '@casl/ability';
21
+ import { ConfigService } from '@nestjs/config';
22
+ import { JwtService } from '@nestjs/jwt';
23
+ import { SentinelModel } from './models/SentinelModel';
24
+
25
+ @Injectable()
26
+ export class SentinelService implements OnModuleInit {
27
+ resources: SecuredResource[];
28
+ options: SentinelModuleOptions;
29
+ constructor(
30
+ @Inject(MODULE_OPTIONS_TOKEN) private _options: SentinelModuleOptions,
31
+ private prisma: PrismaService,
32
+ private jwt: JwtService,
33
+ private config: ConfigService,
34
+ ) {
35
+ this.options = this._options;
36
+ this.resources = this.options.resources;
37
+ }
38
+
39
+ verify(token: string): SentinelModel {
40
+ return this.jwt.verify(token);
41
+ }
42
+
43
+ packModel(model: PermissionKeysModel[]) {
44
+ return this.jwt.sign({
45
+ permissions: model,
46
+ });
47
+ }
48
+
49
+ parseModelAbilities(model: SentinelModel): MongoAbility {
50
+ const { can, cannot, build } = new AbilityBuilder(createMongoAbility);
51
+
52
+ for (const resource of this.resources) {
53
+ for (const action of Object.keys(resource.actions)) {
54
+ if (
55
+ model.permissions.find(
56
+ (permissions) => permissions.action == resource.actions[action],
57
+ )
58
+ ) {
59
+ can(action, resource.name);
60
+ } else cannot(action, resource.name);
61
+ }
62
+ }
63
+
64
+ type Subjects = InferSubjects<any>;
65
+
66
+ return build({
67
+ detectSubjectType: (item) =>
68
+ // eslint-disable-next-line @typescript-eslint/no-unsafe-return
69
+ item.constructor as ExtractSubjectType<Subjects>,
70
+ });
71
+ }
72
+
73
+ async buildModelAbilities(modelId: number) {
74
+ const rawModel = await this.getModelRoles(modelId);
75
+ if (!rawModel)
76
+ return new NotFoundException(
77
+ 'The provided model ID did not return any Role assigned to it.',
78
+ );
79
+
80
+ const model: {
81
+ roles: string[];
82
+ permissions: PermissionKeysModel[];
83
+ } = {
84
+ roles: [],
85
+ permissions: [],
86
+ };
87
+
88
+ // for (const role of rawModel) {
89
+ // model.roles.push(role.Roles.name || '');
90
+ // role.Roles.RoleHasPermissions.forEach((permission) => {
91
+ // model.permissions.push(permission);
92
+ // });
93
+ // }
94
+
95
+ const { can, cannot, build } = new AbilityBuilder(createMongoAbility);
96
+
97
+ for (const resource of this.resources) {
98
+ for (const action of Object.keys(resource.actions)) {
99
+ if (
100
+ model.permissions.find(
101
+ (permissions) => permissions.action == resource.actions[action],
102
+ )
103
+ ) {
104
+ can(action, resource.name);
105
+ } else cannot(action, resource.name);
106
+ }
107
+ }
108
+
109
+ type Subjects = InferSubjects<any>;
110
+
111
+ return build({
112
+ detectSubjectType: (item) =>
113
+ // eslint-disable-next-line @typescript-eslint/no-unsafe-return
114
+ item.constructor as ExtractSubjectType<Subjects>,
115
+ });
116
+ }
117
+
118
+ async onModuleInit() {
119
+ // check if Sentinel key has been generated
120
+ const key: string | undefined = this.config.get('SENTINEL_KEY');
121
+ if (!key) {
122
+ throw new Error(
123
+ 'SENTINEL_KEY was not found during Sentinel runtime. Have you ran `npx sentinel init`?',
124
+ );
125
+ }
126
+
127
+ // check if actions exist in database
128
+ for (const resource of this.options.resources) {
129
+ const matchingActions = await this.prisma.permissionKeys.findMany({
130
+ where: {
131
+ resource: resource.name,
132
+ },
133
+ });
134
+
135
+ Object.keys(resource.actions).forEach((_key) => {
136
+ if (!matchingActions.find((mAction) => mAction.action == _key)) {
137
+ Logger.warn(
138
+ `Action "${_key}" in Secured Resource "${resource.name}" does not exist in the database. If these Actions are new, have you persisted them with "npx sentinel commit"?`,
139
+ );
140
+ }
141
+ });
142
+ }
143
+ }
144
+
145
+ async getRole(role: Prisma.RolesWhereUniqueInput) {
146
+ return this.prisma.roles.findUnique({
147
+ where: role,
148
+ });
149
+ }
150
+
151
+ async getModelRoles(modelId: number): Promise<PermissionKeysModel[]> {
152
+ try {
153
+ const result = await this.prisma.modelHasRoles.findMany({
154
+ where: {
155
+ model: modelId,
156
+ },
157
+ include: {
158
+ Roles: {
159
+ include: {
160
+ RoleHasPermissions: {
161
+ include: {
162
+ PermissionKeys: true,
163
+ },
164
+ },
165
+ },
166
+ },
167
+ },
168
+ });
169
+
170
+ const flatResult: PermissionKeysModel[] = result.flatMap((rhp) =>
171
+ rhp.Roles.RoleHasPermissions.map((rhp) => rhp.PermissionKeys),
172
+ );
173
+
174
+ return flatResult;
175
+ } catch (error) {
176
+ // eslint-disable-next-line @typescript-eslint/no-unsafe-argument
177
+ throw new Error(error);
178
+ }
179
+ }
180
+
181
+ async getPermission(id: 'fds' | 'fdsk') {
182
+ return await this.prisma.permissionKeys.findFirst({
183
+ where: {
184
+ id,
185
+ },
186
+ });
187
+ }
188
+ }
@@ -0,0 +1,11 @@
1
+ {
2
+ "extends": "./tsconfig.json",
3
+ "exclude": [
4
+ "node_modules",
5
+ "src/models/",
6
+ "src/bin",
7
+ "test",
8
+ "dist",
9
+ "**/*spec.ts"
10
+ ]
11
+ }
package/tsconfig.json ADDED
@@ -0,0 +1,25 @@
1
+ {
2
+ "compilerOptions": {
3
+ "module": "nodenext",
4
+ "moduleResolution": "nodenext",
5
+ "resolvePackageJsonExports": true,
6
+ "esModuleInterop": true,
7
+ "isolatedModules": true,
8
+ "declaration": true,
9
+ "removeComments": true,
10
+ "emitDecoratorMetadata": true,
11
+ "experimentalDecorators": true,
12
+ "allowSyntheticDefaultImports": true,
13
+ "target": "ES2023",
14
+ "sourceMap": true,
15
+ "outDir": "./dist",
16
+ "baseUrl": "./",
17
+ "incremental": true,
18
+ "skipLibCheck": true,
19
+ "strictNullChecks": true,
20
+ "forceConsistentCasingInFileNames": true,
21
+ "noImplicitAny": false,
22
+ "strictBindCallApply": false,
23
+ "noFallthroughCasesInSwitch": false
24
+ }
25
+ }