@moostjs/arbac 0.5.27
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/LICENSE +21 -0
- package/README.md +117 -0
- package/dist/index.cjs +163 -0
- package/dist/index.d.ts +47 -0
- package/dist/index.mjs +120 -0
- package/package.json +51 -0
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2022 moostjs
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
package/README.md
ADDED
|
@@ -0,0 +1,117 @@
|
|
|
1
|
+
# @moostjs/arbac
|
|
2
|
+
|
|
3
|
+
Advanced Role-Based Access Control (ARBAC) integration for MoostJS, enabling seamless authorization management with `@prostojs/arbac` while leveraging Moost's dependency injection and metadata-driven event processing capabilities.
|
|
4
|
+
|
|
5
|
+
## Features
|
|
6
|
+
|
|
7
|
+
- **MoostJS Integration** – Provides decorators and utilities for enforcing access control.
|
|
8
|
+
- **Role & Scope-Based Authorization** – Supports fine-grained access control based on user roles and attributes.
|
|
9
|
+
- **Dependency Injection Support** – Utilizes MoostJS's DI system for injecting ARBAC services.
|
|
10
|
+
- **Easy Decorators** – Define resource-based access rules with simple decorators.
|
|
11
|
+
|
|
12
|
+
## Installation
|
|
13
|
+
|
|
14
|
+
```sh
|
|
15
|
+
npm install @moostjs/arbac
|
|
16
|
+
```
|
|
17
|
+
|
|
18
|
+
or using pnpm:
|
|
19
|
+
|
|
20
|
+
```sh
|
|
21
|
+
pnpm i @moostjs/arbac
|
|
22
|
+
```
|
|
23
|
+
|
|
24
|
+
## Quick Start
|
|
25
|
+
|
|
26
|
+
### Setup `MoostArbac`
|
|
27
|
+
|
|
28
|
+
To integrate ARBAC into your MoostJS application, define an instance of `MoostArbac`, register roles, and provide it as a dependency.
|
|
29
|
+
|
|
30
|
+
```typescript
|
|
31
|
+
import { createProvideRegistry } from 'moost'
|
|
32
|
+
import { MoostArbac } from '@moostjs/arbac'
|
|
33
|
+
import * as roles from './roles'
|
|
34
|
+
import type { TRoleScope, TUserAttrs } from './types'
|
|
35
|
+
|
|
36
|
+
export const ArbacProvideRegistry = createProvideRegistry([
|
|
37
|
+
MoostArbac,
|
|
38
|
+
() => {
|
|
39
|
+
const instance = new MoostArbac<TUserAttrs, TRoleScope>()
|
|
40
|
+
for (const role of roles) {
|
|
41
|
+
instance.registerRole(role)
|
|
42
|
+
}
|
|
43
|
+
return instance
|
|
44
|
+
},
|
|
45
|
+
])
|
|
46
|
+
```
|
|
47
|
+
|
|
48
|
+
### Define a Custom User Provider
|
|
49
|
+
|
|
50
|
+
To integrate user attributes and roles dynamically, replace the generic `ArbacUserProvider` with an application-specific implementation.
|
|
51
|
+
|
|
52
|
+
```typescript
|
|
53
|
+
import { createReplaceRegistry } from 'moost'
|
|
54
|
+
import { ArbacUserProvider } from '@moostjs/arbac'
|
|
55
|
+
import { CustomUserProvider } from './custom-user-provider'
|
|
56
|
+
|
|
57
|
+
export const ArbacReplaceRegistry = createReplaceRegistry([ArbacUserProvider, CustomUserProvider])
|
|
58
|
+
```
|
|
59
|
+
|
|
60
|
+
### Register ARBAC in Moost Application
|
|
61
|
+
|
|
62
|
+
```typescript
|
|
63
|
+
import { App } from 'moost'
|
|
64
|
+
import { ArbacProvideRegistry, ArbacReplaceRegistry } from './arbac-setup'
|
|
65
|
+
|
|
66
|
+
const app = new App()
|
|
67
|
+
app.setProvideRegistry(ArbacProvideRegistry)
|
|
68
|
+
app.setReplaceRegistry(ArbacReplaceRegistry)
|
|
69
|
+
```
|
|
70
|
+
|
|
71
|
+
### Protect Routes with `@Authorized()`
|
|
72
|
+
|
|
73
|
+
```typescript
|
|
74
|
+
import { Authorized, Public } from '@moostjs/arbac'
|
|
75
|
+
import { Controller, Get } from 'moost'
|
|
76
|
+
|
|
77
|
+
@Controller('/data')
|
|
78
|
+
@ArbacAuthorized()
|
|
79
|
+
export class DataController {
|
|
80
|
+
@Get()
|
|
81
|
+
getProtectedData() {
|
|
82
|
+
return { data: 'secured' }
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
@Get('/public')
|
|
86
|
+
@ArbacPublic()
|
|
87
|
+
getPublicData() {
|
|
88
|
+
return { data: 'open' }
|
|
89
|
+
}
|
|
90
|
+
}
|
|
91
|
+
```
|
|
92
|
+
|
|
93
|
+
## API
|
|
94
|
+
|
|
95
|
+
### `MoostArbac<TUserAttrs, TScope>`
|
|
96
|
+
|
|
97
|
+
Extends `@prostojs/arbac` and integrates with MoostJS DI.
|
|
98
|
+
|
|
99
|
+
### `ArbacUserProvider<TUserAttrs>`
|
|
100
|
+
|
|
101
|
+
Abstract class for defining user-related access control logic.
|
|
102
|
+
|
|
103
|
+
### `@ArbacAuthorized()`
|
|
104
|
+
|
|
105
|
+
Method decorator to enforce ARBAC checks.
|
|
106
|
+
|
|
107
|
+
### `@ArbacPublic()`
|
|
108
|
+
|
|
109
|
+
Marks a route as publicly accessible, bypassing authorization.
|
|
110
|
+
|
|
111
|
+
### `useArbac()`
|
|
112
|
+
|
|
113
|
+
Composable function for evaluating access control dynamically.
|
|
114
|
+
|
|
115
|
+
## License
|
|
116
|
+
|
|
117
|
+
MIT
|
package/dist/index.cjs
ADDED
|
@@ -0,0 +1,163 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
//#region rolldown:runtime
|
|
3
|
+
var __create = Object.create;
|
|
4
|
+
var __defProp = Object.defineProperty;
|
|
5
|
+
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
|
|
6
|
+
var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
7
|
+
var __getProtoOf = Object.getPrototypeOf;
|
|
8
|
+
var __hasOwnProp = Object.prototype.hasOwnProperty;
|
|
9
|
+
var __copyProps = (to, from, except, desc) => {
|
|
10
|
+
if (from && typeof from === "object" || typeof from === "function") for (var keys = __getOwnPropNames(from), i = 0, n = keys.length, key; i < n; i++) {
|
|
11
|
+
key = keys[i];
|
|
12
|
+
if (!__hasOwnProp.call(to, key) && key !== except) __defProp(to, key, {
|
|
13
|
+
get: ((k) => from[k]).bind(null, key),
|
|
14
|
+
enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable
|
|
15
|
+
});
|
|
16
|
+
}
|
|
17
|
+
return to;
|
|
18
|
+
};
|
|
19
|
+
var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", {
|
|
20
|
+
value: mod,
|
|
21
|
+
enumerable: true
|
|
22
|
+
}) : target, mod));
|
|
23
|
+
|
|
24
|
+
//#endregion
|
|
25
|
+
const moost = __toESM(require("moost"));
|
|
26
|
+
const __prostojs_arbac = __toESM(require("@prostojs/arbac"));
|
|
27
|
+
const __wooksjs_event_http = __toESM(require("@wooksjs/event-http"));
|
|
28
|
+
|
|
29
|
+
//#region packages/arbac/src/moost-arbac.ts
|
|
30
|
+
function _ts_decorate$1(decorators, target, key, desc) {
|
|
31
|
+
var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
|
|
32
|
+
if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
|
|
33
|
+
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;
|
|
34
|
+
return c > 3 && r && Object.defineProperty(target, key, r), r;
|
|
35
|
+
}
|
|
36
|
+
var MoostArbac = class extends __prostojs_arbac.Arbac {};
|
|
37
|
+
MoostArbac = _ts_decorate$1([(0, moost.Injectable)()], MoostArbac);
|
|
38
|
+
|
|
39
|
+
//#endregion
|
|
40
|
+
//#region packages/arbac/src/user.provider.ts
|
|
41
|
+
function _ts_decorate(decorators, target, key, desc) {
|
|
42
|
+
var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
|
|
43
|
+
if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
|
|
44
|
+
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;
|
|
45
|
+
return c > 3 && r && Object.defineProperty(target, key, r), r;
|
|
46
|
+
}
|
|
47
|
+
var ArbacUserProvider = class {
|
|
48
|
+
/**
|
|
49
|
+
* Retrieves the unique identifier of the user.
|
|
50
|
+
*
|
|
51
|
+
* @returns {string | Promise<string>} The user ID, or a rejected promise if not implemented.
|
|
52
|
+
* @throws {Error} If the method is not overridden by a subclass.
|
|
53
|
+
*/ getUserId() {
|
|
54
|
+
return Promise.reject(new Error("ArbacUserProvider class must be extended"));
|
|
55
|
+
}
|
|
56
|
+
/**
|
|
57
|
+
* Retrieves the roles assigned to a user based on their ID.
|
|
58
|
+
*
|
|
59
|
+
* @param {string} id - The user ID.
|
|
60
|
+
* @returns {string[] | Promise<string[]>} An array of role identifiers, or a rejected promise if not implemented.
|
|
61
|
+
* @throws {Error} If the method is not overridden by a subclass.
|
|
62
|
+
*/ getRoles(id) {
|
|
63
|
+
return Promise.reject(new Error("ArbacUserProvider class must be extended"));
|
|
64
|
+
}
|
|
65
|
+
/**
|
|
66
|
+
* Retrieves the attributes associated with a user based on their ID.
|
|
67
|
+
*
|
|
68
|
+
* @param {string} id - The user ID.
|
|
69
|
+
* @returns {TUserAttrs | Promise<TUserAttrs>} The user attributes, or a rejected promise if not implemented.
|
|
70
|
+
* @throws {Error} If the method is not overridden by a subclass.
|
|
71
|
+
*/ getAttrs(id) {
|
|
72
|
+
return Promise.reject(new Error("ArbacUserProvider class must be extended"));
|
|
73
|
+
}
|
|
74
|
+
};
|
|
75
|
+
ArbacUserProvider = _ts_decorate([(0, moost.Injectable)()], ArbacUserProvider);
|
|
76
|
+
|
|
77
|
+
//#endregion
|
|
78
|
+
//#region packages/arbac/src/arbac.composables.ts
|
|
79
|
+
function useArbac() {
|
|
80
|
+
const store = (0, moost.useAsyncEventContext)().store("arbac");
|
|
81
|
+
const cc = (0, moost.useControllerContext)();
|
|
82
|
+
const getScopes = () => store.get("scopes");
|
|
83
|
+
const setScopes = (scope) => store.set("scopes", scope);
|
|
84
|
+
const evaluate = async (opts) => {
|
|
85
|
+
const user = await cc.instantiate(ArbacUserProvider);
|
|
86
|
+
const userId = await user.getUserId();
|
|
87
|
+
const arbac = await cc.instantiate(MoostArbac);
|
|
88
|
+
const result = await arbac.evaluate(opts, {
|
|
89
|
+
id: userId,
|
|
90
|
+
roles: await user.getRoles(userId),
|
|
91
|
+
attrs: async (id) => user.getAttrs(id)
|
|
92
|
+
});
|
|
93
|
+
Object.assign(result, { userId });
|
|
94
|
+
return result;
|
|
95
|
+
};
|
|
96
|
+
const cMeta = cc.getControllerMeta();
|
|
97
|
+
const mMeta = cc.getMethodMeta();
|
|
98
|
+
const resource = mMeta?.arbacResourceId || cMeta?.arbacResourceId || cMeta?.id || (0, moost.getConstructor)(cc.getController()).name;
|
|
99
|
+
const action = mMeta?.arbacActionId || mMeta?.id || cc.getMethod() || "";
|
|
100
|
+
const isPublic = mMeta?.arbacPublic || cMeta?.arbacPublic || false;
|
|
101
|
+
return {
|
|
102
|
+
getScopes,
|
|
103
|
+
setScopes,
|
|
104
|
+
evaluate,
|
|
105
|
+
resource,
|
|
106
|
+
action,
|
|
107
|
+
isPublic
|
|
108
|
+
};
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
//#endregion
|
|
112
|
+
//#region packages/arbac/src/arbac.mate.ts
|
|
113
|
+
function getArbacMate() {
|
|
114
|
+
return (0, moost.getMoostMate)();
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
//#endregion
|
|
118
|
+
//#region packages/arbac/src/arbac.decorator.ts
|
|
119
|
+
const arbackAuthorizeInterceptor = (0, moost.defineInterceptorFn)(async (before, after, onError) => {
|
|
120
|
+
const logger = (0, moost.useEventLogger)("arbac");
|
|
121
|
+
const { setScopes, evaluate, resource, action, isPublic } = useArbac();
|
|
122
|
+
if (!action || !resource || isPublic) return;
|
|
123
|
+
try {
|
|
124
|
+
const { allowed, scopes, userId } = await evaluate({
|
|
125
|
+
resource,
|
|
126
|
+
action
|
|
127
|
+
});
|
|
128
|
+
logger.debug(`[${userId}] ${allowed ? "Authorized" : "Blocked"} "${resource}" : "${action}"`);
|
|
129
|
+
if (!allowed) throw new __wooksjs_event_http.HttpError(403, `Insufficient privileges for action "${action}" on resource "${resource}"`);
|
|
130
|
+
setScopes(scopes);
|
|
131
|
+
} catch (error) {
|
|
132
|
+
if (error instanceof __wooksjs_event_http.HttpError) throw error;
|
|
133
|
+
logger.warn(error);
|
|
134
|
+
throw new __wooksjs_event_http.HttpError(401, `Authorization error`);
|
|
135
|
+
}
|
|
136
|
+
}, moost.TInterceptorPriority.GUARD);
|
|
137
|
+
const ArbacAuthorize = () => (0, moost.Intercept)(arbackAuthorizeInterceptor);
|
|
138
|
+
const ArbacScopes = () => (0, moost.Resolve)(() => useArbac().getScopes());
|
|
139
|
+
const ArbacResource = (name) => getArbacMate().decorate("arbacResourceId", name);
|
|
140
|
+
const ArbacAction = (name) => getArbacMate().decorate("arbacActionId", name);
|
|
141
|
+
const ArbacPublic = () => getArbacMate().decorate("arbacPublic", true);
|
|
142
|
+
|
|
143
|
+
//#endregion
|
|
144
|
+
exports.ArbacAction = ArbacAction
|
|
145
|
+
exports.ArbacAuthorize = ArbacAuthorize
|
|
146
|
+
exports.ArbacPublic = ArbacPublic
|
|
147
|
+
exports.ArbacResource = ArbacResource
|
|
148
|
+
exports.ArbacScopes = ArbacScopes
|
|
149
|
+
Object.defineProperty(exports, 'ArbacUserProvider', {
|
|
150
|
+
enumerable: true,
|
|
151
|
+
get: function () {
|
|
152
|
+
return ArbacUserProvider;
|
|
153
|
+
}
|
|
154
|
+
});
|
|
155
|
+
Object.defineProperty(exports, 'MoostArbac', {
|
|
156
|
+
enumerable: true,
|
|
157
|
+
get: function () {
|
|
158
|
+
return MoostArbac;
|
|
159
|
+
}
|
|
160
|
+
});
|
|
161
|
+
exports.arbackAuthorizeInterceptor = arbackAuthorizeInterceptor
|
|
162
|
+
exports.getArbacMate = getArbacMate
|
|
163
|
+
exports.useArbac = useArbac
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
import * as _prostojs_arbac from '@prostojs/arbac';
|
|
2
|
+
import { Arbac } from '@prostojs/arbac';
|
|
3
|
+
import * as moost from 'moost';
|
|
4
|
+
import { Mate, TMoostMetadata, TMateParamMeta } from 'moost';
|
|
5
|
+
|
|
6
|
+
declare function useArbac<TScope extends object>(): {
|
|
7
|
+
getScopes: () => TScope[] | undefined;
|
|
8
|
+
setScopes: (scope: TScope[] | undefined) => TScope[] | undefined;
|
|
9
|
+
evaluate: (opts: {
|
|
10
|
+
resource: string;
|
|
11
|
+
action: string;
|
|
12
|
+
}) => Promise<_prostojs_arbac.TArbacEvalResult<TScope> & {
|
|
13
|
+
userId: string;
|
|
14
|
+
}>;
|
|
15
|
+
resource: string;
|
|
16
|
+
action: string;
|
|
17
|
+
isPublic: boolean;
|
|
18
|
+
};
|
|
19
|
+
|
|
20
|
+
declare const arbackAuthorizeInterceptor: moost.TInterceptorFn;
|
|
21
|
+
declare const ArbacAuthorize: () => ClassDecorator & MethodDecorator;
|
|
22
|
+
declare const ArbacScopes: () => ParameterDecorator & PropertyDecorator;
|
|
23
|
+
declare const ArbacResource: (name: string) => MethodDecorator & ClassDecorator & ParameterDecorator & PropertyDecorator;
|
|
24
|
+
declare const ArbacAction: (name: string) => MethodDecorator & ClassDecorator & ParameterDecorator & PropertyDecorator;
|
|
25
|
+
declare const ArbacPublic: () => MethodDecorator & ClassDecorator & ParameterDecorator & PropertyDecorator;
|
|
26
|
+
|
|
27
|
+
interface TArbacMeta {
|
|
28
|
+
arbacResourceId?: string;
|
|
29
|
+
arbacActionId?: string;
|
|
30
|
+
arbacPublic?: boolean;
|
|
31
|
+
}
|
|
32
|
+
declare function getArbacMate(): Mate<TMoostMetadata & TArbacMeta & {
|
|
33
|
+
params: Array<TMateParamMeta>;
|
|
34
|
+
}, TMoostMetadata & TArbacMeta & {
|
|
35
|
+
params: Array<TMateParamMeta>;
|
|
36
|
+
}>;
|
|
37
|
+
|
|
38
|
+
declare class MoostArbac<TUserAttrs extends object, TScope extends object> extends Arbac<TUserAttrs, TScope> {
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
declare class ArbacUserProvider<TUserAttrs extends object> {
|
|
42
|
+
getUserId(): string | Promise<string>;
|
|
43
|
+
getRoles(id: string): string[] | Promise<string[]>;
|
|
44
|
+
getAttrs(id: string): TUserAttrs | Promise<TUserAttrs>;
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
export { ArbacAction, ArbacAuthorize, ArbacPublic, ArbacResource, ArbacScopes, ArbacUserProvider, MoostArbac, type TArbacMeta, arbackAuthorizeInterceptor, getArbacMate, useArbac };
|
package/dist/index.mjs
ADDED
|
@@ -0,0 +1,120 @@
|
|
|
1
|
+
import { Injectable, Intercept, Resolve, TInterceptorPriority, defineInterceptorFn, getConstructor, getMoostMate, useAsyncEventContext, useControllerContext, useEventLogger } from "moost";
|
|
2
|
+
import { Arbac } from "@prostojs/arbac";
|
|
3
|
+
import { HttpError } from "@wooksjs/event-http";
|
|
4
|
+
|
|
5
|
+
//#region packages/arbac/src/moost-arbac.ts
|
|
6
|
+
function _ts_decorate$1(decorators, target, key, desc) {
|
|
7
|
+
var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
|
|
8
|
+
if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
|
|
9
|
+
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;
|
|
10
|
+
return c > 3 && r && Object.defineProperty(target, key, r), r;
|
|
11
|
+
}
|
|
12
|
+
var MoostArbac = class extends Arbac {};
|
|
13
|
+
MoostArbac = _ts_decorate$1([Injectable()], MoostArbac);
|
|
14
|
+
|
|
15
|
+
//#endregion
|
|
16
|
+
//#region packages/arbac/src/user.provider.ts
|
|
17
|
+
function _ts_decorate(decorators, target, key, desc) {
|
|
18
|
+
var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
|
|
19
|
+
if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
|
|
20
|
+
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;
|
|
21
|
+
return c > 3 && r && Object.defineProperty(target, key, r), r;
|
|
22
|
+
}
|
|
23
|
+
var ArbacUserProvider = class {
|
|
24
|
+
/**
|
|
25
|
+
* Retrieves the unique identifier of the user.
|
|
26
|
+
*
|
|
27
|
+
* @returns {string | Promise<string>} The user ID, or a rejected promise if not implemented.
|
|
28
|
+
* @throws {Error} If the method is not overridden by a subclass.
|
|
29
|
+
*/ getUserId() {
|
|
30
|
+
return Promise.reject(new Error("ArbacUserProvider class must be extended"));
|
|
31
|
+
}
|
|
32
|
+
/**
|
|
33
|
+
* Retrieves the roles assigned to a user based on their ID.
|
|
34
|
+
*
|
|
35
|
+
* @param {string} id - The user ID.
|
|
36
|
+
* @returns {string[] | Promise<string[]>} An array of role identifiers, or a rejected promise if not implemented.
|
|
37
|
+
* @throws {Error} If the method is not overridden by a subclass.
|
|
38
|
+
*/ getRoles(id) {
|
|
39
|
+
return Promise.reject(new Error("ArbacUserProvider class must be extended"));
|
|
40
|
+
}
|
|
41
|
+
/**
|
|
42
|
+
* Retrieves the attributes associated with a user based on their ID.
|
|
43
|
+
*
|
|
44
|
+
* @param {string} id - The user ID.
|
|
45
|
+
* @returns {TUserAttrs | Promise<TUserAttrs>} The user attributes, or a rejected promise if not implemented.
|
|
46
|
+
* @throws {Error} If the method is not overridden by a subclass.
|
|
47
|
+
*/ getAttrs(id) {
|
|
48
|
+
return Promise.reject(new Error("ArbacUserProvider class must be extended"));
|
|
49
|
+
}
|
|
50
|
+
};
|
|
51
|
+
ArbacUserProvider = _ts_decorate([Injectable()], ArbacUserProvider);
|
|
52
|
+
|
|
53
|
+
//#endregion
|
|
54
|
+
//#region packages/arbac/src/arbac.composables.ts
|
|
55
|
+
function useArbac() {
|
|
56
|
+
const store = useAsyncEventContext().store("arbac");
|
|
57
|
+
const cc = useControllerContext();
|
|
58
|
+
const getScopes = () => store.get("scopes");
|
|
59
|
+
const setScopes = (scope) => store.set("scopes", scope);
|
|
60
|
+
const evaluate = async (opts) => {
|
|
61
|
+
const user = await cc.instantiate(ArbacUserProvider);
|
|
62
|
+
const userId = await user.getUserId();
|
|
63
|
+
const arbac = await cc.instantiate(MoostArbac);
|
|
64
|
+
const result = await arbac.evaluate(opts, {
|
|
65
|
+
id: userId,
|
|
66
|
+
roles: await user.getRoles(userId),
|
|
67
|
+
attrs: async (id) => user.getAttrs(id)
|
|
68
|
+
});
|
|
69
|
+
Object.assign(result, { userId });
|
|
70
|
+
return result;
|
|
71
|
+
};
|
|
72
|
+
const cMeta = cc.getControllerMeta();
|
|
73
|
+
const mMeta = cc.getMethodMeta();
|
|
74
|
+
const resource = mMeta?.arbacResourceId || cMeta?.arbacResourceId || cMeta?.id || getConstructor(cc.getController()).name;
|
|
75
|
+
const action = mMeta?.arbacActionId || mMeta?.id || cc.getMethod() || "";
|
|
76
|
+
const isPublic = mMeta?.arbacPublic || cMeta?.arbacPublic || false;
|
|
77
|
+
return {
|
|
78
|
+
getScopes,
|
|
79
|
+
setScopes,
|
|
80
|
+
evaluate,
|
|
81
|
+
resource,
|
|
82
|
+
action,
|
|
83
|
+
isPublic
|
|
84
|
+
};
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
//#endregion
|
|
88
|
+
//#region packages/arbac/src/arbac.mate.ts
|
|
89
|
+
function getArbacMate() {
|
|
90
|
+
return getMoostMate();
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
//#endregion
|
|
94
|
+
//#region packages/arbac/src/arbac.decorator.ts
|
|
95
|
+
const arbackAuthorizeInterceptor = defineInterceptorFn(async (before, after, onError) => {
|
|
96
|
+
const logger = useEventLogger("arbac");
|
|
97
|
+
const { setScopes, evaluate, resource, action, isPublic } = useArbac();
|
|
98
|
+
if (!action || !resource || isPublic) return;
|
|
99
|
+
try {
|
|
100
|
+
const { allowed, scopes, userId } = await evaluate({
|
|
101
|
+
resource,
|
|
102
|
+
action
|
|
103
|
+
});
|
|
104
|
+
logger.debug(`[${userId}] ${allowed ? "Authorized" : "Blocked"} "${resource}" : "${action}"`);
|
|
105
|
+
if (!allowed) throw new HttpError(403, `Insufficient privileges for action "${action}" on resource "${resource}"`);
|
|
106
|
+
setScopes(scopes);
|
|
107
|
+
} catch (error) {
|
|
108
|
+
if (error instanceof HttpError) throw error;
|
|
109
|
+
logger.warn(error);
|
|
110
|
+
throw new HttpError(401, `Authorization error`);
|
|
111
|
+
}
|
|
112
|
+
}, TInterceptorPriority.GUARD);
|
|
113
|
+
const ArbacAuthorize = () => Intercept(arbackAuthorizeInterceptor);
|
|
114
|
+
const ArbacScopes = () => Resolve(() => useArbac().getScopes());
|
|
115
|
+
const ArbacResource = (name) => getArbacMate().decorate("arbacResourceId", name);
|
|
116
|
+
const ArbacAction = (name) => getArbacMate().decorate("arbacActionId", name);
|
|
117
|
+
const ArbacPublic = () => getArbacMate().decorate("arbacPublic", true);
|
|
118
|
+
|
|
119
|
+
//#endregion
|
|
120
|
+
export { ArbacAction, ArbacAuthorize, ArbacPublic, ArbacResource, ArbacScopes, ArbacUserProvider, MoostArbac, arbackAuthorizeInterceptor, getArbacMate, useArbac };
|
package/package.json
ADDED
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@moostjs/arbac",
|
|
3
|
+
"version": "0.5.27",
|
|
4
|
+
"description": "Access Control @prostojs/arbac",
|
|
5
|
+
"main": "dist/index.cjs",
|
|
6
|
+
"module": "dist/index.mjs",
|
|
7
|
+
"types": "dist/index.d.ts",
|
|
8
|
+
"sideEffects": false,
|
|
9
|
+
"exports": {
|
|
10
|
+
"./package.json": "./package.json",
|
|
11
|
+
".": {
|
|
12
|
+
"types": "./dist/index.d.ts",
|
|
13
|
+
"import": "./dist/index.mjs",
|
|
14
|
+
"require": "./dist/index.cjs"
|
|
15
|
+
}
|
|
16
|
+
},
|
|
17
|
+
"files": [
|
|
18
|
+
"dist"
|
|
19
|
+
],
|
|
20
|
+
"repository": {
|
|
21
|
+
"type": "git",
|
|
22
|
+
"url": "git+https://github.com/moostjs/moostjs.git",
|
|
23
|
+
"directory": "packages/arbac"
|
|
24
|
+
},
|
|
25
|
+
"keywords": [
|
|
26
|
+
"moost",
|
|
27
|
+
"moostjs",
|
|
28
|
+
"arbac",
|
|
29
|
+
"rbac",
|
|
30
|
+
"abac",
|
|
31
|
+
"access control"
|
|
32
|
+
],
|
|
33
|
+
"author": "Artem Maltsev",
|
|
34
|
+
"license": "MIT",
|
|
35
|
+
"bugs": {
|
|
36
|
+
"url": "https://github.com/moostjs/moostjs/issues"
|
|
37
|
+
},
|
|
38
|
+
"homepage": "https://github.com/moostjs/moostjs/tree/main/packages/arbac#readme",
|
|
39
|
+
"dependencies": {
|
|
40
|
+
"@wooksjs/event-http": "^0.5.25",
|
|
41
|
+
"@prostojs/arbac": "^0.0.2",
|
|
42
|
+
"moost": "^0.5.27"
|
|
43
|
+
},
|
|
44
|
+
"devDependencies": {
|
|
45
|
+
"vitest": "^3.0.5"
|
|
46
|
+
},
|
|
47
|
+
"scripts": {
|
|
48
|
+
"pub": "pnpm publish --access public",
|
|
49
|
+
"test": "vitest"
|
|
50
|
+
}
|
|
51
|
+
}
|