nestjs-iacry 0.2.0 → 0.2.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 (39) hide show
  1. package/.husky/commit-msg +1 -0
  2. package/.husky/pre-commit +1 -0
  3. package/CHANGELOG.md +2 -0
  4. package/README.md +48 -229
  5. package/dist/constants.d.ts +1 -0
  6. package/dist/constants.js +2 -1
  7. package/dist/decorators/action.d.ts +1 -1
  8. package/dist/decorators/action.js +2 -2
  9. package/dist/decorators/constants.d.ts +3 -3
  10. package/dist/decorators/entity.js +4 -5
  11. package/dist/decorators/firewall.d.ts +1 -1
  12. package/dist/decorators/firewall.guard.js +2 -1
  13. package/dist/decorators/firewall.js +1 -2
  14. package/dist/decorators/helper.js +8 -8
  15. package/dist/decorators/principal.d.ts +1 -1
  16. package/dist/decorators/principal.js +2 -2
  17. package/dist/decorators/resource.d.ts +1 -1
  18. package/dist/decorators/resource.js +2 -2
  19. package/dist/helpers/core.d.ts +1 -1
  20. package/dist/iacry.module.js +2 -3
  21. package/dist/iacry.service.js +6 -1
  22. package/dist/index.d.ts +2 -1
  23. package/dist/index.js +4 -1
  24. package/dist/interfaces/policy.d.ts +1 -1
  25. package/dist/matcher.js +8 -4
  26. package/dist/policy.js +4 -3
  27. package/dist/storages/cache/ioredis.d.ts +3 -3
  28. package/dist/storages/global.storage.d.ts +2 -2
  29. package/dist/storages/sequelize/storage.model.js +2 -1
  30. package/dist/storages/typeorm/storage.entity.d.ts +9 -0
  31. package/dist/storages/typeorm/storage.entity.js +42 -0
  32. package/dist/storages/typeorm/storage.interface.d.ts +6 -0
  33. package/dist/storages/typeorm/storage.interface.js +2 -0
  34. package/dist/storages/typeorm/typeorm.error.d.ts +3 -0
  35. package/dist/storages/typeorm/typeorm.error.js +7 -0
  36. package/dist/storages/typeorm.storage.d.ts +28 -0
  37. package/dist/storages/typeorm.storage.js +115 -0
  38. package/jest.config.js +12 -14
  39. package/package.json +43 -43
@@ -0,0 +1 @@
1
+ npx commitlint --edit $1
@@ -0,0 +1 @@
1
+ npx lint-staged
package/CHANGELOG.md CHANGED
@@ -2,6 +2,8 @@
2
2
 
