@apipass/cerbos-pep 0.0.68 → 0.0.70
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +123 -0
- package/lib/adapters/cerbos-authorization.adapter.d.ts +8 -0
- package/lib/adapters/cerbos-authorization.adapter.js +28 -0
- package/lib/adapters/cerbos-authorization.adapter.js.map +1 -0
- package/lib/authorizatio-context.d.ts +7 -0
- package/lib/authorizatio-context.js +25 -0
- package/lib/authorizatio-context.js.map +1 -0
- package/lib/cerbos-request-factory.d.ts +19 -0
- package/lib/cerbos-request-factory.js +22 -0
- package/lib/cerbos-request-factory.js.map +1 -0
- package/lib/cerbos.decorator.d.ts +6 -0
- package/lib/cerbos.decorator.js +5 -1
- package/lib/cerbos.decorator.js.map +1 -1
- package/lib/cerbos.interceptor.d.ts +6 -7
- package/lib/cerbos.interceptor.js +16 -39
- package/lib/cerbos.interceptor.js.map +1 -1
- package/lib/cerbos.module.js +13 -1
- package/lib/cerbos.module.js.map +1 -1
- package/lib/cerbos.service.d.ts +11 -0
- package/lib/cerbos.service.js +54 -0
- package/lib/cerbos.service.js.map +1 -0
- package/lib/get-query-plan.d.ts +10 -0
- package/lib/get-query-plan.js +3 -0
- package/lib/get-query-plan.js.map +1 -0
- package/lib/ports/authorization.port.d.ts +13 -0
- package/lib/ports/authorization.port.js +5 -0
- package/lib/ports/authorization.port.js.map +1 -0
- package/package.json +3 -3
package/README.md
ADDED
|
@@ -0,0 +1,123 @@
|
|
|
1
|
+
# @apipass/cerbos-pep
|
|
2
|
+
|
|
3
|
+
[English](#english) | [Português](#português)
|
|
4
|
+
|
|
5
|
+
---
|
|
6
|
+
|
|
7
|
+
## English
|
|
8
|
+
|
|
9
|
+
### Description
|
|
10
|
+
`@apipass/cerbos-pep` is a NestJS utility designed to simplify integration with [Cerbos](https://cerbos.dev/), an open-source authorization layer. This package provides a PEP (Policy Enforcement Point) through a NestJS Interceptor that automatically validates permissions against a Cerbos PDP (Policy Decision Point).
|
|
11
|
+
|
|
12
|
+
### How it works
|
|
13
|
+
The interceptor intercepts incoming HTTP requests, extracts principal information (role and account id) from the request headers (`role` and `account_id`), and checks if the principal is allowed to perform specific actions on a resource defined via the `@CerbosPermission` decorator.
|
|
14
|
+
|
|
15
|
+
### Usage Example
|
|
16
|
+
|
|
17
|
+
#### 1. Import CerbosModule
|
|
18
|
+
Register the `CerbosModule` in your `AppModule`:
|
|
19
|
+
|
|
20
|
+
```typescript
|
|
21
|
+
import { CerbosModule } from '@apipass/cerbos-pep';
|
|
22
|
+
|
|
23
|
+
@Module({
|
|
24
|
+
imports: [
|
|
25
|
+
CerbosModule.register({
|
|
26
|
+
url: process.env.CERBOS_PDP_URL ?? 'http://127.0.0.1:3592', // Cerbos PDP URL
|
|
27
|
+
}),
|
|
28
|
+
],
|
|
29
|
+
})
|
|
30
|
+
export class AppModule {}
|
|
31
|
+
```
|
|
32
|
+
|
|
33
|
+
#### 2. Apply the Interceptor
|
|
34
|
+
You can apply the `CerbosInterceptor` globally, at the controller level, or per-route:
|
|
35
|
+
|
|
36
|
+
```typescript
|
|
37
|
+
import { CerbosInterceptor } from '@apipass/cerbos-pep';
|
|
38
|
+
import { UseInterceptors } from '@nestjs/common';
|
|
39
|
+
|
|
40
|
+
@UseInterceptors(CerbosInterceptor)
|
|
41
|
+
@Controller('orders')
|
|
42
|
+
export class OrdersController {}
|
|
43
|
+
```
|
|
44
|
+
|
|
45
|
+
#### 3. Define Permissions
|
|
46
|
+
Use the `@CerbosPermission` decorator to protect your routes:
|
|
47
|
+
|
|
48
|
+
```typescript
|
|
49
|
+
import { CerbosPermission } from '@apipass/cerbos-pep';
|
|
50
|
+
|
|
51
|
+
@Get(':id')
|
|
52
|
+
@CerbosPermission({
|
|
53
|
+
resource: {
|
|
54
|
+
kind: 'order',
|
|
55
|
+
},
|
|
56
|
+
actions: ['read'],
|
|
57
|
+
})
|
|
58
|
+
async getOrder(@Param('id') id: string) {
|
|
59
|
+
return this.ordersService.findOne(id);
|
|
60
|
+
}
|
|
61
|
+
```
|
|
62
|
+
|
|
63
|
+
**Note:** The interceptor expects `role` and `account_id` headers to be present in the request.
|
|
64
|
+
|
|
65
|
+
---
|
|
66
|
+
|
|
67
|
+
## Português
|
|
68
|
+
|
|
69
|
+
### Descrição
|
|
70
|
+
O `@apipass/cerbos-pep` é um utilitário para NestJS projetado para simplificar a integração com o [Cerbos](https://cerbos.dev/), uma camada de autorização de código aberto. Este pacote fornece um PEP (Policy Enforcement Point) por meio de um Interceptor do NestJS que valida automaticamente as permissões em um Cerbos PDP (Policy Decision Point).
|
|
71
|
+
|
|
72
|
+
### Como funciona
|
|
73
|
+
O interceptor intercepta as requisições HTTP, extrai as informações do principal (role e id da conta) dos cabeçalhos da requisição (`role` e `account_id`) e verifica se o principal tem permissão para realizar ações específicas em um recurso definido através do decorador `@CerbosPermission`.
|
|
74
|
+
|
|
75
|
+
### Exemplo de Utilização
|
|
76
|
+
|
|
77
|
+
#### 1. Importar o CerbosModule
|
|
78
|
+
Registre o `CerbosModule` no seu `AppModule`:
|
|
79
|
+
|
|
80
|
+
```typescript
|
|
81
|
+
import { CerbosModule } from '@apipass/cerbos-pep';
|
|
82
|
+
|
|
83
|
+
@Module({
|
|
84
|
+
imports: [
|
|
85
|
+
CerbosModule.register({
|
|
86
|
+
url: 'http://localhost:3592', // URL do Cerbos PDP
|
|
87
|
+
}),
|
|
88
|
+
],
|
|
89
|
+
})
|
|
90
|
+
export class AppModule {}
|
|
91
|
+
```
|
|
92
|
+
|
|
93
|
+
#### 2. Aplicar o Interceptor
|
|
94
|
+
Você pode aplicar o `CerbosInterceptor` globalmente, no nível do controller ou por rota:
|
|
95
|
+
|
|
96
|
+
```typescript
|
|
97
|
+
import { CerbosInterceptor } from '@apipass/cerbos-pep';
|
|
98
|
+
import { UseInterceptors } from '@nestjs/common';
|
|
99
|
+
|
|
100
|
+
@UseInterceptors(CerbosInterceptor)
|
|
101
|
+
@Controller('orders')
|
|
102
|
+
export class OrdersController {}
|
|
103
|
+
```
|
|
104
|
+
|
|
105
|
+
#### 3. Definir Permissões
|
|
106
|
+
Use o decorador `@CerbosPermission` para proteger suas rotas:
|
|
107
|
+
|
|
108
|
+
```typescript
|
|
109
|
+
import { CerbosPermission } from '@apipass/cerbos-pep';
|
|
110
|
+
|
|
111
|
+
@Get(':id')
|
|
112
|
+
@CerbosPermission({
|
|
113
|
+
resource: {
|
|
114
|
+
kind: 'order',
|
|
115
|
+
},
|
|
116
|
+
actions: ['read'],
|
|
117
|
+
})
|
|
118
|
+
async getOrder(@Param('id') id: string) {
|
|
119
|
+
return this.ordersService.findOne(id);
|
|
120
|
+
}
|
|
121
|
+
```
|
|
122
|
+
|
|
123
|
+
**Nota:** O interceptor espera que os headers `role` e `account_id` estejam presentes na requisição.
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
import { AuthorizationPort } from '../ports/authorization.port';
|
|
2
|
+
import { GetQueryPlanParams } from '../get-query-plan';
|
|
3
|
+
import { CerbosService } from "../cerbos.service";
|
|
4
|
+
export declare class CerbosAuthorizationAdapter implements AuthorizationPort {
|
|
5
|
+
private readonly cerbosService;
|
|
6
|
+
constructor(cerbosService: CerbosService);
|
|
7
|
+
getQueryPlan(params: GetQueryPlanParams): Promise<any>;
|
|
8
|
+
}
|
|
@@ -0,0 +1,28 @@
|
|
|
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.CerbosAuthorizationAdapter = void 0;
|
|
13
|
+
const common_1 = require("@nestjs/common");
|
|
14
|
+
const cerbos_service_1 = require("../cerbos.service");
|
|
15
|
+
let CerbosAuthorizationAdapter = class CerbosAuthorizationAdapter {
|
|
16
|
+
constructor(cerbosService) {
|
|
17
|
+
this.cerbosService = cerbosService;
|
|
18
|
+
}
|
|
19
|
+
async getQueryPlan(params) {
|
|
20
|
+
return this.cerbosService.getQueryPlan(params);
|
|
21
|
+
}
|
|
22
|
+
};
|
|
23
|
+
exports.CerbosAuthorizationAdapter = CerbosAuthorizationAdapter;
|
|
24
|
+
exports.CerbosAuthorizationAdapter = CerbosAuthorizationAdapter = __decorate([
|
|
25
|
+
(0, common_1.Injectable)(),
|
|
26
|
+
__metadata("design:paramtypes", [cerbos_service_1.CerbosService])
|
|
27
|
+
], CerbosAuthorizationAdapter);
|
|
28
|
+
//# sourceMappingURL=cerbos-authorization.adapter.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"cerbos-authorization.adapter.js","sourceRoot":"","sources":["../../src/adapters/cerbos-authorization.adapter.ts"],"names":[],"mappings":";;;;;;;;;;;;AAAA,2CAA2C;AAG3C,sDAAkD;AAG3C,IAAM,0BAA0B,GAAhC,MAAM,0BAA0B;IACnC,YACqB,aAA4B;QAA5B,kBAAa,GAAb,aAAa,CAAe;IAC9C,CAAC;IAEJ,KAAK,CAAC,YAAY,CAAC,MAA0B;QACzC,OAAO,IAAI,CAAC,aAAa,CAAC,YAAY,CAAC,MAAM,CAAC,CAAA;IAClD,CAAC;CACJ,CAAA;AARY,gEAA0B;qCAA1B,0BAA0B;IADtC,IAAA,mBAAU,GAAE;qCAG2B,8BAAa;GAFxC,0BAA0B,CAQtC"}
|
|
@@ -0,0 +1,25 @@
|
|
|
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.HttpAuthorizationContextExtractor = void 0;
|
|
10
|
+
const common_1 = require("@nestjs/common");
|
|
11
|
+
let HttpAuthorizationContextExtractor = class HttpAuthorizationContextExtractor {
|
|
12
|
+
extract(req) {
|
|
13
|
+
const role = req.headers.role?.toLowerCase();
|
|
14
|
+
const accountId = req.headers.account_id;
|
|
15
|
+
if (!role || !accountId) {
|
|
16
|
+
throw new common_1.ForbiddenException('Missing authorization headers');
|
|
17
|
+
}
|
|
18
|
+
return { role, accountId };
|
|
19
|
+
}
|
|
20
|
+
};
|
|
21
|
+
exports.HttpAuthorizationContextExtractor = HttpAuthorizationContextExtractor;
|
|
22
|
+
exports.HttpAuthorizationContextExtractor = HttpAuthorizationContextExtractor = __decorate([
|
|
23
|
+
(0, common_1.Injectable)()
|
|
24
|
+
], HttpAuthorizationContextExtractor);
|
|
25
|
+
//# sourceMappingURL=authorizatio-context.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"authorizatio-context.js","sourceRoot":"","sources":["../src/authorizatio-context.ts"],"names":[],"mappings":";;;;;;;;;AAAA,2CAA8D;AAQvD,IAAM,iCAAiC,GAAvC,MAAM,iCAAiC;IAC1C,OAAO,CAAC,GAAQ;QACZ,MAAM,IAAI,GAAG,GAAG,CAAC,OAAO,CAAC,IAAI,EAAE,WAAW,EAAE,CAAA;QAC5C,MAAM,SAAS,GAAG,GAAG,CAAC,OAAO,CAAC,UAAU,CAAA;QAExC,IAAI,CAAC,IAAI,IAAI,CAAC,SAAS,EAAE,CAAC;YACtB,MAAM,IAAI,2BAAkB,CAAC,+BAA+B,CAAC,CAAA;QACjE,CAAC;QAED,OAAO,EAAE,IAAI,EAAE,SAAS,EAAE,CAAA;IAC9B,CAAC;CACJ,CAAA;AAXY,8EAAiC;4CAAjC,iCAAiC;IAD7C,IAAA,mBAAU,GAAE;GACA,iCAAiC,CAW7C"}
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
import { AuthorizationContext } from "./authorizatio-context";
|
|
2
|
+
import { CerbosPermission } from "./cerbos.decorator";
|
|
3
|
+
export declare class CerbosRequestFactory {
|
|
4
|
+
static fromHttp(permission: CerbosPermission, ctx: AuthorizationContext, handlerName: string): {
|
|
5
|
+
principal: {
|
|
6
|
+
id: string;
|
|
7
|
+
roles: string[];
|
|
8
|
+
attributes: {
|
|
9
|
+
accountId: string;
|
|
10
|
+
};
|
|
11
|
+
};
|
|
12
|
+
resource: {
|
|
13
|
+
kind: string;
|
|
14
|
+
id: string;
|
|
15
|
+
attributes: {};
|
|
16
|
+
};
|
|
17
|
+
actions: string[];
|
|
18
|
+
};
|
|
19
|
+
}
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.CerbosRequestFactory = void 0;
|
|
4
|
+
class CerbosRequestFactory {
|
|
5
|
+
static fromHttp(permission, ctx, handlerName) {
|
|
6
|
+
return {
|
|
7
|
+
principal: {
|
|
8
|
+
id: ctx.role,
|
|
9
|
+
roles: [ctx.role],
|
|
10
|
+
attributes: { accountId: ctx.accountId }
|
|
11
|
+
},
|
|
12
|
+
resource: {
|
|
13
|
+
kind: permission.resource.kind,
|
|
14
|
+
id: permission.resource.id ?? handlerName,
|
|
15
|
+
attributes: {}
|
|
16
|
+
},
|
|
17
|
+
actions: permission.actions
|
|
18
|
+
};
|
|
19
|
+
}
|
|
20
|
+
}
|
|
21
|
+
exports.CerbosRequestFactory = CerbosRequestFactory;
|
|
22
|
+
//# sourceMappingURL=cerbos-request-factory.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"cerbos-request-factory.js","sourceRoot":"","sources":["../src/cerbos-request-factory.ts"],"names":[],"mappings":";;;AAGA,MAAa,oBAAoB;IAC7B,MAAM,CAAC,QAAQ,CAAC,UAA4B,EAAE,GAAyB,EAAE,WAAmB;QACxF,OAAO;YACH,SAAS,EAAE;gBACP,EAAE,EAAE,GAAG,CAAC,IAAI;gBACZ,KAAK,EAAE,CAAC,GAAG,CAAC,IAAI,CAAC;gBACjB,UAAU,EAAE,EAAE,SAAS,EAAE,GAAG,CAAC,SAAS,EAAE;aAC3C;YACD,QAAQ,EAAE;gBACN,IAAI,EAAE,UAAU,CAAC,QAAQ,CAAC,IAAI;gBAC9B,EAAE,EAAE,UAAU,CAAC,QAAQ,CAAC,EAAE,IAAI,WAAW;gBACzC,UAAU,EAAE,EAAE;aACjB;YACD,OAAO,EAAE,UAAU,CAAC,OAAO;SAC9B,CAAA;IACL,CAAC;CACJ;AAhBD,oDAgBC"}
|
|
@@ -1,5 +1,11 @@
|
|
|
1
1
|
export declare const CERBOS_METADATA_KEY = "cerbos:permission";
|
|
2
|
+
export declare const CerbosMode: {
|
|
3
|
+
readonly CHECK: "CHECK";
|
|
4
|
+
readonly QUERY_PLAN: "QUERY_PLAN";
|
|
5
|
+
};
|
|
6
|
+
export type CerbosMode = typeof CerbosMode[keyof typeof CerbosMode];
|
|
2
7
|
export interface CerbosPermission {
|
|
8
|
+
mode?: CerbosMode;
|
|
3
9
|
resource: {
|
|
4
10
|
kind: string;
|
|
5
11
|
id?: string;
|
package/lib/cerbos.decorator.js
CHANGED
|
@@ -1,8 +1,12 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.CerbosPermission = exports.CERBOS_METADATA_KEY = void 0;
|
|
3
|
+
exports.CerbosPermission = exports.CerbosMode = exports.CERBOS_METADATA_KEY = void 0;
|
|
4
4
|
const common_1 = require("@nestjs/common");
|
|
5
5
|
exports.CERBOS_METADATA_KEY = 'cerbos:permission';
|
|
6
|
+
exports.CerbosMode = {
|
|
7
|
+
CHECK: 'CHECK',
|
|
8
|
+
QUERY_PLAN: 'QUERY_PLAN',
|
|
9
|
+
};
|
|
6
10
|
const CerbosPermission = (permission) => (0, common_1.SetMetadata)(exports.CERBOS_METADATA_KEY, permission);
|
|
7
11
|
exports.CerbosPermission = CerbosPermission;
|
|
8
12
|
//# sourceMappingURL=cerbos.decorator.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"cerbos.decorator.js","sourceRoot":"","sources":["../src/cerbos.decorator.ts"],"names":[],"mappings":";;;AAAA,2CAA4C;AAE/B,QAAA,mBAAmB,GAAG,mBAAmB,CAAA;
|
|
1
|
+
{"version":3,"file":"cerbos.decorator.js","sourceRoot":"","sources":["../src/cerbos.decorator.ts"],"names":[],"mappings":";;;AAAA,2CAA4C;AAE/B,QAAA,mBAAmB,GAAG,mBAAmB,CAAA;AACzC,QAAA,UAAU,GAAG;IACxB,KAAK,EAAE,OAAO;IACd,UAAU,EAAE,YAAY;CAChB,CAAA;AAYH,MAAM,gBAAgB,GAAG,CAAC,UAA4B,EAAE,EAAE,CAC/D,IAAA,oBAAW,EAAC,2BAAmB,EAAE,UAAU,CAAC,CAAA;AADjC,QAAA,gBAAgB,oBACiB"}
|
|
@@ -1,12 +1,11 @@
|
|
|
1
1
|
import { type NestInterceptor, type ExecutionContext, type CallHandler } from '@nestjs/common';
|
|
2
2
|
import { Reflector } from '@nestjs/core';
|
|
3
|
-
import {
|
|
4
|
-
import
|
|
3
|
+
import { CerbosService } from './cerbos.service';
|
|
4
|
+
import { HttpAuthorizationContextExtractor } from "./authorizatio-context";
|
|
5
5
|
export declare class CerbosInterceptor implements NestInterceptor {
|
|
6
6
|
private readonly reflector;
|
|
7
|
-
private readonly
|
|
8
|
-
private
|
|
9
|
-
constructor(reflector: Reflector,
|
|
10
|
-
|
|
11
|
-
intercept(context: ExecutionContext, next: CallHandler): Promise<Observable<any>>;
|
|
7
|
+
private readonly cerbosService;
|
|
8
|
+
private readonly extractor;
|
|
9
|
+
constructor(reflector: Reflector, cerbosService: CerbosService, extractor: HttpAuthorizationContextExtractor);
|
|
10
|
+
intercept(context: ExecutionContext, next: CallHandler): Promise<import("rxjs").Observable<any>>;
|
|
12
11
|
}
|
|
@@ -8,64 +8,40 @@ var __decorate = (this && this.__decorate) || function (decorators, target, key,
|
|
|
8
8
|
var __metadata = (this && this.__metadata) || function (k, v) {
|
|
9
9
|
if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v);
|
|
10
10
|
};
|
|
11
|
-
var __param = (this && this.__param) || function (paramIndex, decorator) {
|
|
12
|
-
return function (target, key) { decorator(target, key, paramIndex); }
|
|
13
|
-
};
|
|
14
11
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
15
12
|
exports.CerbosInterceptor = void 0;
|
|
16
13
|
const common_1 = require("@nestjs/common");
|
|
17
14
|
const core_1 = require("@nestjs/core");
|
|
18
15
|
const cerbos_decorator_1 = require("./cerbos.decorator");
|
|
16
|
+
const cerbos_service_1 = require("./cerbos.service");
|
|
17
|
+
const cerbos_request_factory_1 = require("./cerbos-request-factory");
|
|
18
|
+
const authorizatio_context_1 = require("./authorizatio-context");
|
|
19
19
|
let CerbosInterceptor = class CerbosInterceptor {
|
|
20
|
-
constructor(reflector,
|
|
20
|
+
constructor(reflector, cerbosService, extractor) {
|
|
21
21
|
this.reflector = reflector;
|
|
22
|
-
this.
|
|
23
|
-
this.
|
|
24
|
-
}
|
|
25
|
-
async getCerbos() {
|
|
26
|
-
if (!this.cerbosInstance) {
|
|
27
|
-
const { HTTP: Cerbos } = (await import('@cerbos/http'));
|
|
28
|
-
this.cerbosInstance = new Cerbos(this.config.url);
|
|
29
|
-
}
|
|
30
|
-
return this.cerbosInstance;
|
|
22
|
+
this.cerbosService = cerbosService;
|
|
23
|
+
this.extractor = extractor;
|
|
31
24
|
}
|
|
32
25
|
async intercept(context, next) {
|
|
33
26
|
const permission = this.reflector.getAllAndOverride(cerbos_decorator_1.CERBOS_METADATA_KEY, [context.getHandler(), context.getClass()]);
|
|
34
27
|
if (!permission) {
|
|
35
28
|
return next.handle();
|
|
36
29
|
}
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
const role = request.headers.role?.toLowerCase();
|
|
40
|
-
const accountId = request.headers.account_id;
|
|
41
|
-
if (!role || !accountId) {
|
|
42
|
-
throw new common_1.ForbiddenException('Missing authorization headers');
|
|
30
|
+
if (permission.mode === cerbos_decorator_1.CerbosMode.QUERY_PLAN) {
|
|
31
|
+
return next.handle();
|
|
43
32
|
}
|
|
44
|
-
const
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
roles: [role],
|
|
48
|
-
attributes: { accountId }
|
|
49
|
-
},
|
|
50
|
-
resource: {
|
|
51
|
-
kind: permission.resource.kind,
|
|
52
|
-
id: permission.resource.id ?? context.getHandler().name,
|
|
53
|
-
attributes: {}
|
|
54
|
-
},
|
|
55
|
-
actions: permission.actions
|
|
56
|
-
};
|
|
33
|
+
const request = context.switchToHttp().getRequest();
|
|
34
|
+
const authContext = this.extractor.extract(request);
|
|
35
|
+
const cerbosRequest = cerbos_request_factory_1.CerbosRequestFactory.fromHttp(permission, authContext, context.getHandler().name);
|
|
57
36
|
try {
|
|
58
|
-
const
|
|
59
|
-
const result = await cerbos.checkResource(cerbosRequest);
|
|
37
|
+
const result = await this.cerbosService.getCheckResource(cerbosRequest);
|
|
60
38
|
const allowed = permission.actions.every((action) => result.isAllowed(action));
|
|
61
39
|
if (!allowed) {
|
|
62
40
|
throw new common_1.ForbiddenException('Access denied by Cerbos');
|
|
63
41
|
}
|
|
64
42
|
}
|
|
65
43
|
catch (error) {
|
|
66
|
-
|
|
67
|
-
throw error;
|
|
68
|
-
throw new common_1.ForbiddenException('Error checking permissions');
|
|
44
|
+
throw new common_1.ForbiddenException(`Error - ${error}`);
|
|
69
45
|
}
|
|
70
46
|
return next.handle();
|
|
71
47
|
}
|
|
@@ -73,7 +49,8 @@ let CerbosInterceptor = class CerbosInterceptor {
|
|
|
73
49
|
exports.CerbosInterceptor = CerbosInterceptor;
|
|
74
50
|
exports.CerbosInterceptor = CerbosInterceptor = __decorate([
|
|
75
51
|
(0, common_1.Injectable)(),
|
|
76
|
-
|
|
77
|
-
|
|
52
|
+
__metadata("design:paramtypes", [core_1.Reflector,
|
|
53
|
+
cerbos_service_1.CerbosService,
|
|
54
|
+
authorizatio_context_1.HttpAuthorizationContextExtractor])
|
|
78
55
|
], CerbosInterceptor);
|
|
79
56
|
//# sourceMappingURL=cerbos.interceptor.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"cerbos.interceptor.js","sourceRoot":"","sources":["../src/cerbos.interceptor.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"cerbos.interceptor.js","sourceRoot":"","sources":["../src/cerbos.interceptor.ts"],"names":[],"mappings":";;;;;;;;;;;;AAAA,2CAMuB;AACvB,uCAAwC;AACxC,yDAAyF;AACzF,qDAAgD;AAChD,qEAA8D;AAC9D,iEAAyE;AAKlE,IAAM,iBAAiB,GAAvB,MAAM,iBAAiB;IAC5B,YACqB,SAAoB,EACpB,aAA4B,EAC5B,SAA4C;QAF5C,cAAS,GAAT,SAAS,CAAW;QACpB,kBAAa,GAAb,aAAa,CAAe;QAC5B,cAAS,GAAT,SAAS,CAAmC;IAC9D,CAAC;IAEJ,KAAK,CAAC,SAAS,CAAC,OAAyB,EAAE,IAAiB;QAC1D,MAAM,UAAU,GAAqB,IAAI,CAAC,SAAS,CAAC,iBAAiB,CACjE,sCAAmB,EACnB,CAAC,OAAO,CAAC,UAAU,EAAE,EAAE,OAAO,CAAC,QAAQ,EAAE,CAAC,CAC7C,CAAA;QAED,IAAI,CAAC,UAAU,EAAE,CAAC;YAChB,OAAO,IAAI,CAAC,MAAM,EAAE,CAAA;QACtB,CAAC;QAED,IAAI,UAAU,CAAC,IAAI,KAAK,6BAAU,CAAC,UAAU,EAAE,CAAC;YAC9C,OAAO,IAAI,CAAC,MAAM,EAAE,CAAA;QACtB,CAAC;QAED,MAAM,OAAO,GAAG,OAAO,CAAC,YAAY,EAAE,CAAC,UAAU,EAAE,CAAA;QACnD,MAAM,WAAW,GAAG,IAAI,CAAC,SAAS,CAAC,OAAO,CAAC,OAAO,CAAC,CAAA;QAEnD,MAAM,aAAa,GAAG,6CAAoB,CAAC,QAAQ,CAC/C,UAAU,EACV,WAAW,EACX,OAAO,CAAC,UAAU,EAAE,CAAC,IAAI,CAC5B,CAAA;QAED,IAAI,CAAC;YACH,MAAM,MAAM,GAAyB,MAAM,IAAI,CAAC,aAAa,CAAC,gBAAgB,CAAC,aAAa,CAAC,CAAA;YAC7F,MAAM,OAAO,GAAY,UAAU,CAAC,OAAO,CAAC,KAAK,CAAC,CAAC,MAAM,EAAE,EAAE,CACzD,MAAM,CAAC,SAAS,CAAC,MAAM,CAAC,CAC3B,CAAA;YACD,IAAI,CAAC,OAAO,EAAE,CAAC;gBACb,MAAM,IAAI,2BAAkB,CAAC,yBAAyB,CAAC,CAAA;YACzD,CAAC;QACH,CAAC;QAAC,OAAO,KAAK,EAAE,CAAC;YACf,MAAM,IAAI,2BAAkB,CAAC,WAAW,KAAK,EAAE,CAAC,CAAA;QAClD,CAAC;QACD,OAAO,IAAI,CAAC,MAAM,EAAE,CAAA;IACtB,CAAC;CAEF,CAAA;AA5CY,8CAAiB;4BAAjB,iBAAiB;IAD7B,IAAA,mBAAU,GAAE;qCAGqB,gBAAS;QACL,8BAAa;QACjB,wDAAiC;GAJtD,iBAAiB,CA4C7B"}
|
package/lib/cerbos.module.js
CHANGED
|
@@ -10,6 +10,9 @@ Object.defineProperty(exports, "__esModule", { value: true });
|
|
|
10
10
|
exports.CerbosModule = void 0;
|
|
11
11
|
const common_1 = require("@nestjs/common");
|
|
12
12
|
const cerbos_interceptor_1 = require("./cerbos.interceptor");
|
|
13
|
+
const cerbos_service_1 = require("./cerbos.service");
|
|
14
|
+
const cerbos_authorization_adapter_1 = require("./adapters/cerbos-authorization.adapter");
|
|
15
|
+
const authorization_port_1 = require("./ports/authorization.port");
|
|
13
16
|
let CerbosModule = CerbosModule_1 = class CerbosModule {
|
|
14
17
|
static register(options) {
|
|
15
18
|
return {
|
|
@@ -19,9 +22,18 @@ let CerbosModule = CerbosModule_1 = class CerbosModule {
|
|
|
19
22
|
provide: 'CERBOS_PDP_URL',
|
|
20
23
|
useValue: options,
|
|
21
24
|
},
|
|
25
|
+
cerbos_service_1.CerbosService,
|
|
22
26
|
cerbos_interceptor_1.CerbosInterceptor,
|
|
27
|
+
{
|
|
28
|
+
provide: authorization_port_1.AUTHORIZATION_PORT,
|
|
29
|
+
useClass: cerbos_authorization_adapter_1.CerbosAuthorizationAdapter,
|
|
30
|
+
}
|
|
23
31
|
],
|
|
24
|
-
exports: [
|
|
32
|
+
exports: [cerbos_service_1.CerbosService,
|
|
33
|
+
cerbos_interceptor_1.CerbosInterceptor,
|
|
34
|
+
'CERBOS_PDP_URL',
|
|
35
|
+
authorization_port_1.AUTHORIZATION_PORT,
|
|
36
|
+
cerbos_interceptor_1.CerbosInterceptor],
|
|
25
37
|
};
|
|
26
38
|
}
|
|
27
39
|
};
|
package/lib/cerbos.module.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"cerbos.module.js","sourceRoot":"","sources":["../src/cerbos.module.ts"],"names":[],"mappings":";;;;;;;;;;AAAA,2CAA+D;AAC/D,6DAAyD;
|
|
1
|
+
{"version":3,"file":"cerbos.module.js","sourceRoot":"","sources":["../src/cerbos.module.ts"],"names":[],"mappings":";;;;;;;;;;AAAA,2CAA+D;AAC/D,6DAAyD;AACzD,qDAAiD;AACjD,0FAAqF;AACrF,mEAAgE;AAQzD,IAAM,YAAY,oBAAlB,MAAM,YAAY;IACvB,MAAM,CAAC,QAAQ,CAAC,OAA4B;QAC1C,OAAO;YACL,MAAM,EAAE,cAAY;YACpB,SAAS,EAAE;gBACT;oBACE,OAAO,EAAE,gBAAgB;oBACzB,QAAQ,EAAE,OAAO;iBAClB;gBACD,8BAAa;gBACb,sCAAiB;gBACjB;oBACE,OAAO,EAAE,uCAAkB;oBAC3B,QAAQ,EAAE,yDAA0B;iBACrC;aACF;YACD,OAAO,EAAE,CAAC,8BAAa;gBACrB,sCAAiB;gBACjB,gBAAgB;gBAChB,uCAAkB;gBAClB,sCAAiB,CAAC;SACrB,CAAC;IACJ,CAAC;CACF,CAAA;AAvBY,oCAAY;uBAAZ,YAAY;IAFxB,IAAA,eAAM,GAAE;IACR,IAAA,eAAM,EAAC,EAAE,CAAC;GACE,YAAY,CAuBxB"}
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import type { CerbosModuleOptions } from './cerbos.module';
|
|
2
|
+
import type { CheckResourceRequest, CheckResourcesResult } from '@cerbos/core' with { 'resolution-mode': 'import' };
|
|
3
|
+
import { GetQueryPlanParams } from "./get-query-plan";
|
|
4
|
+
export declare class CerbosService {
|
|
5
|
+
private readonly config;
|
|
6
|
+
private cerbos;
|
|
7
|
+
constructor(config: CerbosModuleOptions);
|
|
8
|
+
private getCerbos;
|
|
9
|
+
getCheckResource(request: CheckResourceRequest): Promise<CheckResourcesResult>;
|
|
10
|
+
getQueryPlan(params: GetQueryPlanParams): Promise<any>;
|
|
11
|
+
}
|
|
@@ -0,0 +1,54 @@
|
|
|
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
|
+
var __param = (this && this.__param) || function (paramIndex, decorator) {
|
|
12
|
+
return function (target, key) { decorator(target, key, paramIndex); }
|
|
13
|
+
};
|
|
14
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
15
|
+
exports.CerbosService = void 0;
|
|
16
|
+
const common_1 = require("@nestjs/common");
|
|
17
|
+
let CerbosService = class CerbosService {
|
|
18
|
+
constructor(config) {
|
|
19
|
+
this.config = config;
|
|
20
|
+
}
|
|
21
|
+
async getCerbos() {
|
|
22
|
+
if (!this.cerbos) {
|
|
23
|
+
const { HTTP: Cerbos } = (await import('@cerbos/http'));
|
|
24
|
+
this.cerbos = new Cerbos(this.config.url);
|
|
25
|
+
}
|
|
26
|
+
return this.cerbos;
|
|
27
|
+
}
|
|
28
|
+
async getCheckResource(request) {
|
|
29
|
+
const cerbos = await this.getCerbos();
|
|
30
|
+
return cerbos.checkResource(request);
|
|
31
|
+
}
|
|
32
|
+
async getQueryPlan(params) {
|
|
33
|
+
const cerbos = await this.getCerbos();
|
|
34
|
+
const { principal, resourceKind, action } = params;
|
|
35
|
+
return cerbos.planResources({
|
|
36
|
+
principal: {
|
|
37
|
+
id: principal.id,
|
|
38
|
+
roles: principal.roles,
|
|
39
|
+
attributes: principal.attributes
|
|
40
|
+
},
|
|
41
|
+
resource: {
|
|
42
|
+
kind: resourceKind
|
|
43
|
+
},
|
|
44
|
+
action
|
|
45
|
+
});
|
|
46
|
+
}
|
|
47
|
+
};
|
|
48
|
+
exports.CerbosService = CerbosService;
|
|
49
|
+
exports.CerbosService = CerbosService = __decorate([
|
|
50
|
+
(0, common_1.Injectable)(),
|
|
51
|
+
__param(0, (0, common_1.Inject)('CERBOS_PDP_URL')),
|
|
52
|
+
__metadata("design:paramtypes", [Object])
|
|
53
|
+
], CerbosService);
|
|
54
|
+
//# sourceMappingURL=cerbos.service.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"cerbos.service.js","sourceRoot":"","sources":["../src/cerbos.service.ts"],"names":[],"mappings":";;;;;;;;;;;;;;;AAAA,2CAAoD;AAQ7C,IAAM,aAAa,GAAnB,MAAM,aAAa;IAGxB,YAAuD,MAA2B;QAA3B,WAAM,GAAN,MAAM,CAAqB;IAC/E,CAAC;IAEI,KAAK,CAAC,SAAS;QACrB,IAAI,CAAC,IAAI,CAAC,MAAM,EAAE,CAAC;YACjB,MAAM,EAAE,IAAI,EAAE,MAAM,EAAE,GAAG,CAAC,MAAM,MAAM,CAAC,cAAc,CAAC,CAAgC,CAAC;YACvF,IAAI,CAAC,MAAM,GAAG,IAAI,MAAM,CAAC,IAAI,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC;QAC5C,CAAC;QACD,OAAO,IAAI,CAAC,MAAM,CAAC;IACrB,CAAC;IAED,KAAK,CAAC,gBAAgB,CAAC,OAA6B;QAClD,MAAM,MAAM,GAAS,MAAM,IAAI,CAAC,SAAS,EAAE,CAAC;QAC5C,OAAO,MAAM,CAAC,aAAa,CAAC,OAAO,CAAC,CAAC;IACvC,CAAC;IAEM,KAAK,CAAC,YAAY,CAAE,MAA0B;QACnD,MAAM,MAAM,GAAG,MAAM,IAAI,CAAC,SAAS,EAAE,CAAA;QAErC,MAAM,EAAE,SAAS,EAAE,YAAY,EAAE,MAAM,EAAE,GAAG,MAAM,CAAA;QAElD,OAAO,MAAM,CAAC,aAAa,CAAC;YAC1B,SAAS,EAAE;gBACT,EAAE,EAAE,SAAS,CAAC,EAAE;gBAChB,KAAK,EAAE,SAAS,CAAC,KAAK;gBACtB,UAAU,EAAE,SAAS,CAAC,UAAU;aACjC;YACD,QAAQ,EAAE;gBACR,IAAI,EAAE,YAAY;aACnB;YACD,MAAM;SACP,CAAC,CAAA;IACJ,CAAC;CAEF,CAAA;AArCY,sCAAa;wBAAb,aAAa;IADzB,IAAA,mBAAU,GAAE;IAIE,WAAA,IAAA,eAAM,EAAC,gBAAgB,CAAC,CAAA;;GAH1B,aAAa,CAqCzB"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"get-query-plan.js","sourceRoot":"","sources":["../src/get-query-plan.ts"],"names":[],"mappings":""}
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
export declare const AUTHORIZATION_PORT: unique symbol;
|
|
2
|
+
export interface AuthorizationPort {
|
|
3
|
+
getQueryPlan(params: GetQueryPlanParams): Promise<unknown>;
|
|
4
|
+
}
|
|
5
|
+
export interface GetQueryPlanParams {
|
|
6
|
+
principal: {
|
|
7
|
+
id: string;
|
|
8
|
+
roles: string[];
|
|
9
|
+
attributes: Record<string, any>;
|
|
10
|
+
};
|
|
11
|
+
resourceKind: string;
|
|
12
|
+
action: string;
|
|
13
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"authorization.port.js","sourceRoot":"","sources":["../../src/ports/authorization.port.ts"],"names":[],"mappings":";;;AAAa,QAAA,kBAAkB,GAAG,MAAM,CAAC,mBAAmB,CAAC,CAAA"}
|
package/package.json
CHANGED
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@apipass/cerbos-pep",
|
|
3
|
-
"version": "0.0.
|
|
3
|
+
"version": "0.0.70",
|
|
4
4
|
"description": "Cerbos PEP utility for NestJS",
|
|
5
|
-
"author": "
|
|
5
|
+
"author": "raul@apipass.com.br",
|
|
6
6
|
"license": "ISC",
|
|
7
7
|
"main": "lib/index.js",
|
|
8
8
|
"typings": "lib/index.d.ts",
|
|
@@ -28,5 +28,5 @@
|
|
|
28
28
|
"@nestjs/common": "10.4.15",
|
|
29
29
|
"@nestjs/core": "10.4.15"
|
|
30
30
|
},
|
|
31
|
-
"gitHead": "
|
|
31
|
+
"gitHead": "dd4f65f3ff14747c845ce3247dec92949eb318c2"
|
|
32
32
|
}
|