@application-kit/authorization 0.1.0 → 0.3.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +15 -0
- package/README.md +12 -9
- package/dist/cjs/nest/authorization.module.d.ts +8 -0
- package/dist/cjs/nest/authorization.module.js +35 -0
- package/dist/cjs/nest/current-actor.decorator.d.ts +1 -0
- package/dist/cjs/nest/current-actor.decorator.js +7 -0
- package/dist/cjs/nest/index.d.ts +4 -0
- package/dist/cjs/nest/index.js +4 -0
- package/dist/cjs/nest/internal/index.d.ts +3 -0
- package/dist/cjs/nest/internal/index.js +19 -0
- package/dist/cjs/nest/internal/is-graphql-context.d.ts +2 -0
- package/dist/cjs/nest/internal/is-graphql-context.js +9 -0
- package/dist/cjs/nest/internal/is-public-handler.d.ts +3 -0
- package/dist/cjs/nest/internal/is-public-handler.js +9 -0
- package/dist/cjs/nest/internal/resolve-execution-actor.d.ts +3 -0
- package/dist/cjs/nest/internal/resolve-execution-actor.js +9 -0
- package/dist/cjs/nest/policies.guard.d.ts +0 -1
- package/dist/cjs/nest/policies.guard.js +8 -18
- package/dist/cjs/nest/resolve-execution-request.d.ts +2 -0
- package/dist/cjs/nest/resolve-execution-request.js +9 -0
- package/dist/cjs/nest/with-public-routes.d.ts +2 -0
- package/dist/cjs/nest/with-public-routes.js +36 -0
- package/dist/esm/nest/authorization.module.d.ts +8 -0
- package/dist/esm/nest/authorization.module.js +32 -0
- package/dist/esm/nest/current-actor.decorator.d.ts +1 -0
- package/dist/esm/nest/current-actor.decorator.js +4 -0
- package/dist/esm/nest/index.d.ts +4 -0
- package/dist/esm/nest/index.js +4 -0
- package/dist/esm/nest/internal/index.d.ts +3 -0
- package/dist/esm/nest/internal/index.js +3 -0
- package/dist/esm/nest/internal/is-graphql-context.d.ts +2 -0
- package/dist/esm/nest/internal/is-graphql-context.js +5 -0
- package/dist/esm/nest/internal/is-public-handler.d.ts +3 -0
- package/dist/esm/nest/internal/is-public-handler.js +5 -0
- package/dist/esm/nest/internal/resolve-execution-actor.d.ts +3 -0
- package/dist/esm/nest/internal/resolve-execution-actor.js +5 -0
- package/dist/esm/nest/policies.guard.d.ts +0 -1
- package/dist/esm/nest/policies.guard.js +8 -18
- package/dist/esm/nest/resolve-execution-request.d.ts +2 -0
- package/dist/esm/nest/resolve-execution-request.js +5 -0
- package/dist/esm/nest/with-public-routes.d.ts +2 -0
- package/dist/esm/nest/with-public-routes.js +32 -0
- package/package.json +2 -2
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,20 @@
|
|
|
1
1
|
# @application-kit/authorization
|
|
2
2
|
|
|
3
|
+
## 0.3.0
|
|
4
|
+
|
|
5
|
+
### Minor Changes
|
|
6
|
+
|
|
7
|
+
- ef902ec: Вход `/nest`: `CurrentActor()` — актор параметром обработчика, `withPublicRoutes(AuthGuard('jwt'))` — guard аутентификации с учётом `@Public()`, `resolveExecutionRequest` — чтение запроса без развилки HTTP/GraphQL. Новых зависимостей нет: `@nestjs/graphql` и `@nestjs/passport` пакету не нужны.
|
|
8
|
+
|
|
9
|
+
## 0.2.0
|
|
10
|
+
|
|
11
|
+
### Minor Changes
|
|
12
|
+
|
|
13
|
+
- 4bcfbde: Вход `/nest`: `AuthorizationModule.forRoot({ grants })` — динамический модуль с `AbilityFactory` (токен `ABILITY_FACTORY`),
|
|
14
|
+
картой грантов `ABILITY_GRANTS`, pino-логгером `AUTHORIZATION_LOGGER` и глобальным `PoliciesGuard` (`APP_GUARD`);
|
|
15
|
+
экспортирует `ABILITY_FACTORY`, `ABILITY_GRANTS`, `AbilityFactory`. Ручная проводка провайдеров в приложении больше не нужна.
|
|
16
|
+
`PoliciesGuard` получает `Reflector` по явному токену — модуль собирается и без `emitDecoratorMetadata`.
|
|
17
|
+
|
|
3
18
|
## 0.1.0
|
|
4
19
|
|
|
5
20
|
### Minor Changes
|
package/README.md
CHANGED
|
@@ -52,19 +52,22 @@ const ability = buildAbilityFor({ role: 'EDITOR' }, grants)
|
|
|
52
52
|
проверяется конъюнкцией политик над ability актора из `request.user`. GraphQL-резолвер без обоих декораторов
|
|
53
53
|
запрещён (fail-closed) с предупреждением в лог, REST-обработчик — пропускается: его авторизация на маршруте.
|
|
54
54
|
- `CheckPolicies(...handlers)`, `PolicyHandler`, `permitAuthenticated` — политики обработчика.
|
|
55
|
-
- `Public()`, `IS_PUBLIC_KEY` — открытый
|
|
55
|
+
- `Public()`, `IS_PUBLIC_KEY` — открытый обработчик.
|
|
56
|
+
- `withPublicRoutes(AuthGuard('jwt'))` — guard аутентификации приложения поверх его базы: открытый обработчик пропускает
|
|
57
|
+
мимо базы, а запрос ей отдаёт и на HTTP, и в GraphQL. `@nestjs/passport` пакету не нужен.
|
|
58
|
+
- `CurrentActor()` — параметр обработчика с `request.user`; на открытом обработчике актора может не быть.
|
|
59
|
+
- `resolveExecutionRequest(context)` — запрос без развилки транспорта. `@nestjs/graphql` пакету не нужен.
|
|
56
60
|
- `AbilityFactory` — ability актора по карте грантов.
|
|
57
61
|
|
|
58
|
-
|
|
62
|
+
Собирает механизм `AuthorizationModule.forRoot({ grants })`: фабрика ability, логгер и глобальный guard (`APP_GUARD`)
|
|
63
|
+
провайдятся пакетом, приложение приносит только карту грантов. Наружу модуль отдаёт `ABILITY_FACTORY`, `ABILITY_GRANTS`
|
|
64
|
+
и `AbilityFactory` — для кода, который строит ability актора сам (например, резолвер, отдающий её клиенту).
|
|
59
65
|
|
|
60
66
|
```ts
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
{ provide: AUTHORIZATION_LOGGER, useClass: AuthorizationLogger },
|
|
66
|
-
{ provide: APP_GUARD, useClass: PoliciesGuard },
|
|
67
|
-
]
|
|
67
|
+
@Module({
|
|
68
|
+
imports: [AuthorizationModule.forRoot({ grants })],
|
|
69
|
+
})
|
|
70
|
+
export class AppModule {}
|
|
68
71
|
```
|
|
69
72
|
|
|
70
73
|
`AuthorizationLogger` — pino-логгер пакета с контекстом `AuthorizationLogger`; собирается из глобального
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
import type { DynamicModule } from '@nestjs/common';
|
|
2
|
+
import type { AbilityGrantMap } from '../types/ability-grant.js';
|
|
3
|
+
export interface AuthorizationModuleOptionsInterface {
|
|
4
|
+
readonly grants: AbilityGrantMap;
|
|
5
|
+
}
|
|
6
|
+
export declare class AuthorizationModule {
|
|
7
|
+
static forRoot(options: AuthorizationModuleOptionsInterface): DynamicModule;
|
|
8
|
+
}
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) {
|
|
3
|
+
var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
|
|
4
|
+
if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
|
|
5
|
+
else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
|
|
6
|
+
return c > 3 && r && Object.defineProperty(target, key, r), r;
|
|
7
|
+
};
|
|
8
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
9
|
+
exports.AuthorizationModule = void 0;
|
|
10
|
+
const common_1 = require("@nestjs/common");
|
|
11
|
+
const core_1 = require("@nestjs/core");
|
|
12
|
+
const ability_factory_js_1 = require("./ability.factory.js");
|
|
13
|
+
const authorization_logger_js_1 = require("./authorization-logger.js");
|
|
14
|
+
const constants_js_1 = require("./constants.js");
|
|
15
|
+
const policies_guard_js_1 = require("./policies.guard.js");
|
|
16
|
+
let AuthorizationModule = class AuthorizationModule {
|
|
17
|
+
static forRoot(options) {
|
|
18
|
+
return {
|
|
19
|
+
exports: [constants_js_1.ABILITY_FACTORY, constants_js_1.ABILITY_GRANTS, ability_factory_js_1.AbilityFactory],
|
|
20
|
+
module: this,
|
|
21
|
+
providers: [
|
|
22
|
+
ability_factory_js_1.AbilityFactory,
|
|
23
|
+
{ provide: constants_js_1.ABILITY_FACTORY, useExisting: ability_factory_js_1.AbilityFactory },
|
|
24
|
+
{ provide: constants_js_1.ABILITY_GRANTS, useValue: options.grants },
|
|
25
|
+
{ provide: constants_js_1.AUTHORIZATION_LOGGER, useClass: authorization_logger_js_1.AuthorizationLogger },
|
|
26
|
+
policies_guard_js_1.PoliciesGuard,
|
|
27
|
+
{ provide: core_1.APP_GUARD, useExisting: policies_guard_js_1.PoliciesGuard },
|
|
28
|
+
],
|
|
29
|
+
};
|
|
30
|
+
}
|
|
31
|
+
};
|
|
32
|
+
exports.AuthorizationModule = AuthorizationModule;
|
|
33
|
+
exports.AuthorizationModule = AuthorizationModule = __decorate([
|
|
34
|
+
(0, common_1.Module)({})
|
|
35
|
+
], AuthorizationModule);
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export declare const CurrentActor: (...dataOrPipes: unknown[]) => ParameterDecorator;
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.CurrentActor = void 0;
|
|
4
|
+
const common_1 = require("@nestjs/common");
|
|
5
|
+
const resolve_execution_actor_js_1 = require("./internal/resolve-execution-actor.js");
|
|
6
|
+
const currentActorFactory = (_, context) => (0, resolve_execution_actor_js_1.resolveExecutionActor)(context);
|
|
7
|
+
exports.CurrentActor = (0, common_1.createParamDecorator)(currentActorFactory);
|
package/dist/cjs/nest/index.d.ts
CHANGED
|
@@ -1,8 +1,12 @@
|
|
|
1
1
|
export * from './ability.factory.js';
|
|
2
2
|
export * from './authorization-logger.js';
|
|
3
3
|
export * from './authorization-logger.interface.js';
|
|
4
|
+
export * from './authorization.module.js';
|
|
4
5
|
export * from './check-policies.decorator.js';
|
|
5
6
|
export * from './constants.js';
|
|
7
|
+
export * from './current-actor.decorator.js';
|
|
6
8
|
export * from './policies.guard.js';
|
|
7
9
|
export * from './policy-handlers.js';
|
|
8
10
|
export * from './public.decorator.js';
|
|
11
|
+
export * from './resolve-execution-request.js';
|
|
12
|
+
export * from './with-public-routes.js';
|
package/dist/cjs/nest/index.js
CHANGED
|
@@ -17,8 +17,12 @@ Object.defineProperty(exports, "__esModule", { value: true });
|
|
|
17
17
|
__exportStar(require("./ability.factory.js"), exports);
|
|
18
18
|
__exportStar(require("./authorization-logger.js"), exports);
|
|
19
19
|
__exportStar(require("./authorization-logger.interface.js"), exports);
|
|
20
|
+
__exportStar(require("./authorization.module.js"), exports);
|
|
20
21
|
__exportStar(require("./check-policies.decorator.js"), exports);
|
|
21
22
|
__exportStar(require("./constants.js"), exports);
|
|
23
|
+
__exportStar(require("./current-actor.decorator.js"), exports);
|
|
22
24
|
__exportStar(require("./policies.guard.js"), exports);
|
|
23
25
|
__exportStar(require("./policy-handlers.js"), exports);
|
|
24
26
|
__exportStar(require("./public.decorator.js"), exports);
|
|
27
|
+
__exportStar(require("./resolve-execution-request.js"), exports);
|
|
28
|
+
__exportStar(require("./with-public-routes.js"), exports);
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
3
|
+
if (k2 === undefined) k2 = k;
|
|
4
|
+
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
5
|
+
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
6
|
+
desc = { enumerable: true, get: function() { return m[k]; } };
|
|
7
|
+
}
|
|
8
|
+
Object.defineProperty(o, k2, desc);
|
|
9
|
+
}) : (function(o, m, k, k2) {
|
|
10
|
+
if (k2 === undefined) k2 = k;
|
|
11
|
+
o[k2] = m[k];
|
|
12
|
+
}));
|
|
13
|
+
var __exportStar = (this && this.__exportStar) || function(m, exports) {
|
|
14
|
+
for (var p in m) if (p !== "default" && !Object.prototype.hasOwnProperty.call(exports, p)) __createBinding(exports, m, p);
|
|
15
|
+
};
|
|
16
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
17
|
+
__exportStar(require("./is-graphql-context.js"), exports);
|
|
18
|
+
__exportStar(require("./is-public-handler.js"), exports);
|
|
19
|
+
__exportStar(require("./resolve-execution-actor.js"), exports);
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.isGraphqlContext = void 0;
|
|
4
|
+
const GRAPHQL_CONTEXT_TYPE = 'graphql';
|
|
5
|
+
const isGraphqlContext = (context) => {
|
|
6
|
+
const transport = context.getType();
|
|
7
|
+
return transport === GRAPHQL_CONTEXT_TYPE;
|
|
8
|
+
};
|
|
9
|
+
exports.isGraphqlContext = isGraphqlContext;
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.isPublicHandler = void 0;
|
|
4
|
+
const public_decorator_js_1 = require("../public.decorator.js");
|
|
5
|
+
const isPublicHandler = (reflector, context) => {
|
|
6
|
+
const targets = [context.getHandler(), context.getClass()];
|
|
7
|
+
return reflector.getAllAndOverride(public_decorator_js_1.IS_PUBLIC_KEY, targets) === true;
|
|
8
|
+
};
|
|
9
|
+
exports.isPublicHandler = isPublicHandler;
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.resolveExecutionActor = void 0;
|
|
4
|
+
const resolve_execution_request_js_1 = require("../resolve-execution-request.js");
|
|
5
|
+
const resolveExecutionActor = (context) => {
|
|
6
|
+
const request = (0, resolve_execution_request_js_1.resolveExecutionRequest)(context);
|
|
7
|
+
return request?.user;
|
|
8
|
+
};
|
|
9
|
+
exports.resolveExecutionActor = resolveExecutionActor;
|
|
@@ -17,10 +17,9 @@ const common_1 = require("@nestjs/common");
|
|
|
17
17
|
const core_1 = require("@nestjs/core");
|
|
18
18
|
const check_policies_decorator_js_1 = require("./check-policies.decorator.js");
|
|
19
19
|
const constants_js_1 = require("./constants.js");
|
|
20
|
-
const
|
|
21
|
-
const
|
|
22
|
-
const
|
|
23
|
-
const isGraphql = (context) => context.getType() === GRAPHQL_CONTEXT_TYPE;
|
|
20
|
+
const is_graphql_context_js_1 = require("./internal/is-graphql-context.js");
|
|
21
|
+
const is_public_handler_js_1 = require("./internal/is-public-handler.js");
|
|
22
|
+
const resolve_execution_actor_js_1 = require("./internal/resolve-execution-actor.js");
|
|
24
23
|
let PoliciesGuard = class PoliciesGuard {
|
|
25
24
|
reflector;
|
|
26
25
|
abilityFactory;
|
|
@@ -31,26 +30,22 @@ let PoliciesGuard = class PoliciesGuard {
|
|
|
31
30
|
this.logger = logger;
|
|
32
31
|
}
|
|
33
32
|
canActivate(context) {
|
|
34
|
-
|
|
35
|
-
context.getHandler(),
|
|
36
|
-
context.getClass(),
|
|
37
|
-
]);
|
|
38
|
-
if (isPublic === true) {
|
|
33
|
+
if ((0, is_public_handler_js_1.isPublicHandler)(this.reflector, context)) {
|
|
39
34
|
return true;
|
|
40
35
|
}
|
|
41
36
|
const handlers = this.reflector.getAllAndOverride(check_policies_decorator_js_1.CHECK_POLICIES_KEY, [context.getHandler(), context.getClass()]) ?? [];
|
|
42
37
|
return handlers.length === 0 ? this.allowWithoutPolicies(context) : this.enforcePolicies({ context, handlers });
|
|
43
38
|
}
|
|
44
39
|
allowWithoutPolicies(context) {
|
|
45
|
-
if (
|
|
40
|
+
if ((0, is_graphql_context_js_1.isGraphqlContext)(context)) {
|
|
46
41
|
this.logger.warn({ class: context.getClass().name, handler: context.getHandler().name }, 'PoliciesGuard deny-by-default — GraphQL resolver missing @Public/@CheckPolicies');
|
|
47
42
|
throw new common_1.ForbiddenException('Forbidden by policy');
|
|
48
43
|
}
|
|
49
44
|
return true;
|
|
50
45
|
}
|
|
51
46
|
enforcePolicies(parameters) {
|
|
52
|
-
const actor =
|
|
53
|
-
if (actor ===
|
|
47
|
+
const actor = (0, resolve_execution_actor_js_1.resolveExecutionActor)(parameters.context);
|
|
48
|
+
if (actor === undefined) {
|
|
54
49
|
throw new common_1.ForbiddenException('No authenticated user');
|
|
55
50
|
}
|
|
56
51
|
const ability = this.abilityFactory.createForActor({
|
|
@@ -62,16 +57,11 @@ let PoliciesGuard = class PoliciesGuard {
|
|
|
62
57
|
}
|
|
63
58
|
return true;
|
|
64
59
|
}
|
|
65
|
-
extractActor(context) {
|
|
66
|
-
const request = isGraphql(context)
|
|
67
|
-
? context.getArgByIndex(GRAPHQL_CONTEXT_ARGUMENT_INDEX).req
|
|
68
|
-
: context.switchToHttp().getRequest();
|
|
69
|
-
return request?.user ?? null;
|
|
70
|
-
}
|
|
71
60
|
};
|
|
72
61
|
exports.PoliciesGuard = PoliciesGuard;
|
|
73
62
|
exports.PoliciesGuard = PoliciesGuard = __decorate([
|
|
74
63
|
(0, common_1.Injectable)(),
|
|
64
|
+
__param(0, (0, common_1.Inject)(core_1.Reflector)),
|
|
75
65
|
__param(1, (0, common_1.Inject)(constants_js_1.ABILITY_FACTORY)),
|
|
76
66
|
__param(2, (0, common_1.Inject)(constants_js_1.AUTHORIZATION_LOGGER)),
|
|
77
67
|
__metadata("design:paramtypes", [core_1.Reflector, Function, Object])
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.resolveExecutionRequest = void 0;
|
|
4
|
+
const is_graphql_context_js_1 = require("./internal/is-graphql-context.js");
|
|
5
|
+
const GRAPHQL_CONTEXT_ARGUMENT_INDEX = 2;
|
|
6
|
+
const resolveExecutionRequest = (context) => ((0, is_graphql_context_js_1.isGraphqlContext)(context)
|
|
7
|
+
? context.getArgByIndex(GRAPHQL_CONTEXT_ARGUMENT_INDEX).req
|
|
8
|
+
: context.switchToHttp().getRequest());
|
|
9
|
+
exports.resolveExecutionRequest = resolveExecutionRequest;
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) {
|
|
3
|
+
var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
|
|
4
|
+
if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
|
|
5
|
+
else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
|
|
6
|
+
return c > 3 && r && Object.defineProperty(target, key, r), r;
|
|
7
|
+
};
|
|
8
|
+
var __metadata = (this && this.__metadata) || function (k, v) {
|
|
9
|
+
if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v);
|
|
10
|
+
};
|
|
11
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
12
|
+
exports.withPublicRoutes = void 0;
|
|
13
|
+
const common_1 = require("@nestjs/common");
|
|
14
|
+
const core_1 = require("@nestjs/core");
|
|
15
|
+
const is_public_handler_js_1 = require("./internal/is-public-handler.js");
|
|
16
|
+
const resolve_execution_request_js_1 = require("./resolve-execution-request.js");
|
|
17
|
+
const withPublicRoutes = (base) => {
|
|
18
|
+
let PublicRoutesGuard = class PublicRoutesGuard extends base {
|
|
19
|
+
reflector;
|
|
20
|
+
canActivate(context) {
|
|
21
|
+
return (0, is_public_handler_js_1.isPublicHandler)(this.reflector, context) || super.canActivate(context);
|
|
22
|
+
}
|
|
23
|
+
getRequest(context) {
|
|
24
|
+
return (0, resolve_execution_request_js_1.resolveExecutionRequest)(context);
|
|
25
|
+
}
|
|
26
|
+
};
|
|
27
|
+
__decorate([
|
|
28
|
+
(0, common_1.Inject)(core_1.Reflector),
|
|
29
|
+
__metadata("design:type", core_1.Reflector)
|
|
30
|
+
], PublicRoutesGuard.prototype, "reflector", void 0);
|
|
31
|
+
PublicRoutesGuard = __decorate([
|
|
32
|
+
(0, common_1.Injectable)()
|
|
33
|
+
], PublicRoutesGuard);
|
|
34
|
+
return PublicRoutesGuard;
|
|
35
|
+
};
|
|
36
|
+
exports.withPublicRoutes = withPublicRoutes;
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
import type { DynamicModule } from '@nestjs/common';
|
|
2
|
+
import type { AbilityGrantMap } from '../types/ability-grant.js';
|
|
3
|
+
export interface AuthorizationModuleOptionsInterface {
|
|
4
|
+
readonly grants: AbilityGrantMap;
|
|
5
|
+
}
|
|
6
|
+
export declare class AuthorizationModule {
|
|
7
|
+
static forRoot(options: AuthorizationModuleOptionsInterface): DynamicModule;
|
|
8
|
+
}
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) {
|
|
2
|
+
var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
|
|
3
|
+
if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
|
|
4
|
+
else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
|
|
5
|
+
return c > 3 && r && Object.defineProperty(target, key, r), r;
|
|
6
|
+
};
|
|
7
|
+
import { Module } from '@nestjs/common';
|
|
8
|
+
import { APP_GUARD } from '@nestjs/core';
|
|
9
|
+
import { AbilityFactory } from './ability.factory.js';
|
|
10
|
+
import { AuthorizationLogger } from './authorization-logger.js';
|
|
11
|
+
import { ABILITY_FACTORY, ABILITY_GRANTS, AUTHORIZATION_LOGGER } from './constants.js';
|
|
12
|
+
import { PoliciesGuard } from './policies.guard.js';
|
|
13
|
+
let AuthorizationModule = class AuthorizationModule {
|
|
14
|
+
static forRoot(options) {
|
|
15
|
+
return {
|
|
16
|
+
exports: [ABILITY_FACTORY, ABILITY_GRANTS, AbilityFactory],
|
|
17
|
+
module: this,
|
|
18
|
+
providers: [
|
|
19
|
+
AbilityFactory,
|
|
20
|
+
{ provide: ABILITY_FACTORY, useExisting: AbilityFactory },
|
|
21
|
+
{ provide: ABILITY_GRANTS, useValue: options.grants },
|
|
22
|
+
{ provide: AUTHORIZATION_LOGGER, useClass: AuthorizationLogger },
|
|
23
|
+
PoliciesGuard,
|
|
24
|
+
{ provide: APP_GUARD, useExisting: PoliciesGuard },
|
|
25
|
+
],
|
|
26
|
+
};
|
|
27
|
+
}
|
|
28
|
+
};
|
|
29
|
+
AuthorizationModule = __decorate([
|
|
30
|
+
Module({})
|
|
31
|
+
], AuthorizationModule);
|
|
32
|
+
export { AuthorizationModule };
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export declare const CurrentActor: (...dataOrPipes: unknown[]) => ParameterDecorator;
|
|
@@ -0,0 +1,4 @@
|
|
|
1
|
+
import { createParamDecorator } from '@nestjs/common';
|
|
2
|
+
import { resolveExecutionActor } from './internal/resolve-execution-actor.js';
|
|
3
|
+
const currentActorFactory = (_, context) => resolveExecutionActor(context);
|
|
4
|
+
export const CurrentActor = createParamDecorator(currentActorFactory);
|
package/dist/esm/nest/index.d.ts
CHANGED
|
@@ -1,8 +1,12 @@
|
|
|
1
1
|
export * from './ability.factory.js';
|
|
2
2
|
export * from './authorization-logger.js';
|
|
3
3
|
export * from './authorization-logger.interface.js';
|
|
4
|
+
export * from './authorization.module.js';
|
|
4
5
|
export * from './check-policies.decorator.js';
|
|
5
6
|
export * from './constants.js';
|
|
7
|
+
export * from './current-actor.decorator.js';
|
|
6
8
|
export * from './policies.guard.js';
|
|
7
9
|
export * from './policy-handlers.js';
|
|
8
10
|
export * from './public.decorator.js';
|
|
11
|
+
export * from './resolve-execution-request.js';
|
|
12
|
+
export * from './with-public-routes.js';
|
package/dist/esm/nest/index.js
CHANGED
|
@@ -1,8 +1,12 @@
|
|
|
1
1
|
export * from './ability.factory.js';
|
|
2
2
|
export * from './authorization-logger.js';
|
|
3
3
|
export * from './authorization-logger.interface.js';
|
|
4
|
+
export * from './authorization.module.js';
|
|
4
5
|
export * from './check-policies.decorator.js';
|
|
5
6
|
export * from './constants.js';
|
|
7
|
+
export * from './current-actor.decorator.js';
|
|
6
8
|
export * from './policies.guard.js';
|
|
7
9
|
export * from './policy-handlers.js';
|
|
8
10
|
export * from './public.decorator.js';
|
|
11
|
+
export * from './resolve-execution-request.js';
|
|
12
|
+
export * from './with-public-routes.js';
|
|
@@ -14,10 +14,9 @@ import { ForbiddenException, Inject, Injectable, } from '@nestjs/common';
|
|
|
14
14
|
import { Reflector } from '@nestjs/core';
|
|
15
15
|
import { CHECK_POLICIES_KEY } from './check-policies.decorator.js';
|
|
16
16
|
import { ABILITY_FACTORY, AUTHORIZATION_LOGGER } from './constants.js';
|
|
17
|
-
import {
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
const isGraphql = (context) => context.getType() === GRAPHQL_CONTEXT_TYPE;
|
|
17
|
+
import { isGraphqlContext } from './internal/is-graphql-context.js';
|
|
18
|
+
import { isPublicHandler } from './internal/is-public-handler.js';
|
|
19
|
+
import { resolveExecutionActor } from './internal/resolve-execution-actor.js';
|
|
21
20
|
let PoliciesGuard = class PoliciesGuard {
|
|
22
21
|
reflector;
|
|
23
22
|
abilityFactory;
|
|
@@ -28,26 +27,22 @@ let PoliciesGuard = class PoliciesGuard {
|
|
|
28
27
|
this.logger = logger;
|
|
29
28
|
}
|
|
30
29
|
canActivate(context) {
|
|
31
|
-
|
|
32
|
-
context.getHandler(),
|
|
33
|
-
context.getClass(),
|
|
34
|
-
]);
|
|
35
|
-
if (isPublic === true) {
|
|
30
|
+
if (isPublicHandler(this.reflector, context)) {
|
|
36
31
|
return true;
|
|
37
32
|
}
|
|
38
33
|
const handlers = this.reflector.getAllAndOverride(CHECK_POLICIES_KEY, [context.getHandler(), context.getClass()]) ?? [];
|
|
39
34
|
return handlers.length === 0 ? this.allowWithoutPolicies(context) : this.enforcePolicies({ context, handlers });
|
|
40
35
|
}
|
|
41
36
|
allowWithoutPolicies(context) {
|
|
42
|
-
if (
|
|
37
|
+
if (isGraphqlContext(context)) {
|
|
43
38
|
this.logger.warn({ class: context.getClass().name, handler: context.getHandler().name }, 'PoliciesGuard deny-by-default — GraphQL resolver missing @Public/@CheckPolicies');
|
|
44
39
|
throw new ForbiddenException('Forbidden by policy');
|
|
45
40
|
}
|
|
46
41
|
return true;
|
|
47
42
|
}
|
|
48
43
|
enforcePolicies(parameters) {
|
|
49
|
-
const actor =
|
|
50
|
-
if (actor ===
|
|
44
|
+
const actor = resolveExecutionActor(parameters.context);
|
|
45
|
+
if (actor === undefined) {
|
|
51
46
|
throw new ForbiddenException('No authenticated user');
|
|
52
47
|
}
|
|
53
48
|
const ability = this.abilityFactory.createForActor({
|
|
@@ -59,15 +54,10 @@ let PoliciesGuard = class PoliciesGuard {
|
|
|
59
54
|
}
|
|
60
55
|
return true;
|
|
61
56
|
}
|
|
62
|
-
extractActor(context) {
|
|
63
|
-
const request = isGraphql(context)
|
|
64
|
-
? context.getArgByIndex(GRAPHQL_CONTEXT_ARGUMENT_INDEX).req
|
|
65
|
-
: context.switchToHttp().getRequest();
|
|
66
|
-
return request?.user ?? null;
|
|
67
|
-
}
|
|
68
57
|
};
|
|
69
58
|
PoliciesGuard = __decorate([
|
|
70
59
|
Injectable(),
|
|
60
|
+
__param(0, Inject(Reflector)),
|
|
71
61
|
__param(1, Inject(ABILITY_FACTORY)),
|
|
72
62
|
__param(2, Inject(AUTHORIZATION_LOGGER)),
|
|
73
63
|
__metadata("design:paramtypes", [Reflector, Function, Object])
|
|
@@ -0,0 +1,5 @@
|
|
|
1
|
+
import { isGraphqlContext } from './internal/is-graphql-context.js';
|
|
2
|
+
const GRAPHQL_CONTEXT_ARGUMENT_INDEX = 2;
|
|
3
|
+
export const resolveExecutionRequest = (context) => (isGraphqlContext(context)
|
|
4
|
+
? context.getArgByIndex(GRAPHQL_CONTEXT_ARGUMENT_INDEX).req
|
|
5
|
+
: context.switchToHttp().getRequest());
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) {
|
|
2
|
+
var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
|
|
3
|
+
if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
|
|
4
|
+
else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
|
|
5
|
+
return c > 3 && r && Object.defineProperty(target, key, r), r;
|
|
6
|
+
};
|
|
7
|
+
var __metadata = (this && this.__metadata) || function (k, v) {
|
|
8
|
+
if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v);
|
|
9
|
+
};
|
|
10
|
+
import { Inject, Injectable } from '@nestjs/common';
|
|
11
|
+
import { Reflector } from '@nestjs/core';
|
|
12
|
+
import { isPublicHandler } from './internal/is-public-handler.js';
|
|
13
|
+
import { resolveExecutionRequest } from './resolve-execution-request.js';
|
|
14
|
+
export const withPublicRoutes = (base) => {
|
|
15
|
+
let PublicRoutesGuard = class PublicRoutesGuard extends base {
|
|
16
|
+
reflector;
|
|
17
|
+
canActivate(context) {
|
|
18
|
+
return isPublicHandler(this.reflector, context) || super.canActivate(context);
|
|
19
|
+
}
|
|
20
|
+
getRequest(context) {
|
|
21
|
+
return resolveExecutionRequest(context);
|
|
22
|
+
}
|
|
23
|
+
};
|
|
24
|
+
__decorate([
|
|
25
|
+
Inject(Reflector),
|
|
26
|
+
__metadata("design:type", Reflector)
|
|
27
|
+
], PublicRoutesGuard.prototype, "reflector", void 0);
|
|
28
|
+
PublicRoutesGuard = __decorate([
|
|
29
|
+
Injectable()
|
|
30
|
+
], PublicRoutesGuard);
|
|
31
|
+
return PublicRoutesGuard;
|
|
32
|
+
};
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@application-kit/authorization",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.3.0",
|
|
4
4
|
"description": "Авторизация на CASL для NestJS и браузера: реестры subject'ов и ролей, ability из грантов модулей, глобальный guard политик",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"authorization",
|
|
@@ -51,7 +51,7 @@
|
|
|
51
51
|
"dist"
|
|
52
52
|
],
|
|
53
53
|
"scripts": {
|
|
54
|
-
"build": "rm -rf ./dist && node ../../scripts/barrels.mjs create ./src --entry-only=nest && tsc --project tsconfig.build.json && tsc --project tsconfig.build.cjs.json && node ../../scripts/barrels.mjs clean ./src && node ../../scripts/mark-cjs.mjs ./dist/cjs",
|
|
54
|
+
"build": "rm -rf ./dist && node ../../scripts/barrels.mjs create ./src --entry-only=nest,internal && tsc --project tsconfig.build.json && tsc --project tsconfig.build.cjs.json && node ../../scripts/barrels.mjs clean ./src && node ../../scripts/mark-cjs.mjs ./dist/cjs",
|
|
55
55
|
"lint:tsc": "tsc --noEmit --pretty --project .",
|
|
56
56
|
"test:mutation": "stryker run",
|
|
57
57
|
"test:unit": "vitest run"
|