3
3
  All notable changes to this project will be documented in this file. See [standard-version](https://github.com/conventional-changelog/standard-version) for commit guidelines.
4
4
 
5
+ ### [0.2.1](https://github.com/AlexanderC/nestjs-iacry/compare/v0.2.0...v0.2.1) (2026-02-19)
6
+
5
7
  ## [0.2.0](https://github.com/AlexanderC/nestjs-iacry/compare/v0.0.12...v0.2.0) (2023-07-10)
6
8
 
7
9
  ### [0.0.12](https://github.com/AlexanderC/nestjs-iacry/compare/v0.0.10...v0.0.12) (2022-12-07)
package/README.md CHANGED
@@ -5,7 +5,7 @@
5
5
  </p>
6
6
 
7
7
  <p align="center">
8
- An Identity and Access Control (Management) module for Nest framework (node.js) highly inspired by the <a href="https://aws.amazon.com/iam/">AWS IAM</a>.
8
+ <b>Identity and Access Management (IAM)</b> module for NestJS, inspired by <a href="https://aws.amazon.com/iam/">AWS IAM</a>.
9
9
  </p>
10
10
 
11
11
  <p align="center">
@@ -14,256 +14,75 @@
14
14
  <a href="https://npmjs.com/package/nestjs-iacry"><img src="https://img.shields.io/npm/dm/nestjs-iacry.svg" alt="NPM Downloads" /></a>
15
15
  </p>
16
16
 
17
- ### Installation
17
+ ## Overview
18
18
 
19
- ```sh
20
- npm install --save nestjs-iacry sequelize-typescript
21
- #or
22
- yarn add nestjs-iacry sequelize-typescript
23
- ```
24
-
25
- > Important: `sequelize-typescript` is required if you are using `sequelize` policy storage model.
26
-
27
- ### Configuration
28
-
29
- Configure policy storage using sequelize adapter:
30
- ```typescript
31
- // models/policies-storage.model.ts
32
- import { PoliciesStorageSequelizeModel } from 'nestjs-iacry';
33
-
34
- export default class PoliciesStorage extends PoliciesStorageSequelizeModel<PoliciesStorage> { }
35
- ```
36
-
37
- Configure your models:
38
- ```typescript
39
- // models/user.model.ts
40
- import { IACryEntity } from 'nestjs-iacry';
41
-
42
- // You might optionally use dynamic name fields to allow matching like "Principal: 'admin:*'"
43
- // @IACryEntity({ nameField: 'role' })
44
- @IACryEntity()
45
- @Table({})
46
- export default class User extends Model<User> {
47
- id: string;
48
- role?: string; // nameField
49
- }
50
-
51
- // models/book.model.ts
52
- import { IACryEntity } from 'nestjs-iacry';
53
-
54
- @IACryEntity()
55
- @Table({})
56
- export default class Book extends Model<Book> {
57
- id: string;
58
- }
59
- ```
60
-
61
- And finaly include the module and the service *(assume using [Nestjs Configuration](https://docs.nestjs.com/techniques/configuration))*:
62
- ```typescript
63
- // src/app.module.ts
64
- import { IACryModule, Effect, PolicyInterface, SEQUELIZE_STORAGE, IOREDIS_CACHE } from 'nestjs-iacry';
65
- import PolicyStorage from '../models/policy-storage.model';
66
-
67
- @Module({
68
- imports: [
69
- IACryModule.forRootAsync({
70
- imports: [ConfigModule, RedisModule],
71
- inject: [ConfigService, RedisService],
72
- useFactory: async (configService: ConfigService, redisService: RedisService) => {
73
- return {
74
- storage: SEQUELIZE_STORAGE, // dynamic policy storage (e.g. sequelize)
75
- storageRepository: PolicyStorage, // if database storage specified
76
- cache: IOREDIS_CACHE, // dynamic policy storage cache (e.g. ioredis)
77
- cacheClient: <IORedis.Redis>await redisService.getClient(), // if cache adapter was specified
78
- cacheOptions: { expire: 600 }, // policy cache expires in 10 minutes (default 1 hour)
79
- policies: [ // some hardcoded policies...
80
- ...configService.get<Array<string | PolicyInterface>>('policies'),
81
- {
82
- // allow any action to be performed by the user
83
- Effect: Effect.ALLOW,
84
- Action: '*',
85
- // If used "@IACryEntity({ nameField: 'role' })" you might specify "admin"
86
- Principal: 'user',
87
- },
88
- ],
89
- };
90
- },
91
- }),
92
- ],
93
- },
94
- ```
95
-
96
- ### Usage
19
+ `nestjs-iacry` provides fine-grained, policy-based access control for NestJS applications. Define Allow/Deny policies with glob-pattern matching on Actions, Resources, and Principals — then enforce them via route decorators or a programmatic service API.
97
20
 
98
- Using firewall guard in controllers:
99
- ```typescript
100
- // src/some-fancy.controller.ts
101
- import { Controller, Post, UseGuards } from '@nestjs/common';
102
- import { IACryAction, IACryResource, IACryPrincipal, IACryFirewall, IACryFirewallGuard } from 'nestjs-iacry';
21
+ ## Features
103
22
 
104
- @Controller()
105
- export class BookController {
106
- @IACryAction('book:update')
107
- @IACryResource('book:{params.id}') // {params.id} is replaced with req.params.id [OPTIONAL]
108
- @IACryPrincipal() // taken from req.user by default
109
- @UseGuards(JwtAuthGuard, IACryFirewallGuard)
110
- @Post('book/:id')
111
- async update(@Request() req) { }
23
+ - **AWS IAM-inspired policies** — familiar Allow/Deny model with Action, Resource, and Principal fields
24
+ - **Multiple storage backends** — Sequelize, TypeORM, in-memory, or bring your own
25
+ - **Decorator-based route guards** — `@IACryFirewall`, `@IACryAction`, `@IACryResource`, `@IACryPrincipal`
26
+ - **Programmatic API** `IACryService.isGranted()` for imperative checks
27
+ - **Glob-pattern matching** wildcards, negation, pipes via [micromatch](https://github.com/micromatch/micromatch)
28
+ - **Optional caching** — ioredis with configurable TTL, or custom cache adapters
29
+ - **Sid-based policy management** — for system-managed policy lifecycles
112
30
 
113
- // ...or the definition above might be replaced with a shorthand...
114
- @IACryFirewall({ resource: 'book:{params.id}' })
115
- @UseGuards(JwtAuthGuard, IACryFirewallGuard)
116
- @Post('book/:id')
117
- async update(@Request() req) { }
31
+ ## Documentation
118
32
 
119
- // ...you might also combine them...
120
- @IACryFirewall()
121
- @IACryResource('book:{params.id}')
122
- @UseGuards(JwtAuthGuard, IACryFirewallGuard)
123
- @Post('book/:id')
124
- async update(@Request() req) { }
125
- }
126
- ```
127
-
128
- > Important: check out the `FirewallOptions` definition below:
129
- > ```typescript
130
- > export interface FirewallOptions {
131
- > action?: Action, // default "book:update" for BookController.update()
132
- > resource?: Resource, // default "*"
133
- > principal?: Principal, // default REQUEST_USER
134
- > }
135
- > ```
33
+ Full documentation is available at the **[Documentation Site](https://alexanderc.github.io/nestjs-iacry/)**.
136
34
 
137
- Using the service:
138
- ```typescript
139
- // src/some-fancy.controller.ts
140
- import { Controller, Post, UseGuards, UnauthorizedException } from '@nestjs/common';
141
- import { IACryService } from 'nestjs-iacry';
35
+ - [Getting Started](https://alexanderc.github.io/nestjs-iacry/getting-started) — Installation and basic setup
36
+ - [Configuration](https://alexanderc.github.io/nestjs-iacry/configuration) — Module options and async configuration
37
+ - [Storage Adapters](https://alexanderc.github.io/nestjs-iacry/storage-adapters) — Sequelize, TypeORM, custom adapters
38
+ - [Decorators](https://alexanderc.github.io/nestjs-iacry/decorators) Route-level authorization
39
+ - [Service API](https://alexanderc.github.io/nestjs-iacry/service-api) Programmatic policy management
40
+ - [Policies](https://alexanderc.github.io/nestjs-iacry/policies) — Policy structure and pattern matching
41
+ - [Caching](https://alexanderc.github.io/nestjs-iacry/caching) — Redis and custom cache adapters
42
+ - [Advanced](https://alexanderc.github.io/nestjs-iacry/advanced) — Custom adapters, firewall rules, exports
142
43
 
143
- @Controller()
144
- export class BookController {
145
- constructor(private readonly firewall: IACryService) { }
44
+ ## NestJS Compatibility
146
45
 
147
- @UseGuards(JwtAuthGuard)
148
- @Post('book/:id')
149
- async update(@User user: User, @Book() book: Book) {
150
- if (!this.firewall.isGranted('book:update', user, book)) {
151
- throw new UnauthorizedException(`You are not allowed to update book:${book.id}`);
152
- }
153
- }
154
- }
155
- ```
46
+ | NestJS Version | nestjs-iacry Version |
47
+ |----------------|---------------------|
48
+ | 11.x | >= 0.3.0 |
49
+ | 10.x | >= 0.2.0 |
50
+ | 9.x | >= 0.0.12 |
156
51
 
157
- Manage user policies:
158
- ```typescript
159
- import { IACryService, Effect } from 'nestjs-iacry';
52
+ ## Test Coverage
160
53
 
161
- let firewall: IACryService;
162
- let user: User;
163
- let book: Book;
164
-
165
- const attachedPoliciesCount = await firewall.attach(user, [
166
- // Allow any action on the book service
167
- { Effect: Effect.ALLOW, Action: 'book:*'},
168
- // allow updating any books to any user except the user with ID=7
169
- { Effect: Effect.DENY, Action: ['book:update', 'book:patch'], Principal: 'user:!7' },
170
- // deny deleting books to all users
171
- { Effect: Effect.DENY, Action: 'book:delete', Principal: ['user:*'] },
172
- ]);
173
- const attachedPoliciesCount = await firewall.upsertBySid(
174
- 'Some policy Sid (mainly a name)',
175
- user,
176
- [{ Sid: 'Some policy Sid (mainly a name)', Effect: Effect.ALLOW, Action: 'book:*'}],
177
- );
178
- // oneliner to allow user patching and updating but deleting the book
179
- const attachedPoliciesCount = await firewall.grant('book:patch|update|!delete', user, book);
180
- const policies = await firewall.retrieve(user);
181
- const policies = await firewall.retrieveBySid('Some policy Sid (mainly a name)', user);
182
- const deletedPoliciesCount = await firewall.reset(user);
183
- ```
54
+ | Category | Statements | Branches | Functions | Lines |
55
+ |----------|-----------|----------|-----------|-------|
56
+ | **All files** | **90.25%** | **81.92%** | **87.5%** | **90.27%** |
57
+ | Core (policy, matcher, firewall) | 100% | 91%+ | 100% | 100% |
58
+ | Decorators | 97%+ | 80%+ | 100% | 97%+ |
59
+ | Errors | 100% | 100% | 100% | 100% |
60
+ | Helpers | 100% | 94% | 100% | 100% |
61
+ | Storages | 86%+ | 81%+ | 80%+ | 86%+ |
184
62
 
185
- Managing a policy by it's Sid might be useful when automating policy assignments.
186
- E.g. granting `book:update|patch|delete` on books created by the user is possible by upserting
187
- a system managed policy w/ the sid `system:user:book` as follows:
63
+ **149 tests** across **19 test suites**.
188
64
 
189
- ```typescript
190
- import { IACryService, Effect } from 'nestjs-iacry';
65
+ ## Development
191
66
 
192
- let firewall: IACryService;
193
- let user: User;
194
- let newBook: Book;
195
-
196
- const BOOK_SID = 'system:user:book';
197
- let policies = await firewall.retrieveBySid(BOOK_SID, user);
198
-
199
- if (policies.length > 0) {
200
- policies[0] = policies[0].toJSON();
201
- policies[0].Resource.push(newBook.toDynamicIdentifier());
202
- } else {
203
- policies = [{
204
- Sid: BOOK_SID, // frankly speaking this is optional o_O...
205
- Effect: Effect.ALLOW,
206
- Action: 'book:update|patch|delete',
207
- Resource: [newBook.toDynamicIdentifier()],
208
- }];
209
- }
210
-
211
- await firewall.upsertBySid(BOOK_SID, user, policies);
212
- ```
213
-
214
- ### Documentation
215
-
216
- #### Policy Definition
217
-
218
- Structure:
219
- ```typescript
220
- interface PolicyInterface {
221
- Sid?: string, // policy identifier
222
- Effect: 'Allow' | 'Deny', // Allow | Deny
223
- Action: string | { service: string, action: string } | Array<string | { service: string, action: string }>, // Which action: e.g. "book:update"
224
- Resource?: string | { entity: string, id: number | string } | Array<string | { entity: string, id: number | string }>, // Action object: e.g. "book:33"
225
- Principal?: string | { entity: string, id: number | string } | Array<string | { entity: string, id: number | string }>, // Whom: e.g. "user:1"
226
- }
227
- ```
228
-
229
- Syntax Sugar:
230
-
231
- - **DIs might be negated** which would mean that a `book:!33` would match any book but the one with ID=33.
232
- - **DIs might be piped/or-ed** which would mean that a `book:!(update|delete)` would allow any action BUT updating or deleting a book.
233
- - **DIs might contain complex match patterns** which would mean that a principal `*/admin:!33` would match an admin user from any namespace but the one with ID=33.
234
-
235
- > More information about how `micromatch` matches strings [can be found here](https://github.com/micromatch/micromatch#matching-features).
236
-
237
- > Important: Within the matcher `*` is replaced with `**` automatically, thus a single `*` won't work as of original micromatch docs.
238
-
239
- **Dynamic Identifiers** or **DIs** are considered `Action`, `Resource` and `Principal` properties.
240
-
241
- ### Development
242
-
243
- Running tests:
244
67
  ```bash
68
+ # Run tests
245
69
  npm test
246
- ```
247
70
 
248
- Releasing:
249
- ```bash
71
+ # Build
72
+ npm run build
73
+
74
+ # Release
250
75
  npm run format
251
- npm run release # npm run patch|minor|major
76
+ npm run release # or: npm run patch | minor | major
252
77
  npm run deploy
253
78
  ```
254
79
 
255
- ### TODO
256
-
257
- - [ ] Implement an abstraction over the system managed policies
258
- - [ ] Implement policy conditional statements (e.g. update books that the user created himself)
259
- - [ ] Add more built in conditional matchers to cover basic use-cases
260
- - [ ] Cover most of codebase w/ tests
261
- - [ ] Add comprehensive Documentation
80
+ ## Contributing
262
81
 
263
- ### Contributing
82
+ Contributions are welcome! Please open an issue or submit a pull request.
264
83
 
265
- * [Alex Cucer](https://github.com/AlexanderC)
84
+ - [Alex Cucer](https://github.com/AlexanderC)
266
85
 
267
- ### License
86
+ ## License
268
87
 
269
88
  MIT
@@ -1,5 +1,6 @@
1
1
  export declare const IACRY_OPTIONS = "IACRY_OPTIONS";
2
2
  export declare const SEQUELIZE_STORAGE = "sequelize";
3
+ export declare const TYPEORM_STORAGE = "typeorm";
3
4
  export declare const IOREDIS_CACHE = "ioredis";
4
5
  export declare const IS_ALLOWED = "isAllowed";
5
6
  export declare const IS_ALLOWED_ANY = "isAllowedAny";
package/dist/constants.js CHANGED
@@ -1,8 +1,9 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.IS_ALLOWED_IMPLICIT = exports.IS_ALLOWED_ANY = exports.IS_ALLOWED = exports.IOREDIS_CACHE = exports.SEQUELIZE_STORAGE = exports.IACRY_OPTIONS = void 0;
3
+ exports.IS_ALLOWED_IMPLICIT = exports.IS_ALLOWED_ANY = exports.IS_ALLOWED = exports.IOREDIS_CACHE = exports.TYPEORM_STORAGE = exports.SEQUELIZE_STORAGE = exports.IACRY_OPTIONS = void 0;
4
4
  exports.IACRY_OPTIONS = 'IACRY_OPTIONS';
5
5
  exports.SEQUELIZE_STORAGE = 'sequelize';
6
+ exports.TYPEORM_STORAGE = 'typeorm';
6
7
  exports.IOREDIS_CACHE = 'ioredis';
7
8
  exports.IS_ALLOWED = 'isAllowed';
8
9
  exports.IS_ALLOWED_ANY = 'isAllowedAny';
@@ -1,4 +1,4 @@
1
1
  import { ExecutionContext } from '@nestjs/common';
2
2
  import { Action } from '../interfaces/policy';
3
- export declare const extractDynamicIdentifier: (target: object | Function, ctx?: ExecutionContext) => any;
3
+ export declare const extractDynamicIdentifier: (target: object | Function, ctx?: ExecutionContext) => Action | any | null;
4
4
  export declare function Action(action?: Action): MethodDecorator;
@@ -1,6 +1,7 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.Action = exports.extractDynamicIdentifier = void 0;
3
+ exports.extractDynamicIdentifier = void 0;
4
+ exports.Action = Action;
4
5
  const policy_1 = require("../interfaces/policy");
5
6
  const constants_1 = require("./constants");
6
7
  const helper_1 = require("./helper");
@@ -25,4 +26,3 @@ function Action(action) {
25
26
  return descriptor;
26
27
  };
27
28
  }
28
- exports.Action = Action;
@@ -2,9 +2,9 @@ export declare const REQUEST_USER = "{user}";
2
2
  export declare const META_PREFIX = "IACRY_META_";
3
3
  export declare const ENTITY_META_FIELD: string;
4
4
  export declare const ID_META_FIELD: string;
5
- export declare const ACTION_META_FIELD: string;
6
- export declare const RESOURCE_META_FIELD: string;
7
- export declare const PRINCIPAL_META_FIELD: string;
5
+ export declare const ACTION_META_FIELD = "IACRY_META_ACTION";
6
+ export declare const RESOURCE_META_FIELD = "IACRY_META_RESOURCE";
7
+ export declare const PRINCIPAL_META_FIELD = "IACRY_META_PRINCIPAL";
8
8
  export declare const CTRL_SERVICE_REGEXP: RegExp;
9
9
  export declare const CTRL_ACTION_PLACEHOLDER = "$$IACRY_CTRL$$";
10
10
  export declare const VAR_REGEXP: RegExp;
@@ -1,6 +1,9 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.Entity = exports.toPlainDynamicIdentifier = exports.toDynamicIdentifier = exports.isEntity = void 0;
3
+ exports.isEntity = isEntity;
4
+ exports.toDynamicIdentifier = toDynamicIdentifier;
5
+ exports.toPlainDynamicIdentifier = toPlainDynamicIdentifier;
6
+ exports.Entity = Entity;
4
7
  const decorator_error_1 = require("../errors/decorator.error");
5
8
  const policy_1 = require("../interfaces/policy");
6
9
  const constants_1 = require("./constants");
@@ -15,7 +18,6 @@ function isEntity(target) {
15
18
  'function' &&
16
19
  typeof Reflect.getMetadata(constants_1.ID_META_FIELD, target.constructor) === 'function');
17
20
  }
18
- exports.isEntity = isEntity;
19
21
  function toDynamicIdentifier(target) {
20
22
  if (!isEntity(target)) {
21
23
  throw new decorator_error_1.DecoratorError('Target object should use @Entity() decorator');
@@ -25,12 +27,10 @@ function toDynamicIdentifier(target) {
25
27
  [policy_1.ID_FIELD]: Reflect.getMetadata(constants_1.ID_META_FIELD, target.constructor)(target),
26
28
  };
27
29
  }
28
- exports.toDynamicIdentifier = toDynamicIdentifier;
29
30
  function toPlainDynamicIdentifier(target) {
30
31
  const dynamicIdentifier = toDynamicIdentifier(target);
31
32
  return `${dynamicIdentifier[policy_1.ENTITY_FIELD]}${policy_1.DELIMITER}${dynamicIdentifier[policy_1.ID_FIELD]}`;
32
33
  }
33
- exports.toPlainDynamicIdentifier = toPlainDynamicIdentifier;
34
34
  function Entity(options) {
35
35
  return (target) => {
36
36
  if ((!options || (!options.name && !options.nameField)) && !target.name) {
@@ -49,4 +49,3 @@ function Entity(options) {
49
49
  : instance[policy_1.ID_FIELD], target);
50
50
  };
51
51
  }
52
- exports.Entity = Entity;
@@ -4,4 +4,4 @@ export interface FirewallOptions {
4
4
  resource?: Resource;
5
5
  principal?: Principal;
6
6
  }
7
- export declare function Firewall(options?: FirewallOptions): <TFunction extends Function, Y>(target: object | TFunction, propertyKey?: string | symbol, descriptor?: TypedPropertyDescriptor<Y>) => void;
7
+ export declare function Firewall(options?: FirewallOptions): <TFunction extends Function, Y>(target: TFunction | object, propertyKey?: string | symbol, descriptor?: TypedPropertyDescriptor<Y>) => void;
@@ -8,7 +8,7 @@ const iacry_service_1 = require("../iacry.service");
8
8
  const action_1 = require("./action");
9
9
  const resource_1 = require("./resource");
10
10
  const principal_1 = require("./principal");
11
- let FirewallGuard = exports.FirewallGuard = class FirewallGuard {
11
+ let FirewallGuard = class FirewallGuard {
12
12
  constructor(service) {
13
13
  this.service = service;
14
14
  }
@@ -22,6 +22,7 @@ let FirewallGuard = exports.FirewallGuard = class FirewallGuard {
22
22
  return this.service.isGranted(action, principal, resource || iacry_service_1.IACryService.ANY);
23
23
  }
24
24
  };
25
+ exports.FirewallGuard = FirewallGuard;
25
26
  exports.FirewallGuard = FirewallGuard = tslib_1.__decorate([
26
27
  (0, common_1.Injectable)(),
27
28
  tslib_1.__metadata("design:paramtypes", [iacry_service_1.IACryService])
@@ -1,6 +1,6 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.Firewall = void 0;
3
+ exports.Firewall = Firewall;
4
4
  const common_1 = require("@nestjs/common");
5
5
  const action_1 = require("./action");
6
6
  const resource_1 = require("./resource");
@@ -8,4 +8,3 @@ const principal_1 = require("./principal");
8
8
  function Firewall(options = {}) {
9
9
  return (0, common_1.applyDecorators)((0, action_1.Action)(options.action), (0, principal_1.Principal)(options.principal), ...(options.resource ? [(0, resource_1.Resource)(options.resource)] : []));
10
10
  }
11
- exports.Firewall = Firewall;
@@ -1,7 +1,9 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.dynamicIdentifierExtractor = exports.processTemplate = void 0;
4
- const dotProp = require("dot-prop");
3
+ exports.processTemplate = processTemplate;
4
+ exports.dynamicIdentifierExtractor = dynamicIdentifierExtractor;
5
+ const tslib_1 = require("tslib");
6
+ const dot_prop_1 = tslib_1.__importDefault(require("dot-prop"));
5
7
  const entity_1 = require("./entity");
6
8
  const decorator_error_1 = require("../errors/decorator.error");
7
9
  const constants_1 = require("./constants");
@@ -11,12 +13,12 @@ function processTemplate(template, context) {
11
13
  if (match.index === constants_1.VAR_REGEXP.lastIndex) {
12
14
  constants_1.VAR_REGEXP.lastIndex++;
13
15
  }
14
- let [definition, varPath] = match;
15
- varPath = varPath.trim();
16
- if (!dotProp.has(context, varPath)) {
16
+ const [definition, rawVarPath] = match;
17
+ const varPath = rawVarPath.trim();
18
+ if (!dot_prop_1.default.has(context, varPath)) {
17
19
  throw new decorator_error_1.DecoratorError(`Unable to find ${varPath} property in Request from metadata field`);
18
20
  }
19
- let varValue = dotProp.get(context, varPath);
21
+ let varValue = dot_prop_1.default.get(context, varPath);
20
22
  if ((0, entity_1.isEntity)(varValue)) {
21
23
  varValue = (0, entity_1.toPlainDynamicIdentifier)(varValue);
22
24
  }
@@ -24,7 +26,6 @@ function processTemplate(template, context) {
24
26
  }
25
27
  return template;
26
28
  }
27
- exports.processTemplate = processTemplate;
28
29
  function dynamicIdentifierExtractor(metadataField, hooks) {
29
30
  return (target, ctx) => {
30
31
  if (!Reflect.hasMetadata(metadataField, target)) {
@@ -46,4 +47,3 @@ function dynamicIdentifierExtractor(metadataField, hooks) {
46
47
  return value;
47
48
  };
48
49
  }
49
- exports.dynamicIdentifierExtractor = dynamicIdentifierExtractor;
@@ -1,3 +1,3 @@
1
1
  import { Principal } from '../interfaces/policy';
2
- export declare const extractDynamicIdentifier: (target: object | Function, ctx?: import("@nestjs/common").ExecutionContext) => any;
2
+ export declare const extractDynamicIdentifier: (target: object | Function, ctx?: import("@nestjs/common").ExecutionContext) => Principal | any | null;
3
3
  export declare function Principal(principal?: string | Principal): MethodDecorator;
@@ -1,6 +1,7 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.Principal = exports.extractDynamicIdentifier = void 0;
3
+ exports.extractDynamicIdentifier = void 0;
4
+ exports.Principal = Principal;
4
5
  const constants_1 = require("./constants");
5
6
  const helper_1 = require("./helper");
6
7
  exports.extractDynamicIdentifier = (0, helper_1.dynamicIdentifierExtractor)(constants_1.PRINCIPAL_META_FIELD);
@@ -10,4 +11,3 @@ function Principal(principal) {
10
11
  return descriptor;
11
12
  };
12
13
  }
13
- exports.Principal = Principal;
@@ -1,3 +1,3 @@
1
1
  import { Resource } from '../interfaces/policy';
2
- export declare const extractDynamicIdentifier: (target: object | Function, ctx?: import("@nestjs/common").ExecutionContext) => any;
2
+ export declare const extractDynamicIdentifier: (target: object | Function, ctx?: import("@nestjs/common").ExecutionContext) => Resource | any | null;
3
3
  export declare function Resource(resource: Resource): MethodDecorator;
@@ -1,6 +1,7 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.Resource = exports.extractDynamicIdentifier = void 0;
3
+ exports.extractDynamicIdentifier = void 0;
4
+ exports.Resource = Resource;
4
5
  const constants_1 = require("./constants");
5
6
  const helper_1 = require("./helper");
6
7
  exports.extractDynamicIdentifier = (0, helper_1.dynamicIdentifierExtractor)(constants_1.RESOURCE_META_FIELD);
@@ -10,4 +11,3 @@ function Resource(resource) {
10
11
  return descriptor;
11
12
  };
12
13
  }
13
- exports.Resource = Resource;
@@ -1,7 +1,7 @@
1
1
  import { PolicyInterface, Action, Resource, Principal, ActionObject, ResourceObject, PrincipalObject, DynamicIdentifier, DynamicIdentifierItem, DynamicIdentifierVector, ANY } from '../interfaces/policy';
2
2
  import { Entity } from '../decorators/entity';
3
3
  export declare abstract class CoreHelper {
4
- static readonly ANY: "*";
4
+ static readonly ANY: ANY;
5
5
  static encode(policy: PolicyInterface, beatify?: boolean): string;
6
6
  static decode(rawPolicy: string): PolicyInterface;
7
7
  isDynamicVector(x: DynamicIdentifier<Action | Resource | Principal> | DynamicIdentifierVector<Action | Resource | Principal>): x is DynamicIdentifierVector<Action | Resource | Principal>;
@@ -6,7 +6,7 @@ const tslib_1 = require("tslib");
6
6
  const common_1 = require("@nestjs/common");
7
7
  const iacry_service_1 = require("./iacry.service");
8
8
  const constants_1 = require("./constants");
9
- let IACryModule = exports.IACryModule = IACryModule_1 = class IACryModule {
9
+ let IACryModule = IACryModule_1 = class IACryModule {
10
10
  static forRoot(options) {
11
11
  const OptionsProvider = {
12
12
  provide: constants_1.IACRY_OPTIONS,
@@ -40,14 +40,12 @@ let IACryModule = exports.IACryModule = IACryModule_1 = class IACryModule {
40
40
  static createAsyncOptionsProvider(options) {
41
41
  if (options.useFactory) {
42
42
  return {
43
- name: constants_1.IACRY_OPTIONS,
44
43
  provide: constants_1.IACRY_OPTIONS,
45
44
  useFactory: options.useFactory,
46
45
  inject: options.inject || [],
47
46
  };
48
47
  }
49
48
  return {
50
- name: constants_1.IACRY_OPTIONS,
51
49
  provide: constants_1.IACRY_OPTIONS,
52
50
  useFactory: async (optionsFactory) => {
53
51
  return optionsFactory.createOptions();
@@ -56,6 +54,7 @@ let IACryModule = exports.IACryModule = IACryModule_1 = class IACryModule {
56
54
  };
57
55
  }
58
56
  };
57
+ exports.IACryModule = IACryModule;
59
58
  exports.IACryModule = IACryModule = IACryModule_1 = tslib_1.__decorate([
60
59
  (0, common_1.Global)(),
61
60
  (0, common_1.Module)({})
@@ -5,6 +5,7 @@ const tslib_1 = require("tslib");
5
5
  const common_1 = require("@nestjs/common");
6
6
  const firewall_1 = require("./firewall");
7
7
  const sequelize_storage_1 = require("./storages/sequelize.storage");
8
+ const typeorm_storage_1 = require("./storages/typeorm.storage");
8
9
  const multiple_storage_1 = require("./storages/multiple.storage");
9
10
  const global_storage_1 = require("./storages/global.storage");
10
11
  const iacry_error_1 = require("./errors/iacry.error");
@@ -14,7 +15,7 @@ const core_1 = require("./helpers/core");
14
15
  const ioredis_1 = require("./storages/cache/ioredis");
15
16
  const cached_storage_1 = require("./storages/cached.storage");
16
17
  const policy_manager_1 = require("./policy.manager");
17
- let IACryService = exports.IACryService = class IACryService extends policy_manager_1.PolicyManager {
18
+ let IACryService = class IACryService extends policy_manager_1.PolicyManager {
18
19
  constructor(options) {
19
20
  super(new multiple_storage_1.MultipleStorage());
20
21
  this.options = options;
@@ -41,6 +42,9 @@ let IACryService = exports.IACryService = class IACryService extends policy_mana
41
42
  case constants_1.SEQUELIZE_STORAGE:
42
43
  this.storage.storages.push(new sequelize_storage_1.SequelizeStorage(options.storageRepository));
43
44
  break;
45
+ case constants_1.TYPEORM_STORAGE:
46
+ this.storage.storages.push(new typeorm_storage_1.TypeOrmStorage(options.storageRepository));
47
+ break;
44
48
  default:
45
49
  throw new iacry_error_1.BaseError(`Unrecognized PolicyInterface Storage type: ${options.storage}`);
46
50
  }
@@ -72,6 +76,7 @@ let IACryService = exports.IACryService = class IACryService extends policy_mana
72
76
  this.firewall = firewall_1.Firewall.create(this.storage, new matcher_1.Matcher(options.strict));
73
77
  }
74
78
  };
79
+ exports.IACryService = IACryService;
75
80
  exports.IACryService = IACryService = tslib_1.__decorate([
76
81
  (0, common_1.Injectable)(),
77
82
  tslib_1.__param(0, (0, common_1.Inject)(constants_1.IACRY_OPTIONS)),
package/dist/index.d.ts CHANGED
@@ -1,5 +1,5 @@
1
1
  export { IACryModule } from './iacry.module';
2
- export { SEQUELIZE_STORAGE, IOREDIS_CACHE, IS_ALLOWED, IS_ALLOWED_ANY, IS_ALLOWED_IMPLICIT, } from './constants';
2
+ export { SEQUELIZE_STORAGE, TYPEORM_STORAGE, IOREDIS_CACHE, IS_ALLOWED, IS_ALLOWED_ANY, IS_ALLOWED_IMPLICIT, } from './constants';
3
3
  export { REQUEST_USER } from './decorators/constants';
4
4
  export { Options as IACryModuleOptions } from './interfaces/module.options';
5
5
  export { AsyncOptions as IACryModuleAsyncOptions } from './interfaces/module-async.options';
@@ -10,6 +10,7 @@ export { PolicyInterface, Effect, Action, ActionObject, Resource, ResourceObject
10
10
  export { Cache } from './storages/cache/cache.interface';
11
11
  export { IACryService } from './iacry.service';
12
12
  export { PoliciesStorage as PoliciesStorageSequelizeModel } from './storages/sequelize/storage.model';
13
+ export { PoliciesStorageEntity as PoliciesStorageTypeOrmEntity } from './storages/typeorm/storage.entity';
13
14
  export { Entity as IACryEntity } from './decorators/entity';
14
15
  export { Action as IACryAction } from './decorators/action';
15
16
  export { Resource as IACryResource } from './decorators/resource';
package/dist/index.js CHANGED
@@ -1,10 +1,11 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.WrongPolicyPropFormat = exports.MissingPolicyProps = exports.IACryDecoratorError = exports.IACryError = exports.Firewall = exports.Matcher = exports.PolicyVector = exports.Policy = exports.IACryFirewallGuard = exports.IACryFirewall = exports.IACryPrincipal = exports.IACryResource = exports.IACryAction = exports.IACryEntity = exports.PoliciesStorageSequelizeModel = exports.IACryService = exports.Effect = exports.REQUEST_USER = exports.IS_ALLOWED_IMPLICIT = exports.IS_ALLOWED_ANY = exports.IS_ALLOWED = exports.IOREDIS_CACHE = exports.SEQUELIZE_STORAGE = exports.IACryModule = void 0;
3
+ exports.WrongPolicyPropFormat = exports.MissingPolicyProps = exports.IACryDecoratorError = exports.IACryError = exports.Firewall = exports.Matcher = exports.PolicyVector = exports.Policy = exports.IACryFirewallGuard = exports.IACryFirewall = exports.IACryPrincipal = exports.IACryResource = exports.IACryAction = exports.IACryEntity = exports.PoliciesStorageTypeOrmEntity = exports.PoliciesStorageSequelizeModel = exports.IACryService = exports.Effect = exports.REQUEST_USER = exports.IS_ALLOWED_IMPLICIT = exports.IS_ALLOWED_ANY = exports.IS_ALLOWED = exports.IOREDIS_CACHE = exports.TYPEORM_STORAGE = exports.SEQUELIZE_STORAGE = exports.IACryModule = void 0;
4
4
  var iacry_module_1 = require("./iacry.module");
5
5
  Object.defineProperty(exports, "IACryModule", { enumerable: true, get: function () { return iacry_module_1.IACryModule; } });
6
6
  var constants_1 = require("./constants");
7
7
  Object.defineProperty(exports, "SEQUELIZE_STORAGE", { enumerable: true, get: function () { return constants_1.SEQUELIZE_STORAGE; } });
8
+ Object.defineProperty(exports, "TYPEORM_STORAGE", { enumerable: true, get: function () { return constants_1.TYPEORM_STORAGE; } });
8
9
  Object.defineProperty(exports, "IOREDIS_CACHE", { enumerable: true, get: function () { return constants_1.IOREDIS_CACHE; } });
9
10
  Object.defineProperty(exports, "IS_ALLOWED", { enumerable: true, get: function () { return constants_1.IS_ALLOWED; } });
10
11
  Object.defineProperty(exports, "IS_ALLOWED_ANY", { enumerable: true, get: function () { return constants_1.IS_ALLOWED_ANY; } });
@@ -17,6 +18,8 @@ var iacry_service_1 = require("./iacry.service");
17
18
  Object.defineProperty(exports, "IACryService", { enumerable: true, get: function () { return iacry_service_1.IACryService; } });
18
19
  var storage_model_1 = require("./storages/sequelize/storage.model");
19
20
  Object.defineProperty(exports, "PoliciesStorageSequelizeModel", { enumerable: true, get: function () { return storage_model_1.PoliciesStorage; } });
21
+ var storage_entity_1 = require("./storages/typeorm/storage.entity");
22
+ Object.defineProperty(exports, "PoliciesStorageTypeOrmEntity", { enumerable: true, get: function () { return storage_entity_1.PoliciesStorageEntity; } });
20
23
  var entity_1 = require("./decorators/entity");
21
24
  Object.defineProperty(exports, "IACryEntity", { enumerable: true, get: function () { return entity_1.Entity; } });
22
25
  var action_1 = require("./decorators/action");
@@ -35,7 +35,7 @@ export interface PrincipalObject {
35
35
  export type Principal = string | PrincipalObject;
36
36
  export type DynamicIdentifierItem<T> = {
37
37
  value: T;
38
- parse(thing: T): Array<string | ANY>;
38
+ parse(): ActionObject | ResourceObject | PrincipalObject;
39
39
  };
40
40
  export type DynamicIdentifier<T> = T | DynamicIdentifierItem<T>;
41
41
  export type DynamicIdentifierVector<T> = Array<DynamicIdentifier<T>>;
package/dist/matcher.js CHANGED
@@ -1,7 +1,8 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.Matcher = void 0;
4
- const micromatch = require("micromatch");
4
+ const tslib_1 = require("tslib");
5
+ const micromatch_1 = tslib_1.__importDefault(require("micromatch"));
5
6
  const policy_1 = require("./interfaces/policy");
6
7
  const core_1 = require("./helpers/core");
7
8
  class Matcher extends core_1.CoreHelper {
@@ -13,7 +14,7 @@ class Matcher extends core_1.CoreHelper {
13
14
  const resource = this.normalizeDynamicIdentifier(rawResource, policy_1.RESOURCE);
14
15
  const action = this.normalizeDynamicIdentifier(rawAction, policy_1.ACTION);
15
16
  const principal = this.normalizeDynamicIdentifier(rawPrincipal, policy_1.PRINCIPAL);
16
- let result = {
17
+ const result = {
17
18
  allow: [],
18
19
  deny: [],
19
20
  abstain: [],
@@ -53,14 +54,17 @@ class Matcher extends core_1.CoreHelper {
53
54
  .filter(Boolean).length === matchProps.length);
54
55
  }
55
56
  matchItem(rawSource, rawTarget) {
56
- return micromatch.isMatch(this.normalizeValue(rawSource, true), this.normalizeValue(rawTarget)) || micromatch.isMatch(this.normalizeValue(rawTarget, true), this.normalizeValue(rawSource));
57
+ return (micromatch_1.default.isMatch(this.normalizeValue(rawSource, true), this.normalizeValue(rawTarget)) ||
58
+ micromatch_1.default.isMatch(this.normalizeValue(rawTarget, true), this.normalizeValue(rawSource)));
57
59
  }
58
60
  normalizeValue(rawValue, raw = false) {
59
61
  let value = rawValue.toString();
60
62
  if (!this.strict) {
61
63
  value = value.toLowerCase();
62
64
  }
63
- return raw ? value : value.replace(new RegExp(`\\${Matcher.ANY}+`), Matcher.ANY.repeat(2));
65
+ return raw
66
+ ? value
67
+ : value.replace(new RegExp(`\\${Matcher.ANY}+`), Matcher.ANY.repeat(2));
64
68
  }
65
69
  }
66
70
  exports.Matcher = Matcher;
package/dist/policy.js CHANGED
@@ -1,7 +1,8 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.Policy = void 0;
4
- const clone = require("clone");
4
+ const tslib_1 = require("tslib");
5
+ const clone_1 = tslib_1.__importDefault(require("clone"));
5
6
  const policy_1 = require("./interfaces/policy");
6
7
  const core_1 = require("./helpers/core");
7
8
  const missing_policy_props_error_1 = require("./errors/missing-policy-props.error");
@@ -13,7 +14,7 @@ class Policy extends core_1.CoreHelper {
13
14
  policy = Policy.decode(rawPolicy);
14
15
  }
15
16
  else {
16
- policy = clone(rawPolicy);
17
+ policy = (0, clone_1.default)(rawPolicy);
17
18
  }
18
19
  this.normalizePolicyDynamicIdentifiers(policy);
19
20
  missing_policy_props_error_1.MissingPolicyProps.assert(policy);
@@ -33,7 +34,7 @@ class Policy extends core_1.CoreHelper {
33
34
  }
34
35
  toJSON(parse = false) {
35
36
  const { Sid, Effect, Action, Resource, Principal } = this;
36
- const rawPolicy = clone({ Sid, Effect, Action, Resource, Principal });
37
+ const rawPolicy = (0, clone_1.default)({ Sid, Effect, Action, Resource, Principal });
37
38
  if (parse) {
38
39
  this.unpackPolicyDynamicIdentifiers(rawPolicy);
39
40
  }
@@ -1,10 +1,10 @@
1
1
  import { Cache } from './cache.interface';
2
- import * as IORedis from 'ioredis';
2
+ import Redis from 'ioredis';
3
3
  export declare class IoRedis implements Cache {
4
- readonly client: IORedis.Redis;
4
+ readonly client: Redis;
5
5
  private readonly SUCCESS;
6
6
  private readonly EXPIRE;
7
- constructor(client: IORedis.Redis);
7
+ constructor(client: Redis);
8
8
  set(key: string, value: string, expire?: number): Promise<boolean>;
9
9
  has(key: string): Promise<boolean>;
10
10
  get(key: string): Promise<string>;
@@ -2,9 +2,9 @@ import { PrincipalObject } from '../interfaces/policy';
2
2
  import { PolicyStorage } from '../interfaces/policy-storage';
3
3
  import { PolicyInterface } from '../interfaces/policy';
4
4
  export declare class GlobalStorage implements PolicyStorage {
5
- policies: (string | PolicyInterface)[];
5
+ policies: Array<string | PolicyInterface>;
6
6
  readonly readonly: boolean;
7
- constructor(policies?: (string | PolicyInterface)[]);
7
+ constructor(policies?: Array<string | PolicyInterface>);
8
8
  fetch(_principal: PrincipalObject): Promise<Array<string | PolicyInterface>>;
9
9
  fetchBySid(_sid: string, _principal: PrincipalObject): Promise<Array<string | PolicyInterface>>;
10
10
  save(_principal: PrincipalObject, rawPolicies: Array<string | PolicyInterface>): Promise<number>;
@@ -5,7 +5,7 @@ const tslib_1 = require("tslib");
5
5
  const sequelize_typescript_1 = require("sequelize-typescript");
6
6
  const sequelize_1 = require("sequelize");
7
7
  const core_1 = require("../../helpers/core");
8
- let PoliciesStorage = exports.PoliciesStorage = class PoliciesStorage extends sequelize_typescript_1.Model {
8
+ let PoliciesStorage = class PoliciesStorage extends sequelize_typescript_1.Model {
9
9
  static async savePrincipalPolicies(principal, rawPolicies, attach, sid) {
10
10
  if (!attach) {
11
11
  await this.destroyByPrincipal(principal, sid);
@@ -93,6 +93,7 @@ let PoliciesStorage = exports.PoliciesStorage = class PoliciesStorage extends se
93
93
  return item.toLowerCase();
94
94
  }
95
95
  };
96
+ exports.PoliciesStorage = PoliciesStorage;
96
97
  tslib_1.__decorate([
97
98
  sequelize_typescript_1.PrimaryKey,
98
99
  sequelize_typescript_1.AutoIncrement,
@@ -0,0 +1,9 @@
1
+ export declare class PoliciesStorageEntity {
2
+ pk: number;
3
+ sid: string | null;
4
+ id: string | null;
5
+ entity: string | null;
6
+ policy: string;
7
+ createdAt: Date;
8
+ updatedAt: Date;
9
+ }
@@ -0,0 +1,42 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.PoliciesStorageEntity = void 0;
4
+ const tslib_1 = require("tslib");
5
+ const typeorm_1 = require("typeorm");
6
+ let PoliciesStorageEntity = class PoliciesStorageEntity {
7
+ };
8
+ exports.PoliciesStorageEntity = PoliciesStorageEntity;
9
+ tslib_1.__decorate([
10
+ (0, typeorm_1.PrimaryGeneratedColumn)(),
11
+ tslib_1.__metadata("design:type", Number)
12
+ ], PoliciesStorageEntity.prototype, "pk", void 0);
13
+ tslib_1.__decorate([
14
+ (0, typeorm_1.Index)('policy_typeorm_sid'),
15
+ (0, typeorm_1.Column)({ type: 'varchar', nullable: true }),
16
+ tslib_1.__metadata("design:type", String)
17
+ ], PoliciesStorageEntity.prototype, "sid", void 0);
18
+ tslib_1.__decorate([
19
+ (0, typeorm_1.Index)('policy_typeorm_principal_id'),
20
+ (0, typeorm_1.Column)({ type: 'varchar', nullable: true }),
21
+ tslib_1.__metadata("design:type", String)
22
+ ], PoliciesStorageEntity.prototype, "id", void 0);
23
+ tslib_1.__decorate([
24
+ (0, typeorm_1.Index)('policy_typeorm_principal_entity'),
25
+ (0, typeorm_1.Column)({ type: 'varchar', nullable: true }),
26
+ tslib_1.__metadata("design:type", String)
27
+ ], PoliciesStorageEntity.prototype, "entity", void 0);
28
+ tslib_1.__decorate([
29
+ (0, typeorm_1.Column)({ type: 'text' }),
30
+ tslib_1.__metadata("design:type", String)
31
+ ], PoliciesStorageEntity.prototype, "policy", void 0);
32
+ tslib_1.__decorate([
33
+ (0, typeorm_1.CreateDateColumn)(),
34
+ tslib_1.__metadata("design:type", Date)
35
+ ], PoliciesStorageEntity.prototype, "createdAt", void 0);
36
+ tslib_1.__decorate([
37
+ (0, typeorm_1.UpdateDateColumn)(),
38
+ tslib_1.__metadata("design:type", Date)
39
+ ], PoliciesStorageEntity.prototype, "updatedAt", void 0);
40
+ exports.PoliciesStorageEntity = PoliciesStorageEntity = tslib_1.__decorate([
41
+ (0, typeorm_1.Entity)('policies_storage')
42
+ ], PoliciesStorageEntity);
@@ -0,0 +1,6 @@
1
+ import { PrincipalObject, PolicyInterface } from '../../interfaces/policy';
2
+ export interface Storage {
3
+ savePrincipalPolicies(principal: PrincipalObject, rawPolicies: Array<string | PolicyInterface>, attach?: boolean, sid?: string): Promise<number>;
4
+ findByPrincipal(principal: PrincipalObject, sid?: string): Promise<Array<string>>;
5
+ destroyByPrincipal(principal: PrincipalObject, sid?: string): Promise<number>;
6
+ }
@@ -0,0 +1,2 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
@@ -0,0 +1,3 @@
1
+ import { BaseError } from '../../errors/iacry.error';
2
+ export declare class TypeOrmError extends BaseError {
3
+ }
@@ -0,0 +1,7 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.TypeOrmError = void 0;
4
+ const iacry_error_1 = require("../../errors/iacry.error");
5
+ class TypeOrmError extends iacry_error_1.BaseError {
6
+ }
7
+ exports.TypeOrmError = TypeOrmError;
@@ -0,0 +1,28 @@
1
+ import { PrincipalObject } from '../interfaces/policy';
2
+ import { PolicyStorage } from '../interfaces/policy-storage';
3
+ import { PolicyInterface } from '../interfaces/policy';
4
+ export interface TypeOrmRepository {
5
+ find(options: any): Promise<any[]>;
6
+ save(entities: any[]): Promise<any[]>;
7
+ delete(criteria: any): Promise<{
8
+ affected?: number;
9
+ }>;
10
+ create(entityLike: any): any;
11
+ }
12
+ export declare class TypeOrmStorage implements PolicyStorage {
13
+ private readonly storageRepository;
14
+ readonly readonly: boolean;
15
+ constructor(storageRepository: TypeOrmRepository | any);
16
+ fetch(principal: PrincipalObject): Promise<Array<string | PolicyInterface>>;
17
+ fetchBySid(sid: string, principal: PrincipalObject): Promise<Array<string | PolicyInterface>>;
18
+ save(principal: PrincipalObject, rawPolicies: Array<string | PolicyInterface>): Promise<number>;
19
+ add(_principal: PrincipalObject, rawPolicies: Array<string | PolicyInterface>): Promise<number>;
20
+ saveBySid(sid: string, principal: PrincipalObject, rawPolicies: Array<string | PolicyInterface>): Promise<number>;
21
+ purge(principal: PrincipalObject): Promise<number>;
22
+ get repository(): TypeOrmRepository;
23
+ protected isRepository(x: any): x is TypeOrmRepository;
24
+ private insertPolicies;
25
+ private destroyByPrincipal;
26
+ private buildWhere;
27
+ private normalizePrincipalField;
28
+ }
@@ -0,0 +1,115 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.TypeOrmStorage = void 0;
4
+ const core_1 = require("../helpers/core");
5
+ class TypeOrmStorage {
6
+ constructor(storageRepository) {
7
+ this.storageRepository = storageRepository;
8
+ this.readonly = false;
9
+ }
10
+ async fetch(principal) {
11
+ const entries = await this.repository.find({
12
+ where: this.buildWhere(principal),
13
+ select: ['policy'],
14
+ });
15
+ return entries.map((entry) => entry.policy);
16
+ }
17
+ async fetchBySid(sid, principal) {
18
+ const entries = await this.repository.find({
19
+ where: this.buildWhere(principal, sid),
20
+ select: ['policy'],
21
+ });
22
+ return entries.map((entry) => entry.policy);
23
+ }
24
+ async save(principal, rawPolicies) {
25
+ await this.destroyByPrincipal(principal);
26
+ return this.insertPolicies(principal, rawPolicies);
27
+ }
28
+ async add(_principal, rawPolicies) {
29
+ return this.insertPolicies(_principal, rawPolicies);
30
+ }
31
+ async saveBySid(sid, principal, rawPolicies) {
32
+ await this.destroyByPrincipal(principal, sid);
33
+ return this.insertPolicies(principal, rawPolicies);
34
+ }
35
+ async purge(principal) {
36
+ return this.destroyByPrincipal(principal);
37
+ }
38
+ get repository() {
39
+ if (!this.isRepository(this.storageRepository)) {
40
+ throw new TypeError('Policies repository must be a TypeORM Repository instance');
41
+ }
42
+ return this.storageRepository;
43
+ }
44
+ isRepository(x) {
45
+ return (x &&
46
+ typeof x === 'object' &&
47
+ typeof x.find === 'function' &&
48
+ typeof x.save === 'function' &&
49
+ typeof x.delete === 'function' &&
50
+ typeof x.create === 'function');
51
+ }
52
+ async insertPolicies(principal, rawPolicies) {
53
+ const entities = rawPolicies.map((rawPolicy) => {
54
+ const policy = typeof rawPolicy === 'string'
55
+ ? core_1.CoreHelper.decode(rawPolicy)
56
+ : rawPolicy;
57
+ return this.repository.create({
58
+ sid: policy.Sid || null,
59
+ entity: this.normalizePrincipalField(principal.entity),
60
+ id: this.normalizePrincipalField(principal.id),
61
+ policy: core_1.CoreHelper.encode(policy),
62
+ });
63
+ });
64
+ const result = await this.repository.save(entities);
65
+ return result.length;
66
+ }
67
+ async destroyByPrincipal(principal, sid) {
68
+ const where = {};
69
+ const entity = this.normalizePrincipalField(principal.entity);
70
+ const id = this.normalizePrincipalField(principal.id);
71
+ if (entity !== null) {
72
+ where.entity = entity;
73
+ }
74
+ if (id !== null) {
75
+ where.id = id;
76
+ }
77
+ if (sid) {
78
+ where.sid = sid;
79
+ }
80
+ const result = await this.repository.delete(Object.keys(where).length > 0 ? where : {});
81
+ return result.affected || 0;
82
+ }
83
+ buildWhere(principal, sid) {
84
+ const entity = this.normalizePrincipalField(principal.entity);
85
+ const id = this.normalizePrincipalField(principal.id);
86
+ const conditions = [];
87
+ const baseCondition = {};
88
+ if (sid) {
89
+ baseCondition.sid = sid;
90
+ }
91
+ if (entity !== null && id !== null) {
92
+ conditions.push({ ...baseCondition, entity, id });
93
+ conditions.push({ ...baseCondition, entity: null, id: null });
94
+ }
95
+ else if (entity !== null) {
96
+ conditions.push({ ...baseCondition, entity });
97
+ conditions.push({ ...baseCondition, entity: null, id: null });
98
+ }
99
+ else if (id !== null) {
100
+ conditions.push({ ...baseCondition, id });
101
+ conditions.push({ ...baseCondition, entity: null, id: null });
102
+ }
103
+ else {
104
+ conditions.push(baseCondition);
105
+ }
106
+ return conditions;
107
+ }
108
+ normalizePrincipalField(value) {
109
+ if (!value || value === core_1.CoreHelper.ANY) {
110
+ return null;
111
+ }
112
+ return typeof value === 'string' ? value.toLowerCase() : String(value);
113
+ }
114
+ }
115
+ exports.TypeOrmStorage = TypeOrmStorage;
package/jest.config.js CHANGED
@@ -1,17 +1,15 @@
1
- require('ts-node/register');
2
-
1
+ /** @type {import('ts-jest').JestConfigWithTsJest} */
3
2
  module.exports = {
4
- 'moduleFileExtensions': [
5
- 'js',
6
- 'json',
7
- 'ts',
8
- ],
9
- 'rootDir': 'lib',
10
- 'testRegex': '/lib/.*\\.spec\\.(ts|js)$',
11
- 'globals': {
12
- 'ts-jest': {
13
- 'tsConfig': 'tsconfig.json'
14
- }
3
+ moduleFileExtensions: ['js', 'json', 'ts'],
4
+ rootDir: 'lib',
5
+ testRegex: '/lib/.*\\.spec\\.(ts|js)$',
6
+ preset: 'ts-jest',
7
+ transform: {
8
+ '^.+\\.ts$': [
9
+ 'ts-jest',
10
+ {
11
+ tsconfig: 'tsconfig.json',
12
+ },
13
+ ],
15
14
  },
16
- 'preset': 'ts-jest',
17
15
  };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "nestjs-iacry",
3
- "version": "0.2.0",
3
+ "version": "0.2.1",
4
4
  "private": false,
5
5
  "description": "NestJS - an identity and access control module inspired by AWS IAM (@iac, @iam)",
6
6
  "keywords": [
@@ -35,65 +35,65 @@
35
35
  "patch": "npm run release -- --release-as patch",
36
36
  "release": "npx standard-version",
37
37
  "test": "npx jest",
38
- "test:watch": "npx jest --watch"
39
- },
40
- "husky": {
41
- "hooks": {
42
- "pre-commit": "lint-staged",
43
- "commit-msg": "commitlint -c .commitlintrc.json -E HUSKY_GIT_PARAMS"
44
- }
38
+ "test:watch": "npx jest --watch",
39
+ "prepare": "husky"
45
40
  },
46
41
  "lint-staged": {
47
42
  "*.ts": [
48
43
  "prettier --write"
49
44
  ]
50
45
  },
46
+ "engines": {
47
+ "node": ">=20.0.0"
48
+ },
51
49
  "dependencies": {
50
+ "clone": "^2.1.2",
52
51
  "dot-prop": "^6.0.1",
53
52
  "micromatch": "^4.0.5"
54
53
  },
55
54
  "optionalDependencies": {
56
- "ioredis": "^5.3.2",
57
- "sequelize": "6.32.1",
58
- "sequelize-typescript": "2.1.5"
55
+ "ioredis": "^5.4.0",
56
+ "sequelize": "^6.37.0",
57
+ "sequelize-typescript": "^2.1.6",
58
+ "typeorm": "^0.3.20"
59
59
  },
60
60
  "devDependencies": {
61
- "@commitlint/cli": "^17.6.6",
62
- "@commitlint/config-angular": "^17.6.6",
63
- "@golevelup/nestjs-testing": "^0.1.2",
64
- "@nestjs/cli": "^10.1.7",
65
- "@nestjs/common": "^10.0.5",
66
- "@nestjs/core": "10.0.5",
67
- "@nestjs/schematics": "^10.0.1",
68
- "@nestjs/testing": "^10.0.5",
69
- "@types/ioredis": "^4.28.10",
70
- "@types/jest": "^29.5.2",
71
- "@types/node": "^20.4.0",
72
- "@types/sequelize": "^4.28.15",
73
- "@types/supertest": "^2.0.12",
74
- "@typescript-eslint/eslint-plugin": "^5.61.0",
75
- "@typescript-eslint/parser": "^5.61.0",
76
- "eslint": "^8.44.0",
77
- "eslint-config-prettier": "^8.8.0",
78
- "eslint-plugin-import": "^2.27.5",
79
- "husky": "^8.0.3",
80
- "jest": "^29.6.1",
81
- "lint-staged": "^13.2.3",
61
+ "@commitlint/cli": "^19.0.0",
62
+ "@commitlint/config-angular": "^19.0.0",
63
+ "@nestjs/cli": "^11.0.0",
64
+ "@nestjs/common": "^11.0.0",
65
+ "@nestjs/core": "^11.0.0",
66
+ "@nestjs/schematics": "^11.0.0",
67
+ "@nestjs/testing": "^11.0.0",
68
+ "@types/clone": "^2.1.4",
69
+ "@types/jest": "^29.5.14",
70
+ "@types/micromatch": "^4.0.9",
71
+ "@types/node": "^22.0.0",
72
+ "@types/supertest": "^6.0.0",
73
+ "@typescript-eslint/eslint-plugin": "^7.0.0",
74
+ "@typescript-eslint/parser": "^7.0.0",
75
+ "eslint": "^8.56.0",
76
+ "eslint-config-prettier": "^9.0.0",
77
+ "eslint-plugin-import": "^2.31.0",
78
+ "husky": "^9.0.0",
79
+ "jest": "^29.7.0",
80
+ "lint-staged": "^15.0.0",
82
81
  "prettier": "^3.0.0",
83
- "reflect-metadata": "^0.1.13",
84
- "rimraf": "^3.0.2",
85
- "sequelize": "6.32.1",
86
- "sequelize-typescript": "2.1.5",
82
+ "reflect-metadata": "^0.2.2",
83
+ "rimraf": "^6.0.0",
84
+ "sequelize": "^6.37.0",
85
+ "sequelize-typescript": "^2.1.6",
87
86
  "standard-version": "^9.5.0",
88
- "supertest": "^6.3.3",
89
- "ts-jest": "^29.1.1",
90
- "ts-loader": "^9.4.4",
91
- "ts-node": "^10.9.1",
87
+ "supertest": "^7.0.0",
88
+ "typeorm": "^0.3.20",
89
+ "ts-jest": "^29.2.0",
90
+ "ts-loader": "^9.5.0",
91
+ "ts-node": "^10.9.2",
92
92
  "tsconfig-paths": "^4.2.0",
93
- "typescript": "^5.1.6"
93
+ "typescript": "^5.7.0"
94
94
  },
95
95
  "peerDependencies": {
96
- "@nestjs/common": "^9.2.1",
97
- "@nestjs/core": "9.2.1"
96
+ "@nestjs/common": "^9.0.0 || ^10.0.0 || ^11.0.0",
97
+ "@nestjs/core": "^9.0.0 || ^10.0.0 || ^11.0.0"
98
98
  }
99
99
  }