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.
- package/.husky/commit-msg +1 -0
- package/.husky/pre-commit +1 -0
- package/CHANGELOG.md +2 -0
- package/README.md +48 -229
- package/dist/constants.d.ts +1 -0
- package/dist/constants.js +2 -1
- package/dist/decorators/action.d.ts +1 -1
- package/dist/decorators/action.js +2 -2
- package/dist/decorators/constants.d.ts +3 -3
- package/dist/decorators/entity.js +4 -5
- package/dist/decorators/firewall.d.ts +1 -1
- package/dist/decorators/firewall.guard.js +2 -1
- package/dist/decorators/firewall.js +1 -2
- package/dist/decorators/helper.js +8 -8
- package/dist/decorators/principal.d.ts +1 -1
- package/dist/decorators/principal.js +2 -2
- package/dist/decorators/resource.d.ts +1 -1
- package/dist/decorators/resource.js +2 -2
- package/dist/helpers/core.d.ts +1 -1
- package/dist/iacry.module.js +2 -3
- package/dist/iacry.service.js +6 -1
- package/dist/index.d.ts +2 -1
- package/dist/index.js +4 -1
- package/dist/interfaces/policy.d.ts +1 -1
- package/dist/matcher.js +8 -4
- package/dist/policy.js +4 -3
- package/dist/storages/cache/ioredis.d.ts +3 -3
- package/dist/storages/global.storage.d.ts +2 -2
- package/dist/storages/sequelize/storage.model.js +2 -1
- package/dist/storages/typeorm/storage.entity.d.ts +9 -0
- package/dist/storages/typeorm/storage.entity.js +42 -0
- package/dist/storages/typeorm/storage.interface.d.ts +6 -0
- package/dist/storages/typeorm/storage.interface.js +2 -0
- package/dist/storages/typeorm/typeorm.error.d.ts +3 -0
- package/dist/storages/typeorm/typeorm.error.js +7 -0
- package/dist/storages/typeorm.storage.d.ts +28 -0
- package/dist/storages/typeorm.storage.js +115 -0
- package/jest.config.js +12 -14
- 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
|
-
|
|
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
|
-
|
|
17
|
+
## Overview
|
|
18
18
|
|
|
19
|
-
|
|
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
|
-
|
|
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
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
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
|
-
|
|
114
|
-
@IACryFirewall({ resource: 'book:{params.id}' })
|
|
115
|
-
@UseGuards(JwtAuthGuard, IACryFirewallGuard)
|
|
116
|
-
@Post('book/:id')
|
|
117
|
-
async update(@Request() req) { }
|
|
31
|
+
## Documentation
|
|
118
32
|
|
|
119
|
-
|
|
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
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
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
|
-
|
|
144
|
-
export class BookController {
|
|
145
|
-
constructor(private readonly firewall: IACryService) { }
|
|
44
|
+
## NestJS Compatibility
|
|
146
45
|
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
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
|
-
|
|
158
|
-
```typescript
|
|
159
|
-
import { IACryService, Effect } from 'nestjs-iacry';
|
|
52
|
+
## Test Coverage
|
|
160
53
|
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
249
|
-
|
|
71
|
+
# Build
|
|
72
|
+
npm run build
|
|
73
|
+
|
|
74
|
+
# Release
|
|
250
75
|
npm run format
|
|
251
|
-
npm run release
|
|
76
|
+
npm run release # or: npm run patch | minor | major
|
|
252
77
|
npm run deploy
|
|
253
78
|
```
|
|
254
79
|
|
|
255
|
-
|
|
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
|
-
|
|
82
|
+
Contributions are welcome! Please open an issue or submit a pull request.
|
|
264
83
|
|
|
265
|
-
|
|
84
|
+
- [Alex Cucer](https://github.com/AlexanderC)
|
|
266
85
|
|
|
267
|
-
|
|
86
|
+
## License
|
|
268
87
|
|
|
269
88
|
MIT
|
package/dist/constants.d.ts
CHANGED
|
@@ -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.
|
|
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
|
|
6
|
-
export declare const RESOURCE_META_FIELD
|
|
7
|
-
export declare const PRINCIPAL_META_FIELD
|
|
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.
|
|
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:
|
|
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 =
|
|
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 =
|
|
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.
|
|
4
|
-
|
|
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
|
-
|
|
15
|
-
varPath =
|
|
16
|
-
if (!
|
|
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 =
|
|
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.
|
|
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.
|
|
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;
|
package/dist/helpers/core.d.ts
CHANGED
|
@@ -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>;
|
package/dist/iacry.module.js
CHANGED
|
@@ -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 =
|
|
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)({})
|
package/dist/iacry.service.js
CHANGED
|
@@ -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 =
|
|
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(
|
|
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
|
|
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
|
-
|
|
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
|
|
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
|
|
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
|
|
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 =
|
|
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 =
|
|
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
|
|
2
|
+
import Redis from 'ioredis';
|
|
3
3
|
export declare class IoRedis implements Cache {
|
|
4
|
-
readonly client:
|
|
4
|
+
readonly client: Redis;
|
|
5
5
|
private readonly SUCCESS;
|
|
6
6
|
private readonly EXPIRE;
|
|
7
|
-
constructor(client:
|
|
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:
|
|
5
|
+
policies: Array<string | PolicyInterface>;
|
|
6
6
|
readonly readonly: boolean;
|
|
7
|
-
constructor(policies?:
|
|
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 =
|
|
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,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,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
|
-
|
|
2
|
-
|
|
1
|
+
/** @type {import('ts-jest').JestConfigWithTsJest} */
|
|
3
2
|
module.exports = {
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
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.
|
|
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.
|
|
57
|
-
"sequelize": "6.
|
|
58
|
-
"sequelize-typescript": "2.1.
|
|
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": "^
|
|
62
|
-
"@commitlint/config-angular": "^
|
|
63
|
-
"@
|
|
64
|
-
"@nestjs/
|
|
65
|
-
"@nestjs/
|
|
66
|
-
"@nestjs/
|
|
67
|
-
"@nestjs/
|
|
68
|
-
"@
|
|
69
|
-
"@types/
|
|
70
|
-
"@types/
|
|
71
|
-
"@types/node": "^
|
|
72
|
-
"@types/
|
|
73
|
-
"@
|
|
74
|
-
"@typescript-eslint/
|
|
75
|
-
"
|
|
76
|
-
"eslint": "^
|
|
77
|
-
"eslint-
|
|
78
|
-
"
|
|
79
|
-
"
|
|
80
|
-
"
|
|
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.
|
|
84
|
-
"rimraf": "^
|
|
85
|
-
"sequelize": "6.
|
|
86
|
-
"sequelize-typescript": "2.1.
|
|
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": "^
|
|
89
|
-
"
|
|
90
|
-
"ts-
|
|
91
|
-
"ts-
|
|
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.
|
|
93
|
+
"typescript": "^5.7.0"
|
|
94
94
|
},
|
|
95
95
|
"peerDependencies": {
|
|
96
|
-
"@nestjs/common": "^9.
|
|
97
|
-
"@nestjs/core": "9.
|
|
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
|
}
|