@apipass/permissions 1.0.192 → 1.0.194
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 +36 -14
- package/esm2022/permission-gate/permission-gate-structural.directive.mjs +80 -0
- package/esm2022/permissions.module.mjs +10 -5
- package/esm2022/public-api.mjs +3 -1
- package/esm2022/service/permissions.providers.mjs +37 -0
- package/fesm2022/apipass-permissions.mjs +122 -7
- package/fesm2022/apipass-permissions.mjs.map +1 -1
- package/package.json +1 -1
- package/permission-gate/permission-gate-structural.directive.d.ts +28 -0
- package/permissions.module.d.ts +4 -3
- package/public-api.d.ts +2 -0
- package/service/permissions.providers.d.ts +30 -0
package/README.md
CHANGED
|
@@ -24,30 +24,51 @@ These mirror the React `PermissionGate`:
|
|
|
24
24
|
|
|
25
25
|
## Setup
|
|
26
26
|
|
|
27
|
+
Each app supplies one `PermissionsConfig` (how to read the Cerbos toggle, how to
|
|
28
|
+
build the principal, how to resolve legacy decisions) and wires everything with a
|
|
29
|
+
single `providePermissions` call:
|
|
30
|
+
|
|
27
31
|
```ts
|
|
28
|
-
|
|
29
|
-
|
|
32
|
+
// permissions-config.factory.ts — the only app-specific piece
|
|
33
|
+
export function permissionsConfigFactory(featureToggle: FeatureToggleService, iam: IamService): PermissionsConfig {
|
|
34
|
+
return {
|
|
35
|
+
cerbosEnabled: async () => (await featureToggle.getFeatures()).isCerbosEnabled(),
|
|
36
|
+
userInfo: async () => (await iam.getLoggedUserInfo()).cerbosUserInfo,
|
|
37
|
+
legacyResolver: async ({ kind, action }, ctx) => {
|
|
38
|
+
const { role } = await iam.getLoggedUserInfo()
|
|
39
|
+
// map { kind, action } → role.canX()
|
|
40
|
+
if (kind === 'flow' && action === 'read') return role.canAccessFlow((ctx as LegacyContext)?.projectId)
|
|
41
|
+
return false
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
```
|
|
46
|
+
|
|
47
|
+
```ts
|
|
48
|
+
import { PermissionsModule, providePermissions } from '@apipass/permissions'
|
|
30
49
|
|
|
31
50
|
@NgModule({
|
|
32
51
|
imports: [PermissionsModule, /* ... */],
|
|
33
52
|
providers: [
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
legacyResolver: async ({ kind, action }, ctx) => {
|
|
39
|
-
const { role } = await iamService.getLoggedUserInfo()
|
|
40
|
-
// map { kind, action } → role.canX()
|
|
41
|
-
if (kind === 'flow' && action === 'read') return role.canAccessFlow((ctx as any)?.projectId)
|
|
42
|
-
return false
|
|
43
|
-
}
|
|
53
|
+
providePermissions({
|
|
54
|
+
cerbosUrl: 'https://cerbos.example.com', // optional: registers CERBOS_SERVICE
|
|
55
|
+
configFactory: permissionsConfigFactory,
|
|
56
|
+
deps: [FEATURE_TOGGLE_SERVICE, IAM_SERVICE]
|
|
44
57
|
})
|
|
45
58
|
]
|
|
46
59
|
})
|
|
47
60
|
export class AppModule {}
|
|
48
61
|
```
|
|
49
62
|
|
|
50
|
-
|
|
63
|
+
For a static config without DI, pass `config` instead of `configFactory`/`deps`
|
|
64
|
+
(or use the lower-level `providePermissionsConfig`).
|
|
65
|
+
|
|
66
|
+
`HttpClient` must be available (e.g. via `provideHttpClient()`), as required by
|
|
67
|
+
`provideCerbos` when `cerbosUrl` is set.
|
|
68
|
+
|
|
69
|
+
> Adoption note: the config callbacks are inherently app-specific. Apps whose
|
|
70
|
+
> `UserInfo` does not expose `cerbosUserInfo` need a small adapter building
|
|
71
|
+
> `{ accountId, domain, roles }` from their own user model.
|
|
51
72
|
|
|
52
73
|
## Reading permissions
|
|
53
74
|
|
|
@@ -105,7 +126,8 @@ For Material controls prefer the component: Material reads its own `disabled`
|
|
|
105
126
|
| Symbol | Kind | Description |
|
|
106
127
|
|---|---|---|
|
|
107
128
|
| `PermissionsService` | service | `checkResource` / `checkResources`, hybrid Cerbos + legacy |
|
|
108
|
-
| `
|
|
129
|
+
| `providePermissions` / `ProvidePermissionsOptions` | fn / interface | One-call setup (config + service + optional Cerbos) |
|
|
130
|
+
| `PERMISSIONS_CONFIG` / `providePermissionsConfig` | token / fn | Lower-level per-app wiring |
|
|
109
131
|
| `PermissionsConfig` / `LegacyCheck` | interface | Config contract |
|
|
110
132
|
| `PermissionGateComponent` | component | `<apipass-permission-gate>` |
|
|
111
133
|
| `PermissionGateDirective` | directive | `[apipassPermissionGate]` |
|
|
@@ -0,0 +1,80 @@
|
|
|
1
|
+
import { Directive, HostBinding, HostListener, Input } from '@angular/core';
|
|
2
|
+
import * as i0 from "@angular/core";
|
|
3
|
+
/**
|
|
4
|
+
* Gates an action element by user permission — single, unified API.
|
|
5
|
+
*
|
|
6
|
+
* IMPORTANT: this is UX gating only. The real authorization boundary is the
|
|
7
|
+
* backend Cerbos PEP (fail-closed). Never rely on this directive for security.
|
|
8
|
+
*
|
|
9
|
+
* Works on:
|
|
10
|
+
* - native `<button>` and `mat-menu-item` — disabled via the native `disabled`
|
|
11
|
+
* property (Material grays them out automatically);
|
|
12
|
+
* - apipass buttons (`secondary-button`, `primary-button`, `tertiary-button`)
|
|
13
|
+
* that read the `aria-disabled` attribute set here and disable themselves;
|
|
14
|
+
* - any other element — `.apipass-gate-blocked` class is exposed as a CSS hook.
|
|
15
|
+
*/
|
|
16
|
+
class ApipassGateDirective {
|
|
17
|
+
isAllowed = true;
|
|
18
|
+
label = '';
|
|
19
|
+
get blocked() { return !this.isAllowed; }
|
|
20
|
+
// Native disabled — <button>, mat-menu-item gray out on their own.
|
|
21
|
+
get _disabled() { return this.blocked || null; }
|
|
22
|
+
// Accessibility + the signal apipass buttons read to disable themselves.
|
|
23
|
+
get _ariaDisabled() { return this.blocked || null; }
|
|
24
|
+
// CSS hook for elements that are neither native buttons nor apipass buttons.
|
|
25
|
+
get _blockedClass() { return this.blocked; }
|
|
26
|
+
get _title() { return this.blocked && this.label ? this.label : null; }
|
|
27
|
+
get _cursor() { return this.blocked ? 'not-allowed' : null; }
|
|
28
|
+
// Keeps pointer-events active so the native title tooltip fires on hover
|
|
29
|
+
// even when the element is disabled (Material sets pointer-events:none).
|
|
30
|
+
get _pointerEvents() { return this.blocked ? 'auto' : null; }
|
|
31
|
+
// Belt-and-suspenders: block the action even if the host still emits an
|
|
32
|
+
// event. Covers mouse click and keyboard activation (Enter / Space).
|
|
33
|
+
block(e) {
|
|
34
|
+
if (this.blocked) {
|
|
35
|
+
e.preventDefault();
|
|
36
|
+
e.stopImmediatePropagation();
|
|
37
|
+
}
|
|
38
|
+
}
|
|
39
|
+
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "16.0.2", ngImport: i0, type: ApipassGateDirective, deps: [], target: i0.ɵɵFactoryTarget.Directive });
|
|
40
|
+
static ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "14.0.0", version: "16.0.2", type: ApipassGateDirective, selector: "[apipassGate]", inputs: { isAllowed: ["apipassGate", "isAllowed"], label: ["apipassGateLabel", "label"] }, host: { listeners: { "click": "block($event)", "keydown.enter": "block($event)", "keydown.space": "block($event)" }, properties: { "disabled": "this._disabled", "attr.aria-disabled": "this._ariaDisabled", "class.apipass-gate-blocked": "this._blockedClass", "attr.title": "this._title", "style.cursor": "this._cursor", "style.pointer-events": "this._pointerEvents" } }, ngImport: i0 });
|
|
41
|
+
}
|
|
42
|
+
export { ApipassGateDirective };
|
|
43
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "16.0.2", ngImport: i0, type: ApipassGateDirective, decorators: [{
|
|
44
|
+
type: Directive,
|
|
45
|
+
args: [{ selector: '[apipassGate]' }]
|
|
46
|
+
}], propDecorators: { isAllowed: [{
|
|
47
|
+
type: Input,
|
|
48
|
+
args: ['apipassGate']
|
|
49
|
+
}], label: [{
|
|
50
|
+
type: Input,
|
|
51
|
+
args: ['apipassGateLabel']
|
|
52
|
+
}], _disabled: [{
|
|
53
|
+
type: HostBinding,
|
|
54
|
+
args: ['disabled']
|
|
55
|
+
}], _ariaDisabled: [{
|
|
56
|
+
type: HostBinding,
|
|
57
|
+
args: ['attr.aria-disabled']
|
|
58
|
+
}], _blockedClass: [{
|
|
59
|
+
type: HostBinding,
|
|
60
|
+
args: ['class.apipass-gate-blocked']
|
|
61
|
+
}], _title: [{
|
|
62
|
+
type: HostBinding,
|
|
63
|
+
args: ['attr.title']
|
|
64
|
+
}], _cursor: [{
|
|
65
|
+
type: HostBinding,
|
|
66
|
+
args: ['style.cursor']
|
|
67
|
+
}], _pointerEvents: [{
|
|
68
|
+
type: HostBinding,
|
|
69
|
+
args: ['style.pointer-events']
|
|
70
|
+
}], block: [{
|
|
71
|
+
type: HostListener,
|
|
72
|
+
args: ['click', ['$event']]
|
|
73
|
+
}, {
|
|
74
|
+
type: HostListener,
|
|
75
|
+
args: ['keydown.enter', ['$event']]
|
|
76
|
+
}, {
|
|
77
|
+
type: HostListener,
|
|
78
|
+
args: ['keydown.space', ['$event']]
|
|
79
|
+
}] } });
|
|
80
|
+
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoicGVybWlzc2lvbi1nYXRlLXN0cnVjdHVyYWwuZGlyZWN0aXZlLmpzIiwic291cmNlUm9vdCI6IiIsInNvdXJjZXMiOlsiLi4vLi4vLi4vLi4vcHJvamVjdHMvcGVybWlzc2lvbnMvc3JjL3Blcm1pc3Npb24tZ2F0ZS9wZXJtaXNzaW9uLWdhdGUtc3RydWN0dXJhbC5kaXJlY3RpdmUudHMiXSwibmFtZXMiOltdLCJtYXBwaW5ncyI6IkFBQUEsT0FBTyxFQUFFLFNBQVMsRUFBRSxXQUFXLEVBQUUsWUFBWSxFQUFFLEtBQUssRUFBRSxNQUFNLGVBQWUsQ0FBQTs7QUFFM0U7Ozs7Ozs7Ozs7OztHQVlHO0FBQ0gsTUFDYSxvQkFBb0I7SUFFVCxTQUFTLEdBQUcsSUFBSSxDQUFBO0lBQ1gsS0FBSyxHQUFHLEVBQUUsQ0FBQTtJQUVyQyxJQUFZLE9BQU8sS0FBYyxPQUFPLENBQUMsSUFBSSxDQUFDLFNBQVMsQ0FBQSxDQUFDLENBQUM7SUFFekQsbUVBQW1FO0lBQ25FLElBQ0ksU0FBUyxLQUFrQixPQUFPLElBQUksQ0FBQyxPQUFPLElBQUksSUFBSSxDQUFBLENBQUMsQ0FBQztJQUU1RCx5RUFBeUU7SUFDekUsSUFDSSxhQUFhLEtBQWtCLE9BQU8sSUFBSSxDQUFDLE9BQU8sSUFBSSxJQUFJLENBQUEsQ0FBQyxDQUFDO0lBRWhFLDZFQUE2RTtJQUM3RSxJQUNJLGFBQWEsS0FBYyxPQUFPLElBQUksQ0FBQyxPQUFPLENBQUEsQ0FBQyxDQUFDO0lBRXBELElBQ0ksTUFBTSxLQUFvQixPQUFPLElBQUksQ0FBQyxPQUFPLElBQUksSUFBSSxDQUFDLEtBQUssQ0FBQyxDQUFDLENBQUMsSUFBSSxDQUFDLEtBQUssQ0FBQyxDQUFDLENBQUMsSUFBSSxDQUFBLENBQUMsQ0FBQztJQUVyRixJQUNJLE9BQU8sS0FBb0IsT0FBTyxJQUFJLENBQUMsT0FBTyxDQUFDLENBQUMsQ0FBQyxhQUFhLENBQUMsQ0FBQyxDQUFDLElBQUksQ0FBQSxDQUFDLENBQUM7SUFFM0UseUVBQXlFO0lBQ3pFLHlFQUF5RTtJQUN6RSxJQUNJLGNBQWMsS0FBb0IsT0FBTyxJQUFJLENBQUMsT0FBTyxDQUFDLENBQUMsQ0FBQyxNQUFNLENBQUMsQ0FBQyxDQUFDLElBQUksQ0FBQSxDQUFDLENBQUM7SUFFM0Usd0VBQXdFO0lBQ3hFLHFFQUFxRTtJQUlyRSxLQUFLLENBQUMsQ0FBUTtRQUNaLElBQUksSUFBSSxDQUFDLE9BQU8sRUFBRTtZQUNoQixDQUFDLENBQUMsY0FBYyxFQUFFLENBQUE7WUFDbEIsQ0FBQyxDQUFDLHdCQUF3QixFQUFFLENBQUE7U0FDN0I7SUFDSCxDQUFDO3VHQXhDVSxvQkFBb0I7MkZBQXBCLG9CQUFvQjs7U0FBcEIsb0JBQW9COzJGQUFwQixvQkFBb0I7a0JBRGhDLFNBQVM7bUJBQUMsRUFBRSxRQUFRLEVBQUUsZUFBZSxFQUFFOzhCQUdoQixTQUFTO3NCQUE5QixLQUFLO3VCQUFDLGFBQWE7Z0JBQ08sS0FBSztzQkFBL0IsS0FBSzt1QkFBQyxrQkFBa0I7Z0JBTXJCLFNBQVM7c0JBRFosV0FBVzt1QkFBQyxVQUFVO2dCQUtuQixhQUFhO3NCQURoQixXQUFXO3VCQUFDLG9CQUFvQjtnQkFLN0IsYUFBYTtzQkFEaEIsV0FBVzt1QkFBQyw0QkFBNEI7Z0JBSXJDLE1BQU07c0JBRFQsV0FBVzt1QkFBQyxZQUFZO2dCQUlyQixPQUFPO3NCQURWLFdBQVc7dUJBQUMsY0FBYztnQkFNdkIsY0FBYztzQkFEakIsV0FBVzt1QkFBQyxzQkFBc0I7Z0JBUW5DLEtBQUs7c0JBSEosWUFBWTt1QkFBQyxPQUFPLEVBQUUsQ0FBQyxRQUFRLENBQUM7O3NCQUNoQyxZQUFZO3VCQUFDLGVBQWUsRUFBRSxDQUFDLFFBQVEsQ0FBQzs7c0JBQ3hDLFlBQVk7dUJBQUMsZUFBZSxFQUFFLENBQUMsUUFBUSxDQUFDIiwic291cmNlc0NvbnRlbnQiOlsiaW1wb3J0IHsgRGlyZWN0aXZlLCBIb3N0QmluZGluZywgSG9zdExpc3RlbmVyLCBJbnB1dCB9IGZyb20gJ0Bhbmd1bGFyL2NvcmUnXG5cbi8qKlxuICogR2F0ZXMgYW4gYWN0aW9uIGVsZW1lbnQgYnkgdXNlciBwZXJtaXNzaW9uIOKAlCBzaW5nbGUsIHVuaWZpZWQgQVBJLlxuICpcbiAqIElNUE9SVEFOVDogdGhpcyBpcyBVWCBnYXRpbmcgb25seS4gVGhlIHJlYWwgYXV0aG9yaXphdGlvbiBib3VuZGFyeSBpcyB0aGVcbiAqIGJhY2tlbmQgQ2VyYm9zIFBFUCAoZmFpbC1jbG9zZWQpLiBOZXZlciByZWx5IG9uIHRoaXMgZGlyZWN0aXZlIGZvciBzZWN1cml0eS5cbiAqXG4gKiBXb3JrcyBvbjpcbiAqICAtIG5hdGl2ZSBgPGJ1dHRvbj5gIGFuZCBgbWF0LW1lbnUtaXRlbWAg4oCUIGRpc2FibGVkIHZpYSB0aGUgbmF0aXZlIGBkaXNhYmxlZGBcbiAqICAgIHByb3BlcnR5IChNYXRlcmlhbCBncmF5cyB0aGVtIG91dCBhdXRvbWF0aWNhbGx5KTtcbiAqICAtIGFwaXBhc3MgYnV0dG9ucyAoYHNlY29uZGFyeS1idXR0b25gLCBgcHJpbWFyeS1idXR0b25gLCBgdGVydGlhcnktYnV0dG9uYClcbiAqICAgIHRoYXQgcmVhZCB0aGUgYGFyaWEtZGlzYWJsZWRgIGF0dHJpYnV0ZSBzZXQgaGVyZSBhbmQgZGlzYWJsZSB0aGVtc2VsdmVzO1xuICogIC0gYW55IG90aGVyIGVsZW1lbnQg4oCUIGAuYXBpcGFzcy1nYXRlLWJsb2NrZWRgIGNsYXNzIGlzIGV4cG9zZWQgYXMgYSBDU1MgaG9vay5cbiAqL1xuQERpcmVjdGl2ZSh7IHNlbGVjdG9yOiAnW2FwaXBhc3NHYXRlXScgfSlcbmV4cG9ydCBjbGFzcyBBcGlwYXNzR2F0ZURpcmVjdGl2ZSB7XG5cbiAgQElucHV0KCdhcGlwYXNzR2F0ZScpIGlzQWxsb3dlZCA9IHRydWVcbiAgQElucHV0KCdhcGlwYXNzR2F0ZUxhYmVsJykgbGFiZWwgPSAnJ1xuXG4gIHByaXZhdGUgZ2V0IGJsb2NrZWQoKTogYm9vbGVhbiB7IHJldHVybiAhdGhpcy5pc0FsbG93ZWQgfVxuXG4gIC8vIE5hdGl2ZSBkaXNhYmxlZCDigJQgPGJ1dHRvbj4sIG1hdC1tZW51LWl0ZW0gZ3JheSBvdXQgb24gdGhlaXIgb3duLlxuICBASG9zdEJpbmRpbmcoJ2Rpc2FibGVkJylcbiAgZ2V0IF9kaXNhYmxlZCgpOiB0cnVlIHwgbnVsbCB7IHJldHVybiB0aGlzLmJsb2NrZWQgfHwgbnVsbCB9XG5cbiAgLy8gQWNjZXNzaWJpbGl0eSArIHRoZSBzaWduYWwgYXBpcGFzcyBidXR0b25zIHJlYWQgdG8gZGlzYWJsZSB0aGVtc2VsdmVzLlxuICBASG9zdEJpbmRpbmcoJ2F0dHIuYXJpYS1kaXNhYmxlZCcpXG4gIGdldCBfYXJpYURpc2FibGVkKCk6IHRydWUgfCBudWxsIHsgcmV0dXJuIHRoaXMuYmxvY2tlZCB8fCBudWxsIH1cblxuICAvLyBDU1MgaG9vayBmb3IgZWxlbWVudHMgdGhhdCBhcmUgbmVpdGhlciBuYXRpdmUgYnV0dG9ucyBub3IgYXBpcGFzcyBidXR0b25zLlxuICBASG9zdEJpbmRpbmcoJ2NsYXNzLmFwaXBhc3MtZ2F0ZS1ibG9ja2VkJylcbiAgZ2V0IF9ibG9ja2VkQ2xhc3MoKTogYm9vbGVhbiB7IHJldHVybiB0aGlzLmJsb2NrZWQgfVxuXG4gIEBIb3N0QmluZGluZygnYXR0ci50aXRsZScpXG4gIGdldCBfdGl0bGUoKTogc3RyaW5nIHwgbnVsbCB7IHJldHVybiB0aGlzLmJsb2NrZWQgJiYgdGhpcy5sYWJlbCA/IHRoaXMubGFiZWwgOiBudWxsIH1cblxuICBASG9zdEJpbmRpbmcoJ3N0eWxlLmN1cnNvcicpXG4gIGdldCBfY3Vyc29yKCk6IHN0cmluZyB8IG51bGwgeyByZXR1cm4gdGhpcy5ibG9ja2VkID8gJ25vdC1hbGxvd2VkJyA6IG51bGwgfVxuXG4gIC8vIEtlZXBzIHBvaW50ZXItZXZlbnRzIGFjdGl2ZSBzbyB0aGUgbmF0aXZlIHRpdGxlIHRvb2x0aXAgZmlyZXMgb24gaG92ZXJcbiAgLy8gZXZlbiB3aGVuIHRoZSBlbGVtZW50IGlzIGRpc2FibGVkIChNYXRlcmlhbCBzZXRzIHBvaW50ZXItZXZlbnRzOm5vbmUpLlxuICBASG9zdEJpbmRpbmcoJ3N0eWxlLnBvaW50ZXItZXZlbnRzJylcbiAgZ2V0IF9wb2ludGVyRXZlbnRzKCk6IHN0cmluZyB8IG51bGwgeyByZXR1cm4gdGhpcy5ibG9ja2VkID8gJ2F1dG8nIDogbnVsbCB9XG5cbiAgLy8gQmVsdC1hbmQtc3VzcGVuZGVyczogYmxvY2sgdGhlIGFjdGlvbiBldmVuIGlmIHRoZSBob3N0IHN0aWxsIGVtaXRzIGFuXG4gIC8vIGV2ZW50LiBDb3ZlcnMgbW91c2UgY2xpY2sgYW5kIGtleWJvYXJkIGFjdGl2YXRpb24gKEVudGVyIC8gU3BhY2UpLlxuICBASG9zdExpc3RlbmVyKCdjbGljaycsIFsnJGV2ZW50J10pXG4gIEBIb3N0TGlzdGVuZXIoJ2tleWRvd24uZW50ZXInLCBbJyRldmVudCddKVxuICBASG9zdExpc3RlbmVyKCdrZXlkb3duLnNwYWNlJywgWyckZXZlbnQnXSlcbiAgYmxvY2soZTogRXZlbnQpOiB2b2lkIHtcbiAgICBpZiAodGhpcy5ibG9ja2VkKSB7XG4gICAgICBlLnByZXZlbnREZWZhdWx0KClcbiAgICAgIGUuc3RvcEltbWVkaWF0ZVByb3BhZ2F0aW9uKClcbiAgICB9XG4gIH1cblxufVxuIl19
|
|
@@ -3,14 +3,17 @@ import { CommonModule } from '@angular/common';
|
|
|
3
3
|
import { MatTooltipModule } from '@angular/material/tooltip';
|
|
4
4
|
import { PermissionGateComponent } from './permission-gate/permission-gate.component';
|
|
5
5
|
import { PermissionGateDirective } from './permission-gate/permission-gate.directive';
|
|
6
|
+
import { ApipassGateDirective } from './permission-gate/permission-gate-structural.directive';
|
|
6
7
|
import { PermissionsService } from './service/permissions.service';
|
|
7
8
|
import * as i0 from "@angular/core";
|
|
8
9
|
class PermissionsModule {
|
|
9
10
|
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "16.0.2", ngImport: i0, type: PermissionsModule, deps: [], target: i0.ɵɵFactoryTarget.NgModule });
|
|
10
11
|
static ɵmod = i0.ɵɵngDeclareNgModule({ minVersion: "14.0.0", version: "16.0.2", ngImport: i0, type: PermissionsModule, declarations: [PermissionGateComponent,
|
|
11
|
-
PermissionGateDirective
|
|
12
|
+
PermissionGateDirective,
|
|
13
|
+
ApipassGateDirective], imports: [CommonModule,
|
|
12
14
|
MatTooltipModule], exports: [PermissionGateComponent,
|
|
13
|
-
PermissionGateDirective
|
|
15
|
+
PermissionGateDirective,
|
|
16
|
+
ApipassGateDirective] });
|
|
14
17
|
static ɵinj = i0.ɵɵngDeclareInjector({ minVersion: "12.0.0", version: "16.0.2", ngImport: i0, type: PermissionsModule, providers: [
|
|
15
18
|
PermissionsService
|
|
16
19
|
], imports: [CommonModule,
|
|
@@ -26,15 +29,17 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "16.0.2", ngImpor
|
|
|
26
29
|
],
|
|
27
30
|
declarations: [
|
|
28
31
|
PermissionGateComponent,
|
|
29
|
-
PermissionGateDirective
|
|
32
|
+
PermissionGateDirective,
|
|
33
|
+
ApipassGateDirective
|
|
30
34
|
],
|
|
31
35
|
exports: [
|
|
32
36
|
PermissionGateComponent,
|
|
33
|
-
PermissionGateDirective
|
|
37
|
+
PermissionGateDirective,
|
|
38
|
+
ApipassGateDirective
|
|
34
39
|
],
|
|
35
40
|
providers: [
|
|
36
41
|
PermissionsService
|
|
37
42
|
]
|
|
38
43
|
}]
|
|
39
44
|
}] });
|
|
40
|
-
//# sourceMappingURL=data:application/json;base64,
|
|
45
|
+
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoicGVybWlzc2lvbnMubW9kdWxlLmpzIiwic291cmNlUm9vdCI6IiIsInNvdXJjZXMiOlsiLi4vLi4vLi4vcHJvamVjdHMvcGVybWlzc2lvbnMvc3JjL3Blcm1pc3Npb25zLm1vZHVsZS50cyJdLCJuYW1lcyI6W10sIm1hcHBpbmdzIjoiQUFBQSxPQUFPLEVBQUUsUUFBUSxFQUFFLE1BQU0sZUFBZSxDQUFBO0FBQ3hDLE9BQU8sRUFBRSxZQUFZLEVBQUUsTUFBTSxpQkFBaUIsQ0FBQTtBQUM5QyxPQUFPLEVBQUUsZ0JBQWdCLEVBQUUsTUFBTSwyQkFBMkIsQ0FBQTtBQUM1RCxPQUFPLEVBQUUsdUJBQXVCLEVBQUUsTUFBTSw2Q0FBNkMsQ0FBQTtBQUNyRixPQUFPLEVBQUUsdUJBQXVCLEVBQUUsTUFBTSw2Q0FBNkMsQ0FBQTtBQUNyRixPQUFPLEVBQUUsb0JBQW9CLEVBQUUsTUFBTSx3REFBd0QsQ0FBQTtBQUM3RixPQUFPLEVBQUUsa0JBQWtCLEVBQUUsTUFBTSwrQkFBK0IsQ0FBQTs7QUFFbEUsTUFtQmEsaUJBQWlCO3VHQUFqQixpQkFBaUI7d0dBQWpCLGlCQUFpQixpQkFiMUIsdUJBQXVCO1lBQ3ZCLHVCQUF1QjtZQUN2QixvQkFBb0IsYUFOcEIsWUFBWTtZQUNaLGdCQUFnQixhQVFoQix1QkFBdUI7WUFDdkIsdUJBQXVCO1lBQ3ZCLG9CQUFvQjt3R0FNWCxpQkFBaUIsYUFKakI7WUFDVCxrQkFBa0I7U0FDbkIsWUFmQyxZQUFZO1lBQ1osZ0JBQWdCOztTQWdCUCxpQkFBaUI7MkZBQWpCLGlCQUFpQjtrQkFuQjdCLFFBQVE7bUJBQUM7b0JBQ1IsT0FBTyxFQUFFO3dCQUNQLFlBQVk7d0JBQ1osZ0JBQWdCO3FCQUNqQjtvQkFDRCxZQUFZLEVBQUU7d0JBQ1osdUJBQXVCO3dCQUN2Qix1QkFBdUI7d0JBQ3ZCLG9CQUFvQjtxQkFDckI7b0JBQ0QsT0FBTyxFQUFFO3dCQUNQLHVCQUF1Qjt3QkFDdkIsdUJBQXVCO3dCQUN2QixvQkFBb0I7cUJBQ3JCO29CQUNELFNBQVMsRUFBRTt3QkFDVCxrQkFBa0I7cUJBQ25CO2lCQUNGIiwic291cmNlc0NvbnRlbnQiOlsiaW1wb3J0IHsgTmdNb2R1bGUgfSBmcm9tICdAYW5ndWxhci9jb3JlJ1xuaW1wb3J0IHsgQ29tbW9uTW9kdWxlIH0gZnJvbSAnQGFuZ3VsYXIvY29tbW9uJ1xuaW1wb3J0IHsgTWF0VG9vbHRpcE1vZHVsZSB9IGZyb20gJ0Bhbmd1bGFyL21hdGVyaWFsL3Rvb2x0aXAnXG5pbXBvcnQgeyBQZXJtaXNzaW9uR2F0ZUNvbXBvbmVudCB9IGZyb20gJy4vcGVybWlzc2lvbi1nYXRlL3Blcm1pc3Npb24tZ2F0ZS5jb21wb25lbnQnXG5pbXBvcnQgeyBQZXJtaXNzaW9uR2F0ZURpcmVjdGl2ZSB9IGZyb20gJy4vcGVybWlzc2lvbi1nYXRlL3Blcm1pc3Npb24tZ2F0ZS5kaXJlY3RpdmUnXG5pbXBvcnQgeyBBcGlwYXNzR2F0ZURpcmVjdGl2ZSB9IGZyb20gJy4vcGVybWlzc2lvbi1nYXRlL3Blcm1pc3Npb24tZ2F0ZS1zdHJ1Y3R1cmFsLmRpcmVjdGl2ZSdcbmltcG9ydCB7IFBlcm1pc3Npb25zU2VydmljZSB9IGZyb20gJy4vc2VydmljZS9wZXJtaXNzaW9ucy5zZXJ2aWNlJ1xuXG5ATmdNb2R1bGUoe1xuICBpbXBvcnRzOiBbXG4gICAgQ29tbW9uTW9kdWxlLFxuICAgIE1hdFRvb2x0aXBNb2R1bGVcbiAgXSxcbiAgZGVjbGFyYXRpb25zOiBbXG4gICAgUGVybWlzc2lvbkdhdGVDb21wb25lbnQsXG4gICAgUGVybWlzc2lvbkdhdGVEaXJlY3RpdmUsXG4gICAgQXBpcGFzc0dhdGVEaXJlY3RpdmVcbiAgXSxcbiAgZXhwb3J0czogW1xuICAgIFBlcm1pc3Npb25HYXRlQ29tcG9uZW50LFxuICAgIFBlcm1pc3Npb25HYXRlRGlyZWN0aXZlLFxuICAgIEFwaXBhc3NHYXRlRGlyZWN0aXZlXG4gIF0sXG4gIHByb3ZpZGVyczogW1xuICAgIFBlcm1pc3Npb25zU2VydmljZVxuICBdXG59KVxuZXhwb3J0IGNsYXNzIFBlcm1pc3Npb25zTW9kdWxlIHt9XG4iXX0=
|
package/esm2022/public-api.mjs
CHANGED
|
@@ -1,8 +1,10 @@
|
|
|
1
1
|
export * from './permissions.module';
|
|
2
2
|
export * from './permission-gate/permission-gate.component';
|
|
3
3
|
export * from './permission-gate/permission-gate.directive';
|
|
4
|
+
export * from './permission-gate/permission-gate-structural.directive';
|
|
4
5
|
export * from './service/permissions.service';
|
|
5
6
|
export * from './service/permissions.config';
|
|
7
|
+
export * from './service/permissions.providers';
|
|
6
8
|
export * from './model/permission-check.interface';
|
|
7
9
|
export * from './model/permission-result.interface';
|
|
8
|
-
//# sourceMappingURL=data:application/json;base64,
|
|
10
|
+
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoicHVibGljLWFwaS5qcyIsInNvdXJjZVJvb3QiOiIiLCJzb3VyY2VzIjpbIi4uLy4uLy4uL3Byb2plY3RzL3Blcm1pc3Npb25zL3NyYy9wdWJsaWMtYXBpLnRzIl0sIm5hbWVzIjpbXSwibWFwcGluZ3MiOiJBQUFBLGNBQWMsc0JBQXNCLENBQUE7QUFDcEMsY0FBYyw2Q0FBNkMsQ0FBQTtBQUMzRCxjQUFjLDZDQUE2QyxDQUFBO0FBQzNELGNBQWMsd0RBQXdELENBQUE7QUFDdEUsY0FBYywrQkFBK0IsQ0FBQTtBQUM3QyxjQUFjLDhCQUE4QixDQUFBO0FBQzVDLGNBQWMsaUNBQWlDLENBQUE7QUFDL0MsY0FBYyxvQ0FBb0MsQ0FBQTtBQUNsRCxjQUFjLHFDQUFxQyxDQUFBIiwic291cmNlc0NvbnRlbnQiOlsiZXhwb3J0ICogZnJvbSAnLi9wZXJtaXNzaW9ucy5tb2R1bGUnXG5leHBvcnQgKiBmcm9tICcuL3Blcm1pc3Npb24tZ2F0ZS9wZXJtaXNzaW9uLWdhdGUuY29tcG9uZW50J1xuZXhwb3J0ICogZnJvbSAnLi9wZXJtaXNzaW9uLWdhdGUvcGVybWlzc2lvbi1nYXRlLmRpcmVjdGl2ZSdcbmV4cG9ydCAqIGZyb20gJy4vcGVybWlzc2lvbi1nYXRlL3Blcm1pc3Npb24tZ2F0ZS1zdHJ1Y3R1cmFsLmRpcmVjdGl2ZSdcbmV4cG9ydCAqIGZyb20gJy4vc2VydmljZS9wZXJtaXNzaW9ucy5zZXJ2aWNlJ1xuZXhwb3J0ICogZnJvbSAnLi9zZXJ2aWNlL3Blcm1pc3Npb25zLmNvbmZpZydcbmV4cG9ydCAqIGZyb20gJy4vc2VydmljZS9wZXJtaXNzaW9ucy5wcm92aWRlcnMnXG5leHBvcnQgKiBmcm9tICcuL21vZGVsL3Blcm1pc3Npb24tY2hlY2suaW50ZXJmYWNlJ1xuZXhwb3J0ICogZnJvbSAnLi9tb2RlbC9wZXJtaXNzaW9uLXJlc3VsdC5pbnRlcmZhY2UnXG4iXX0=
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
import { provideCerbos } from '@apipass/frontend-utils';
|
|
2
|
+
import { PERMISSIONS_CONFIG } from './permissions.config';
|
|
3
|
+
import { PermissionsService } from './permissions.service';
|
|
4
|
+
/**
|
|
5
|
+
* One-call setup for `@apipass/permissions`: registers the {@link PERMISSIONS_CONFIG},
|
|
6
|
+
* the {@link PermissionsService} and (optionally) the Cerbos service.
|
|
7
|
+
*
|
|
8
|
+
* @example
|
|
9
|
+
* ```ts
|
|
10
|
+
* @NgModule({
|
|
11
|
+
* providers: [
|
|
12
|
+
* providePermissions({
|
|
13
|
+
* cerbosUrl: $ENV.CERBOS_PDP_URL,
|
|
14
|
+
* configFactory: permissionsConfigFactory,
|
|
15
|
+
* deps: [FEATURE_TOGGLE_SERVICE, IAM_SERVICE]
|
|
16
|
+
* })
|
|
17
|
+
* ]
|
|
18
|
+
* })
|
|
19
|
+
* ```
|
|
20
|
+
*/
|
|
21
|
+
export function providePermissions(options) {
|
|
22
|
+
const { config, configFactory, deps, cerbosUrl } = options;
|
|
23
|
+
if ((config && configFactory) || (!config && !configFactory)) {
|
|
24
|
+
throw new Error('providePermissions: provide exactly one of `config` or `configFactory`');
|
|
25
|
+
}
|
|
26
|
+
const providers = [
|
|
27
|
+
config
|
|
28
|
+
? { provide: PERMISSIONS_CONFIG, useValue: config }
|
|
29
|
+
: { provide: PERMISSIONS_CONFIG, useFactory: configFactory, deps: deps ?? [] },
|
|
30
|
+
PermissionsService
|
|
31
|
+
];
|
|
32
|
+
if (cerbosUrl) {
|
|
33
|
+
providers.push(provideCerbos(cerbosUrl));
|
|
34
|
+
}
|
|
35
|
+
return providers;
|
|
36
|
+
}
|
|
37
|
+
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoicGVybWlzc2lvbnMucHJvdmlkZXJzLmpzIiwic291cmNlUm9vdCI6IiIsInNvdXJjZXMiOlsiLi4vLi4vLi4vLi4vcHJvamVjdHMvcGVybWlzc2lvbnMvc3JjL3NlcnZpY2UvcGVybWlzc2lvbnMucHJvdmlkZXJzLnRzIl0sIm5hbWVzIjpbXSwibWFwcGluZ3MiOiJBQUNBLE9BQU8sRUFBRSxhQUFhLEVBQUUsTUFBTSx5QkFBeUIsQ0FBQTtBQUN2RCxPQUFPLEVBQUUsa0JBQWtCLEVBQXFCLE1BQU0sc0JBQXNCLENBQUE7QUFDNUUsT0FBTyxFQUFFLGtCQUFrQixFQUFFLE1BQU0sdUJBQXVCLENBQUE7QUFhMUQ7Ozs7Ozs7Ozs7Ozs7Ozs7R0FnQkc7QUFDSCxNQUFNLFVBQVUsa0JBQWtCLENBQUMsT0FBa0M7SUFDbkUsTUFBTSxFQUFFLE1BQU0sRUFBRSxhQUFhLEVBQUUsSUFBSSxFQUFFLFNBQVMsRUFBRSxHQUFHLE9BQU8sQ0FBQTtJQUUxRCxJQUFJLENBQUMsTUFBTSxJQUFJLGFBQWEsQ0FBQyxJQUFJLENBQUMsQ0FBQyxNQUFNLElBQUksQ0FBQyxhQUFhLENBQUMsRUFBRTtRQUM1RCxNQUFNLElBQUksS0FBSyxDQUFDLHdFQUF3RSxDQUFDLENBQUE7S0FDMUY7SUFFRCxNQUFNLFNBQVMsR0FBZTtRQUM1QixNQUFNO1lBQ0osQ0FBQyxDQUFDLEVBQUUsT0FBTyxFQUFFLGtCQUFrQixFQUFFLFFBQVEsRUFBRSxNQUFNLEVBQUU7WUFDbkQsQ0FBQyxDQUFDLEVBQUUsT0FBTyxFQUFFLGtCQUFrQixFQUFFLFVBQVUsRUFBRSxhQUFhLEVBQUUsSUFBSSxFQUFFLElBQUksSUFBSSxFQUFFLEVBQUU7UUFDaEYsa0JBQWtCO0tBQ25CLENBQUE7SUFFRCxJQUFJLFNBQVMsRUFBRTtRQUNiLFNBQVMsQ0FBQyxJQUFJLENBQUMsYUFBYSxDQUFDLFNBQVMsQ0FBQyxDQUFDLENBQUE7S0FDekM7SUFFRCxPQUFPLFNBQVMsQ0FBQTtBQUNsQixDQUFDIiwic291cmNlc0NvbnRlbnQiOlsiaW1wb3J0IHsgUHJvdmlkZXIgfSBmcm9tICdAYW5ndWxhci9jb3JlJ1xuaW1wb3J0IHsgcHJvdmlkZUNlcmJvcyB9IGZyb20gJ0BhcGlwYXNzL2Zyb250ZW5kLXV0aWxzJ1xuaW1wb3J0IHsgUEVSTUlTU0lPTlNfQ09ORklHLCBQZXJtaXNzaW9uc0NvbmZpZyB9IGZyb20gJy4vcGVybWlzc2lvbnMuY29uZmlnJ1xuaW1wb3J0IHsgUGVybWlzc2lvbnNTZXJ2aWNlIH0gZnJvbSAnLi9wZXJtaXNzaW9ucy5zZXJ2aWNlJ1xuXG5leHBvcnQgaW50ZXJmYWNlIFByb3ZpZGVQZXJtaXNzaW9uc09wdGlvbnMge1xuICAvKiogU3RhdGljIGNvbmZpZyAocmVnaXN0ZXJlZCB3aXRoIGB1c2VWYWx1ZWApLiBNdXR1YWxseSBleGNsdXNpdmUgd2l0aCBgY29uZmlnRmFjdG9yeWAuICovXG4gIGNvbmZpZz86IFBlcm1pc3Npb25zQ29uZmlnXG4gIC8qKiBDb25maWcgZmFjdG9yeSB3aXRoIERJIChyZWdpc3RlcmVkIHdpdGggYHVzZUZhY3RvcnlgKS4gTXV0dWFsbHkgZXhjbHVzaXZlIHdpdGggYGNvbmZpZ2AuICovXG4gIGNvbmZpZ0ZhY3Rvcnk/OiAoLi4uZGVwczogbmV2ZXJbXSkgPT4gUGVybWlzc2lvbnNDb25maWdcbiAgLyoqIEluamVjdGlvbiB0b2tlbnMgcGFzc2VkIHRvIGBjb25maWdGYWN0b3J5YCwgaW4gcGFyYW1ldGVyIG9yZGVyLiAqL1xuICBkZXBzPzogdW5rbm93bltdXG4gIC8qKiBPcHRpb25hbCBDZXJib3MgUERQIHVybDsgd2hlbiBzZXQsIGFsc28gcmVnaXN0ZXJzIGBDRVJCT1NfU0VSVklDRWAgdmlhIGBwcm92aWRlQ2VyYm9zYC4gKi9cbiAgY2VyYm9zVXJsPzogc3RyaW5nXG59XG5cbi8qKlxuICogT25lLWNhbGwgc2V0dXAgZm9yIGBAYXBpcGFzcy9wZXJtaXNzaW9uc2A6IHJlZ2lzdGVycyB0aGUge0BsaW5rIFBFUk1JU1NJT05TX0NPTkZJR30sXG4gKiB0aGUge0BsaW5rIFBlcm1pc3Npb25zU2VydmljZX0gYW5kIChvcHRpb25hbGx5KSB0aGUgQ2VyYm9zIHNlcnZpY2UuXG4gKlxuICogQGV4YW1wbGVcbiAqIGBgYHRzXG4gKiBATmdNb2R1bGUoe1xuICogICBwcm92aWRlcnM6IFtcbiAqICAgICBwcm92aWRlUGVybWlzc2lvbnMoe1xuICogICAgICAgY2VyYm9zVXJsOiAkRU5WLkNFUkJPU19QRFBfVVJMLFxuICogICAgICAgY29uZmlnRmFjdG9yeTogcGVybWlzc2lvbnNDb25maWdGYWN0b3J5LFxuICogICAgICAgZGVwczogW0ZFQVRVUkVfVE9HR0xFX1NFUlZJQ0UsIElBTV9TRVJWSUNFXVxuICogICAgIH0pXG4gKiAgIF1cbiAqIH0pXG4gKiBgYGBcbiAqL1xuZXhwb3J0IGZ1bmN0aW9uIHByb3ZpZGVQZXJtaXNzaW9ucyhvcHRpb25zOiBQcm92aWRlUGVybWlzc2lvbnNPcHRpb25zKTogUHJvdmlkZXJbXSB7XG4gIGNvbnN0IHsgY29uZmlnLCBjb25maWdGYWN0b3J5LCBkZXBzLCBjZXJib3NVcmwgfSA9IG9wdGlvbnNcblxuICBpZiAoKGNvbmZpZyAmJiBjb25maWdGYWN0b3J5KSB8fCAoIWNvbmZpZyAmJiAhY29uZmlnRmFjdG9yeSkpIHtcbiAgICB0aHJvdyBuZXcgRXJyb3IoJ3Byb3ZpZGVQZXJtaXNzaW9uczogcHJvdmlkZSBleGFjdGx5IG9uZSBvZiBgY29uZmlnYCBvciBgY29uZmlnRmFjdG9yeWAnKVxuICB9XG5cbiAgY29uc3QgcHJvdmlkZXJzOiBQcm92aWRlcltdID0gW1xuICAgIGNvbmZpZ1xuICAgICAgPyB7IHByb3ZpZGU6IFBFUk1JU1NJT05TX0NPTkZJRywgdXNlVmFsdWU6IGNvbmZpZyB9XG4gICAgICA6IHsgcHJvdmlkZTogUEVSTUlTU0lPTlNfQ09ORklHLCB1c2VGYWN0b3J5OiBjb25maWdGYWN0b3J5LCBkZXBzOiBkZXBzID8/IFtdIH0sXG4gICAgUGVybWlzc2lvbnNTZXJ2aWNlXG4gIF1cblxuICBpZiAoY2VyYm9zVXJsKSB7XG4gICAgcHJvdmlkZXJzLnB1c2gocHJvdmlkZUNlcmJvcyhjZXJib3NVcmwpKVxuICB9XG5cbiAgcmV0dXJuIHByb3ZpZGVyc1xufVxuIl19
|
|
@@ -1,10 +1,10 @@
|
|
|
1
1
|
import * as i0 from '@angular/core';
|
|
2
|
-
import { TemplateRef, Component, Input, ContentChild, Directive, InjectionToken, Injectable, Inject, Optional, NgModule } from '@angular/core';
|
|
2
|
+
import { TemplateRef, Component, Input, ContentChild, Directive, HostBinding, HostListener, InjectionToken, Injectable, Inject, Optional, NgModule } from '@angular/core';
|
|
3
3
|
import * as i1 from '@angular/common';
|
|
4
4
|
import { CommonModule } from '@angular/common';
|
|
5
5
|
import * as i2 from '@angular/material/tooltip';
|
|
6
6
|
import { MatTooltipModule } from '@angular/material/tooltip';
|
|
7
|
-
import { CERBOS_SERVICE } from '@apipass/frontend-utils';
|
|
7
|
+
import { CERBOS_SERVICE, provideCerbos } from '@apipass/frontend-utils';
|
|
8
8
|
|
|
9
9
|
/**
|
|
10
10
|
* Angular equivalent of the React `PermissionGate`.
|
|
@@ -100,6 +100,83 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "16.0.2", ngImpor
|
|
|
100
100
|
type: Input
|
|
101
101
|
}] } });
|
|
102
102
|
|
|
103
|
+
/**
|
|
104
|
+
* Gates an action element by user permission — single, unified API.
|
|
105
|
+
*
|
|
106
|
+
* IMPORTANT: this is UX gating only. The real authorization boundary is the
|
|
107
|
+
* backend Cerbos PEP (fail-closed). Never rely on this directive for security.
|
|
108
|
+
*
|
|
109
|
+
* Works on:
|
|
110
|
+
* - native `<button>` and `mat-menu-item` — disabled via the native `disabled`
|
|
111
|
+
* property (Material grays them out automatically);
|
|
112
|
+
* - apipass buttons (`secondary-button`, `primary-button`, `tertiary-button`)
|
|
113
|
+
* that read the `aria-disabled` attribute set here and disable themselves;
|
|
114
|
+
* - any other element — `.apipass-gate-blocked` class is exposed as a CSS hook.
|
|
115
|
+
*/
|
|
116
|
+
class ApipassGateDirective {
|
|
117
|
+
isAllowed = true;
|
|
118
|
+
label = '';
|
|
119
|
+
get blocked() { return !this.isAllowed; }
|
|
120
|
+
// Native disabled — <button>, mat-menu-item gray out on their own.
|
|
121
|
+
get _disabled() { return this.blocked || null; }
|
|
122
|
+
// Accessibility + the signal apipass buttons read to disable themselves.
|
|
123
|
+
get _ariaDisabled() { return this.blocked || null; }
|
|
124
|
+
// CSS hook for elements that are neither native buttons nor apipass buttons.
|
|
125
|
+
get _blockedClass() { return this.blocked; }
|
|
126
|
+
get _title() { return this.blocked && this.label ? this.label : null; }
|
|
127
|
+
get _cursor() { return this.blocked ? 'not-allowed' : null; }
|
|
128
|
+
// Keeps pointer-events active so the native title tooltip fires on hover
|
|
129
|
+
// even when the element is disabled (Material sets pointer-events:none).
|
|
130
|
+
get _pointerEvents() { return this.blocked ? 'auto' : null; }
|
|
131
|
+
// Belt-and-suspenders: block the action even if the host still emits an
|
|
132
|
+
// event. Covers mouse click and keyboard activation (Enter / Space).
|
|
133
|
+
block(e) {
|
|
134
|
+
if (this.blocked) {
|
|
135
|
+
e.preventDefault();
|
|
136
|
+
e.stopImmediatePropagation();
|
|
137
|
+
}
|
|
138
|
+
}
|
|
139
|
+
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "16.0.2", ngImport: i0, type: ApipassGateDirective, deps: [], target: i0.ɵɵFactoryTarget.Directive });
|
|
140
|
+
static ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "14.0.0", version: "16.0.2", type: ApipassGateDirective, selector: "[apipassGate]", inputs: { isAllowed: ["apipassGate", "isAllowed"], label: ["apipassGateLabel", "label"] }, host: { listeners: { "click": "block($event)", "keydown.enter": "block($event)", "keydown.space": "block($event)" }, properties: { "disabled": "this._disabled", "attr.aria-disabled": "this._ariaDisabled", "class.apipass-gate-blocked": "this._blockedClass", "attr.title": "this._title", "style.cursor": "this._cursor", "style.pointer-events": "this._pointerEvents" } }, ngImport: i0 });
|
|
141
|
+
}
|
|
142
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "16.0.2", ngImport: i0, type: ApipassGateDirective, decorators: [{
|
|
143
|
+
type: Directive,
|
|
144
|
+
args: [{ selector: '[apipassGate]' }]
|
|
145
|
+
}], propDecorators: { isAllowed: [{
|
|
146
|
+
type: Input,
|
|
147
|
+
args: ['apipassGate']
|
|
148
|
+
}], label: [{
|
|
149
|
+
type: Input,
|
|
150
|
+
args: ['apipassGateLabel']
|
|
151
|
+
}], _disabled: [{
|
|
152
|
+
type: HostBinding,
|
|
153
|
+
args: ['disabled']
|
|
154
|
+
}], _ariaDisabled: [{
|
|
155
|
+
type: HostBinding,
|
|
156
|
+
args: ['attr.aria-disabled']
|
|
157
|
+
}], _blockedClass: [{
|
|
158
|
+
type: HostBinding,
|
|
159
|
+
args: ['class.apipass-gate-blocked']
|
|
160
|
+
}], _title: [{
|
|
161
|
+
type: HostBinding,
|
|
162
|
+
args: ['attr.title']
|
|
163
|
+
}], _cursor: [{
|
|
164
|
+
type: HostBinding,
|
|
165
|
+
args: ['style.cursor']
|
|
166
|
+
}], _pointerEvents: [{
|
|
167
|
+
type: HostBinding,
|
|
168
|
+
args: ['style.pointer-events']
|
|
169
|
+
}], block: [{
|
|
170
|
+
type: HostListener,
|
|
171
|
+
args: ['click', ['$event']]
|
|
172
|
+
}, {
|
|
173
|
+
type: HostListener,
|
|
174
|
+
args: ['keydown.enter', ['$event']]
|
|
175
|
+
}, {
|
|
176
|
+
type: HostListener,
|
|
177
|
+
args: ['keydown.space', ['$event']]
|
|
178
|
+
}] } });
|
|
179
|
+
|
|
103
180
|
const PERMISSIONS_CONFIG = new InjectionToken('PERMISSIONS_CONFIG');
|
|
104
181
|
/** Registers the {@link PermissionsConfig} used by {@link PermissionsService}. */
|
|
105
182
|
function providePermissionsConfig(config) {
|
|
@@ -194,9 +271,11 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "16.0.2", ngImpor
|
|
|
194
271
|
class PermissionsModule {
|
|
195
272
|
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "16.0.2", ngImport: i0, type: PermissionsModule, deps: [], target: i0.ɵɵFactoryTarget.NgModule });
|
|
196
273
|
static ɵmod = i0.ɵɵngDeclareNgModule({ minVersion: "14.0.0", version: "16.0.2", ngImport: i0, type: PermissionsModule, declarations: [PermissionGateComponent,
|
|
197
|
-
PermissionGateDirective
|
|
274
|
+
PermissionGateDirective,
|
|
275
|
+
ApipassGateDirective], imports: [CommonModule,
|
|
198
276
|
MatTooltipModule], exports: [PermissionGateComponent,
|
|
199
|
-
PermissionGateDirective
|
|
277
|
+
PermissionGateDirective,
|
|
278
|
+
ApipassGateDirective] });
|
|
200
279
|
static ɵinj = i0.ɵɵngDeclareInjector({ minVersion: "12.0.0", version: "16.0.2", ngImport: i0, type: PermissionsModule, providers: [
|
|
201
280
|
PermissionsService
|
|
202
281
|
], imports: [CommonModule,
|
|
@@ -211,11 +290,13 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "16.0.2", ngImpor
|
|
|
211
290
|
],
|
|
212
291
|
declarations: [
|
|
213
292
|
PermissionGateComponent,
|
|
214
|
-
PermissionGateDirective
|
|
293
|
+
PermissionGateDirective,
|
|
294
|
+
ApipassGateDirective
|
|
215
295
|
],
|
|
216
296
|
exports: [
|
|
217
297
|
PermissionGateComponent,
|
|
218
|
-
PermissionGateDirective
|
|
298
|
+
PermissionGateDirective,
|
|
299
|
+
ApipassGateDirective
|
|
219
300
|
],
|
|
220
301
|
providers: [
|
|
221
302
|
PermissionsService
|
|
@@ -223,9 +304,43 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "16.0.2", ngImpor
|
|
|
223
304
|
}]
|
|
224
305
|
}] });
|
|
225
306
|
|
|
307
|
+
/**
|
|
308
|
+
* One-call setup for `@apipass/permissions`: registers the {@link PERMISSIONS_CONFIG},
|
|
309
|
+
* the {@link PermissionsService} and (optionally) the Cerbos service.
|
|
310
|
+
*
|
|
311
|
+
* @example
|
|
312
|
+
* ```ts
|
|
313
|
+
* @NgModule({
|
|
314
|
+
* providers: [
|
|
315
|
+
* providePermissions({
|
|
316
|
+
* cerbosUrl: $ENV.CERBOS_PDP_URL,
|
|
317
|
+
* configFactory: permissionsConfigFactory,
|
|
318
|
+
* deps: [FEATURE_TOGGLE_SERVICE, IAM_SERVICE]
|
|
319
|
+
* })
|
|
320
|
+
* ]
|
|
321
|
+
* })
|
|
322
|
+
* ```
|
|
323
|
+
*/
|
|
324
|
+
function providePermissions(options) {
|
|
325
|
+
const { config, configFactory, deps, cerbosUrl } = options;
|
|
326
|
+
if ((config && configFactory) || (!config && !configFactory)) {
|
|
327
|
+
throw new Error('providePermissions: provide exactly one of `config` or `configFactory`');
|
|
328
|
+
}
|
|
329
|
+
const providers = [
|
|
330
|
+
config
|
|
331
|
+
? { provide: PERMISSIONS_CONFIG, useValue: config }
|
|
332
|
+
: { provide: PERMISSIONS_CONFIG, useFactory: configFactory, deps: deps ?? [] },
|
|
333
|
+
PermissionsService
|
|
334
|
+
];
|
|
335
|
+
if (cerbosUrl) {
|
|
336
|
+
providers.push(provideCerbos(cerbosUrl));
|
|
337
|
+
}
|
|
338
|
+
return providers;
|
|
339
|
+
}
|
|
340
|
+
|
|
226
341
|
/**
|
|
227
342
|
* Generated bundle index. Do not edit.
|
|
228
343
|
*/
|
|
229
344
|
|
|
230
|
-
export { PERMISSIONS_CONFIG, PermissionGateComponent, PermissionGateDirective, PermissionsModule, PermissionsService, providePermissionsConfig };
|
|
345
|
+
export { ApipassGateDirective, PERMISSIONS_CONFIG, PermissionGateComponent, PermissionGateDirective, PermissionsModule, PermissionsService, providePermissions, providePermissionsConfig };
|
|
231
346
|
//# sourceMappingURL=apipass-permissions.mjs.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"apipass-permissions.mjs","sources":["../../../projects/permissions/src/permission-gate/permission-gate.component.ts","../../../projects/permissions/src/permission-gate/permission-gate.component.html","../../../projects/permissions/src/permission-gate/permission-gate.directive.ts","../../../projects/permissions/src/service/permissions.config.ts","../../../projects/permissions/src/service/permissions.service.ts","../../../projects/permissions/src/permissions.module.ts","../../../projects/permissions/src/apipass-permissions.ts"],"sourcesContent":["import { Component, ContentChild, Input, TemplateRef } from '@angular/core'\n\n/** Context object passed to the projected template — `$implicit` is the `allowed` flag. */\nexport interface PermissionGateContext {\n $implicit: boolean\n}\n\n/**\n * Angular equivalent of the React `PermissionGate`.\n *\n * It **never hides** the protected action: when blocked it still renders the\n * content, disabled, wrapped in a tooltip that explains why. The decision\n * (`isAllowed`) is supplied by the caller — this component only presents it.\n *\n * @example\n * ```html\n * <apipass-permission-gate [isAllowed]=\"canCreate\" [noAccessLabel]=\"'PERMISSIONS.NO_CREATE' | translate\">\n * <ng-template let-allowed>\n * <button mat-raised-button [disabled]=\"!allowed\" (click)=\"allowed && create()\">New</button>\n * </ng-template>\n * </apipass-permission-gate>\n * ```\n */\n@Component({\n selector: 'apipass-permission-gate',\n templateUrl: './permission-gate.component.html',\n styleUrls: ['./permission-gate.component.scss']\n})\nexport class PermissionGateComponent {\n\n /** Whether the gated action is allowed. */\n @Input() isAllowed = false\n\n /** Tooltip text shown when the action is blocked. */\n @Input() noAccessLabel = ''\n\n @ContentChild(TemplateRef) template?: TemplateRef<PermissionGateContext>\n\n}\n","<ng-container *ngIf=\"isAllowed; else blocked\">\n <ng-container *ngTemplateOutlet=\"template ?? null; context: { $implicit: true }\"></ng-container>\n</ng-container>\n\n<ng-template #blocked>\n <span\n class=\"permission-gate-blocked\"\n [matTooltip]=\"noAccessLabel\"\n [matTooltipDisabled]=\"!noAccessLabel\">\n <ng-container *ngTemplateOutlet=\"template ?? null; context: { $implicit: false }\"></ng-container>\n </span>\n</ng-template>\n","import { Directive, ElementRef, Input, OnChanges, Renderer2 } from '@angular/core'\n\n/**\n * Lightweight, dependency-free variant of {@link PermissionGateComponent} for\n * **native** elements (e.g. `<button>`, `<a>`). When blocked it sets the\n * `disabled` attribute, a `.permission-blocked` class and a native `title`\n * tooltip — the element stays in the DOM, never hidden.\n *\n * For Angular Material controls (`mat-button`, etc.) prefer the component, since\n * Material reads its own `disabled` @Input rather than the DOM attribute.\n *\n * @example\n * ```html\n * <button [apipassPermissionGate]=\"canCreate\" noAccessLabel=\"No permission\" (click)=\"create()\">New</button>\n * ```\n */\n@Directive({\n selector: '[apipassPermissionGate]'\n})\nexport class PermissionGateDirective implements OnChanges {\n\n /** Whether the gated action is allowed. */\n @Input('apipassPermissionGate') isAllowed = false\n\n /** Native tooltip text shown when the action is blocked. */\n @Input() noAccessLabel = ''\n\n constructor(\n private readonly el: ElementRef<HTMLElement>,\n private readonly renderer: Renderer2\n ) {}\n\n ngOnChanges(): void {\n const node = this.el.nativeElement\n if (this.isAllowed) {\n this.renderer.removeAttribute(node, 'disabled')\n this.renderer.removeAttribute(node, 'title')\n this.renderer.removeClass(node, 'permission-blocked')\n return\n }\n this.renderer.setAttribute(node, 'disabled', 'true')\n this.renderer.addClass(node, 'permission-blocked')\n if (this.noAccessLabel) {\n this.renderer.setAttribute(node, 'title', this.noAccessLabel)\n } else {\n this.renderer.removeAttribute(node, 'title')\n }\n }\n\n}\n","import { InjectionToken, Provider } from '@angular/core'\nimport { UserInfo } from '@apipass/frontend-utils'\n\nexport type MaybeAsync<T> = T | Promise<T>\n\n/** A single `{ kind, action }` pair handed to the legacy resolver. */\nexport interface LegacyCheck {\n kind: string\n action: string\n}\n\n/**\n * Per-application configuration for {@link PermissionsService}.\n *\n * The library is decoupled from any concrete app: each consumer wires in how to\n * read the Cerbos toggle, how to build the Cerbos principal, and how to resolve\n * the legacy (pre-Cerbos) decision.\n */\nexport interface PermissionsConfig {\n /** Whether Cerbos authorization is active. When `false`, only `legacyResolver` runs. */\n cerbosEnabled(): MaybeAsync<boolean>\n /** Builds the Cerbos principal (`{ accountId, domain, roles }`). Only called when Cerbos is enabled. */\n userInfo(): MaybeAsync<UserInfo>\n /**\n * Resolves a single check using the legacy role model. Only called when Cerbos is disabled.\n * `context` is the `legacyContext` carried by the originating {@link PermissionCheck}.\n */\n legacyResolver(check: LegacyCheck, context?: unknown): MaybeAsync<boolean>\n}\n\nexport const PERMISSIONS_CONFIG = new InjectionToken<PermissionsConfig>('PERMISSIONS_CONFIG')\n\n/** Registers the {@link PermissionsConfig} used by {@link PermissionsService}. */\nexport function providePermissionsConfig(config: PermissionsConfig): Provider {\n return { provide: PERMISSIONS_CONFIG, useValue: config }\n}\n","import { Inject, Injectable, Optional } from '@angular/core'\nimport { CERBOS_SERVICE, ICerbosService } from '@apipass/frontend-utils'\nimport { PERMISSIONS_CONFIG, PermissionsConfig } from './permissions.config'\nimport { PermissionCheck } from '../model/permission-check.interface'\nimport { PermissionResult, PermissionResultMap } from '../model/permission-result.interface'\n\ntype DecisionMap = Record<string, Record<string, boolean>>\n\n/**\n * Hybrid authorization orchestrator: decides between Cerbos and the legacy role\n * model based on {@link PermissionsConfig.cerbosEnabled}.\n *\n * - **Cerbos disabled** → behavior is identical to the pre-Cerbos system (legacy resolver).\n * - **Cerbos enabled** → asks the PDP; on any failure it is **fail-closed** (every action denied),\n * so a PDP outage never silently grants access.\n */\n@Injectable()\nexport class PermissionsService {\n\n constructor(\n @Inject(PERMISSIONS_CONFIG) private readonly config: PermissionsConfig,\n @Optional() @Inject(CERBOS_SERVICE) private readonly cerbos: ICerbosService | null\n ) {}\n\n /** Evaluates several resource kinds at once. With Cerbos enabled this is a single round-trip. */\n async checkResources(checks: PermissionCheck[]): Promise<PermissionResultMap> {\n const cerbosEnabled = await this.config.cerbosEnabled()\n const decisions = cerbosEnabled\n ? await this.resolveWithCerbos(checks)\n : await this.resolveWithLegacy(checks)\n return this.buildResultMap(decisions)\n }\n\n /** Convenience for the common single-kind case. */\n async checkResource(check: PermissionCheck): Promise<PermissionResult> {\n const map = await this.checkResources([check])\n return map.for(check.kind)\n }\n\n private async resolveWithCerbos(checks: PermissionCheck[]): Promise<DecisionMap> {\n // Fail-closed: no PDP wired in means no grants.\n if (!this.cerbos) {\n console.error(`${PermissionsService.name} - CERBOS_SERVICE not provided; denying all (fail-closed)`)\n return this.denyAll(checks)\n }\n try {\n const userInfo = await this.config.userInfo()\n return await this.cerbos.checkResources(\n userInfo,\n checks.map(({ kind, actions, id }) => ({ kind, actions, id }))\n )\n } catch (e) {\n console.error(`${PermissionsService.name} - Cerbos check failed; denying all (fail-closed)`, e)\n return this.denyAll(checks)\n }\n }\n\n private async resolveWithLegacy(checks: PermissionCheck[]): Promise<DecisionMap> {\n const decisions: DecisionMap = {}\n for (const check of checks) {\n const actions: Record<string, boolean> = {}\n for (const action of check.actions) {\n actions[action] = await this.config.legacyResolver({ kind: check.kind, action }, check.legacyContext)\n }\n decisions[check.kind] = actions\n }\n return decisions\n }\n\n private denyAll(checks: PermissionCheck[]): DecisionMap {\n return checks.reduce<DecisionMap>((acc, check) => {\n acc[check.kind] = check.actions.reduce<Record<string, boolean>>((actions, action) => {\n actions[action] = false\n return actions\n }, {})\n return acc\n }, {})\n }\n\n private buildResultMap(decisions: DecisionMap): PermissionResultMap {\n const can = (kind: string, action: string): boolean => decisions[kind]?.[action] ?? false\n return {\n can,\n for: (kind: string): PermissionResult => ({ can: (action: string) => can(kind, action) })\n }\n }\n\n}\n","import { NgModule } from '@angular/core'\nimport { CommonModule } from '@angular/common'\nimport { MatTooltipModule } from '@angular/material/tooltip'\nimport { PermissionGateComponent } from './permission-gate/permission-gate.component'\nimport { PermissionGateDirective } from './permission-gate/permission-gate.directive'\nimport { PermissionsService } from './service/permissions.service'\n\n@NgModule({\n imports: [\n CommonModule,\n MatTooltipModule\n ],\n declarations: [\n PermissionGateComponent,\n PermissionGateDirective\n ],\n exports: [\n PermissionGateComponent,\n PermissionGateDirective\n ],\n providers: [\n PermissionsService\n ]\n})\nexport class PermissionsModule {}\n","/**\n * Generated bundle index. Do not edit.\n */\n\nexport * from './public-api';\n"],"names":[],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"apipass-permissions.mjs","sources":["../../../projects/permissions/src/permission-gate/permission-gate.component.ts","../../../projects/permissions/src/permission-gate/permission-gate.component.html","../../../projects/permissions/src/permission-gate/permission-gate.directive.ts","../../../projects/permissions/src/permission-gate/permission-gate-structural.directive.ts","../../../projects/permissions/src/service/permissions.config.ts","../../../projects/permissions/src/service/permissions.service.ts","../../../projects/permissions/src/permissions.module.ts","../../../projects/permissions/src/service/permissions.providers.ts","../../../projects/permissions/src/apipass-permissions.ts"],"sourcesContent":["import { Component, ContentChild, Input, TemplateRef } from '@angular/core'\n\n/** Context object passed to the projected template — `$implicit` is the `allowed` flag. */\nexport interface PermissionGateContext {\n $implicit: boolean\n}\n\n/**\n * Angular equivalent of the React `PermissionGate`.\n *\n * It **never hides** the protected action: when blocked it still renders the\n * content, disabled, wrapped in a tooltip that explains why. The decision\n * (`isAllowed`) is supplied by the caller — this component only presents it.\n *\n * @example\n * ```html\n * <apipass-permission-gate [isAllowed]=\"canCreate\" [noAccessLabel]=\"'PERMISSIONS.NO_CREATE' | translate\">\n * <ng-template let-allowed>\n * <button mat-raised-button [disabled]=\"!allowed\" (click)=\"allowed && create()\">New</button>\n * </ng-template>\n * </apipass-permission-gate>\n * ```\n */\n@Component({\n selector: 'apipass-permission-gate',\n templateUrl: './permission-gate.component.html',\n styleUrls: ['./permission-gate.component.scss']\n})\nexport class PermissionGateComponent {\n\n /** Whether the gated action is allowed. */\n @Input() isAllowed = false\n\n /** Tooltip text shown when the action is blocked. */\n @Input() noAccessLabel = ''\n\n @ContentChild(TemplateRef) template?: TemplateRef<PermissionGateContext>\n\n}\n","<ng-container *ngIf=\"isAllowed; else blocked\">\n <ng-container *ngTemplateOutlet=\"template ?? null; context: { $implicit: true }\"></ng-container>\n</ng-container>\n\n<ng-template #blocked>\n <span\n class=\"permission-gate-blocked\"\n [matTooltip]=\"noAccessLabel\"\n [matTooltipDisabled]=\"!noAccessLabel\">\n <ng-container *ngTemplateOutlet=\"template ?? null; context: { $implicit: false }\"></ng-container>\n </span>\n</ng-template>\n","import { Directive, ElementRef, Input, OnChanges, Renderer2 } from '@angular/core'\n\n/**\n * Lightweight, dependency-free variant of {@link PermissionGateComponent} for\n * **native** elements (e.g. `<button>`, `<a>`). When blocked it sets the\n * `disabled` attribute, a `.permission-blocked` class and a native `title`\n * tooltip — the element stays in the DOM, never hidden.\n *\n * For Angular Material controls (`mat-button`, etc.) prefer the component, since\n * Material reads its own `disabled` @Input rather than the DOM attribute.\n *\n * @example\n * ```html\n * <button [apipassPermissionGate]=\"canCreate\" noAccessLabel=\"No permission\" (click)=\"create()\">New</button>\n * ```\n */\n@Directive({\n selector: '[apipassPermissionGate]'\n})\nexport class PermissionGateDirective implements OnChanges {\n\n /** Whether the gated action is allowed. */\n @Input('apipassPermissionGate') isAllowed = false\n\n /** Native tooltip text shown when the action is blocked. */\n @Input() noAccessLabel = ''\n\n constructor(\n private readonly el: ElementRef<HTMLElement>,\n private readonly renderer: Renderer2\n ) {}\n\n ngOnChanges(): void {\n const node = this.el.nativeElement\n if (this.isAllowed) {\n this.renderer.removeAttribute(node, 'disabled')\n this.renderer.removeAttribute(node, 'title')\n this.renderer.removeClass(node, 'permission-blocked')\n return\n }\n this.renderer.setAttribute(node, 'disabled', 'true')\n this.renderer.addClass(node, 'permission-blocked')\n if (this.noAccessLabel) {\n this.renderer.setAttribute(node, 'title', this.noAccessLabel)\n } else {\n this.renderer.removeAttribute(node, 'title')\n }\n }\n\n}\n","import { Directive, HostBinding, HostListener, Input } from '@angular/core'\n\n/**\n * Gates an action element by user permission — single, unified API.\n *\n * IMPORTANT: this is UX gating only. The real authorization boundary is the\n * backend Cerbos PEP (fail-closed). Never rely on this directive for security.\n *\n * Works on:\n * - native `<button>` and `mat-menu-item` — disabled via the native `disabled`\n * property (Material grays them out automatically);\n * - apipass buttons (`secondary-button`, `primary-button`, `tertiary-button`)\n * that read the `aria-disabled` attribute set here and disable themselves;\n * - any other element — `.apipass-gate-blocked` class is exposed as a CSS hook.\n */\n@Directive({ selector: '[apipassGate]' })\nexport class ApipassGateDirective {\n\n @Input('apipassGate') isAllowed = true\n @Input('apipassGateLabel') label = ''\n\n private get blocked(): boolean { return !this.isAllowed }\n\n // Native disabled — <button>, mat-menu-item gray out on their own.\n @HostBinding('disabled')\n get _disabled(): true | null { return this.blocked || null }\n\n // Accessibility + the signal apipass buttons read to disable themselves.\n @HostBinding('attr.aria-disabled')\n get _ariaDisabled(): true | null { return this.blocked || null }\n\n // CSS hook for elements that are neither native buttons nor apipass buttons.\n @HostBinding('class.apipass-gate-blocked')\n get _blockedClass(): boolean { return this.blocked }\n\n @HostBinding('attr.title')\n get _title(): string | null { return this.blocked && this.label ? this.label : null }\n\n @HostBinding('style.cursor')\n get _cursor(): string | null { return this.blocked ? 'not-allowed' : null }\n\n // Keeps pointer-events active so the native title tooltip fires on hover\n // even when the element is disabled (Material sets pointer-events:none).\n @HostBinding('style.pointer-events')\n get _pointerEvents(): string | null { return this.blocked ? 'auto' : null }\n\n // Belt-and-suspenders: block the action even if the host still emits an\n // event. Covers mouse click and keyboard activation (Enter / Space).\n @HostListener('click', ['$event'])\n @HostListener('keydown.enter', ['$event'])\n @HostListener('keydown.space', ['$event'])\n block(e: Event): void {\n if (this.blocked) {\n e.preventDefault()\n e.stopImmediatePropagation()\n }\n }\n\n}\n","import { InjectionToken, Provider } from '@angular/core'\nimport { UserInfo } from '@apipass/frontend-utils'\n\nexport type MaybeAsync<T> = T | Promise<T>\n\n/** A single `{ kind, action }` pair handed to the legacy resolver. */\nexport interface LegacyCheck {\n kind: string\n action: string\n}\n\n/**\n * Per-application configuration for {@link PermissionsService}.\n *\n * The library is decoupled from any concrete app: each consumer wires in how to\n * read the Cerbos toggle, how to build the Cerbos principal, and how to resolve\n * the legacy (pre-Cerbos) decision.\n */\nexport interface PermissionsConfig {\n /** Whether Cerbos authorization is active. When `false`, only `legacyResolver` runs. */\n cerbosEnabled(): MaybeAsync<boolean>\n /** Builds the Cerbos principal (`{ accountId, domain, roles }`). Only called when Cerbos is enabled. */\n userInfo(): MaybeAsync<UserInfo>\n /**\n * Resolves a single check using the legacy role model. Only called when Cerbos is disabled.\n * `context` is the `legacyContext` carried by the originating {@link PermissionCheck}.\n */\n legacyResolver(check: LegacyCheck, context?: unknown): MaybeAsync<boolean>\n}\n\nexport const PERMISSIONS_CONFIG = new InjectionToken<PermissionsConfig>('PERMISSIONS_CONFIG')\n\n/** Registers the {@link PermissionsConfig} used by {@link PermissionsService}. */\nexport function providePermissionsConfig(config: PermissionsConfig): Provider {\n return { provide: PERMISSIONS_CONFIG, useValue: config }\n}\n","import { Inject, Injectable, Optional } from '@angular/core'\nimport { CERBOS_SERVICE, ICerbosService } from '@apipass/frontend-utils'\nimport { PERMISSIONS_CONFIG, PermissionsConfig } from './permissions.config'\nimport { PermissionCheck } from '../model/permission-check.interface'\nimport { PermissionResult, PermissionResultMap } from '../model/permission-result.interface'\n\ntype DecisionMap = Record<string, Record<string, boolean>>\n\n/**\n * Hybrid authorization orchestrator: decides between Cerbos and the legacy role\n * model based on {@link PermissionsConfig.cerbosEnabled}.\n *\n * - **Cerbos disabled** → behavior is identical to the pre-Cerbos system (legacy resolver).\n * - **Cerbos enabled** → asks the PDP; on any failure it is **fail-closed** (every action denied),\n * so a PDP outage never silently grants access.\n */\n@Injectable()\nexport class PermissionsService {\n\n constructor(\n @Inject(PERMISSIONS_CONFIG) private readonly config: PermissionsConfig,\n @Optional() @Inject(CERBOS_SERVICE) private readonly cerbos: ICerbosService | null\n ) {}\n\n /** Evaluates several resource kinds at once. With Cerbos enabled this is a single round-trip. */\n async checkResources(checks: PermissionCheck[]): Promise<PermissionResultMap> {\n const cerbosEnabled = await this.config.cerbosEnabled()\n const decisions = cerbosEnabled\n ? await this.resolveWithCerbos(checks)\n : await this.resolveWithLegacy(checks)\n return this.buildResultMap(decisions)\n }\n\n /** Convenience for the common single-kind case. */\n async checkResource(check: PermissionCheck): Promise<PermissionResult> {\n const map = await this.checkResources([check])\n return map.for(check.kind)\n }\n\n private async resolveWithCerbos(checks: PermissionCheck[]): Promise<DecisionMap> {\n // Fail-closed: no PDP wired in means no grants.\n if (!this.cerbos) {\n console.error(`${PermissionsService.name} - CERBOS_SERVICE not provided; denying all (fail-closed)`)\n return this.denyAll(checks)\n }\n try {\n const userInfo = await this.config.userInfo()\n return await this.cerbos.checkResources(\n userInfo,\n checks.map(({ kind, actions, id }) => ({ kind, actions, id }))\n )\n } catch (e) {\n console.error(`${PermissionsService.name} - Cerbos check failed; denying all (fail-closed)`, e)\n return this.denyAll(checks)\n }\n }\n\n private async resolveWithLegacy(checks: PermissionCheck[]): Promise<DecisionMap> {\n const decisions: DecisionMap = {}\n for (const check of checks) {\n const actions: Record<string, boolean> = {}\n for (const action of check.actions) {\n actions[action] = await this.config.legacyResolver({ kind: check.kind, action }, check.legacyContext)\n }\n decisions[check.kind] = actions\n }\n return decisions\n }\n\n private denyAll(checks: PermissionCheck[]): DecisionMap {\n return checks.reduce<DecisionMap>((acc, check) => {\n acc[check.kind] = check.actions.reduce<Record<string, boolean>>((actions, action) => {\n actions[action] = false\n return actions\n }, {})\n return acc\n }, {})\n }\n\n private buildResultMap(decisions: DecisionMap): PermissionResultMap {\n const can = (kind: string, action: string): boolean => decisions[kind]?.[action] ?? false\n return {\n can,\n for: (kind: string): PermissionResult => ({ can: (action: string) => can(kind, action) })\n }\n }\n\n}\n","import { NgModule } from '@angular/core'\nimport { CommonModule } from '@angular/common'\nimport { MatTooltipModule } from '@angular/material/tooltip'\nimport { PermissionGateComponent } from './permission-gate/permission-gate.component'\nimport { PermissionGateDirective } from './permission-gate/permission-gate.directive'\nimport { ApipassGateDirective } from './permission-gate/permission-gate-structural.directive'\nimport { PermissionsService } from './service/permissions.service'\n\n@NgModule({\n imports: [\n CommonModule,\n MatTooltipModule\n ],\n declarations: [\n PermissionGateComponent,\n PermissionGateDirective,\n ApipassGateDirective\n ],\n exports: [\n PermissionGateComponent,\n PermissionGateDirective,\n ApipassGateDirective\n ],\n providers: [\n PermissionsService\n ]\n})\nexport class PermissionsModule {}\n","import { Provider } from '@angular/core'\nimport { provideCerbos } from '@apipass/frontend-utils'\nimport { PERMISSIONS_CONFIG, PermissionsConfig } from './permissions.config'\nimport { PermissionsService } from './permissions.service'\n\nexport interface ProvidePermissionsOptions {\n /** Static config (registered with `useValue`). Mutually exclusive with `configFactory`. */\n config?: PermissionsConfig\n /** Config factory with DI (registered with `useFactory`). Mutually exclusive with `config`. */\n configFactory?: (...deps: never[]) => PermissionsConfig\n /** Injection tokens passed to `configFactory`, in parameter order. */\n deps?: unknown[]\n /** Optional Cerbos PDP url; when set, also registers `CERBOS_SERVICE` via `provideCerbos`. */\n cerbosUrl?: string\n}\n\n/**\n * One-call setup for `@apipass/permissions`: registers the {@link PERMISSIONS_CONFIG},\n * the {@link PermissionsService} and (optionally) the Cerbos service.\n *\n * @example\n * ```ts\n * @NgModule({\n * providers: [\n * providePermissions({\n * cerbosUrl: $ENV.CERBOS_PDP_URL,\n * configFactory: permissionsConfigFactory,\n * deps: [FEATURE_TOGGLE_SERVICE, IAM_SERVICE]\n * })\n * ]\n * })\n * ```\n */\nexport function providePermissions(options: ProvidePermissionsOptions): Provider[] {\n const { config, configFactory, deps, cerbosUrl } = options\n\n if ((config && configFactory) || (!config && !configFactory)) {\n throw new Error('providePermissions: provide exactly one of `config` or `configFactory`')\n }\n\n const providers: Provider[] = [\n config\n ? { provide: PERMISSIONS_CONFIG, useValue: config }\n : { provide: PERMISSIONS_CONFIG, useFactory: configFactory, deps: deps ?? [] },\n PermissionsService\n ]\n\n if (cerbosUrl) {\n providers.push(provideCerbos(cerbosUrl))\n }\n\n return providers\n}\n","/**\n * Generated bundle index. Do not edit.\n */\n\nexport * from './public-api';\n"],"names":[],"mappings":"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"}
|
package/package.json
CHANGED
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
import * as i0 from "@angular/core";
|
|
2
|
+
/**
|
|
3
|
+
* Gates an action element by user permission — single, unified API.
|
|
4
|
+
*
|
|
5
|
+
* IMPORTANT: this is UX gating only. The real authorization boundary is the
|
|
6
|
+
* backend Cerbos PEP (fail-closed). Never rely on this directive for security.
|
|
7
|
+
*
|
|
8
|
+
* Works on:
|
|
9
|
+
* - native `<button>` and `mat-menu-item` — disabled via the native `disabled`
|
|
10
|
+
* property (Material grays them out automatically);
|
|
11
|
+
* - apipass buttons (`secondary-button`, `primary-button`, `tertiary-button`)
|
|
12
|
+
* that read the `aria-disabled` attribute set here and disable themselves;
|
|
13
|
+
* - any other element — `.apipass-gate-blocked` class is exposed as a CSS hook.
|
|
14
|
+
*/
|
|
15
|
+
export declare class ApipassGateDirective {
|
|
16
|
+
isAllowed: boolean;
|
|
17
|
+
label: string;
|
|
18
|
+
private get blocked();
|
|
19
|
+
get _disabled(): true | null;
|
|
20
|
+
get _ariaDisabled(): true | null;
|
|
21
|
+
get _blockedClass(): boolean;
|
|
22
|
+
get _title(): string | null;
|
|
23
|
+
get _cursor(): string | null;
|
|
24
|
+
get _pointerEvents(): string | null;
|
|
25
|
+
block(e: Event): void;
|
|
26
|
+
static ɵfac: i0.ɵɵFactoryDeclaration<ApipassGateDirective, never>;
|
|
27
|
+
static ɵdir: i0.ɵɵDirectiveDeclaration<ApipassGateDirective, "[apipassGate]", never, { "isAllowed": { "alias": "apipassGate"; "required": false; }; "label": { "alias": "apipassGateLabel"; "required": false; }; }, {}, never, never, false, never>;
|
|
28
|
+
}
|
package/permissions.module.d.ts
CHANGED
|
@@ -1,10 +1,11 @@
|
|
|
1
1
|
import * as i0 from "@angular/core";
|
|
2
2
|
import * as i1 from "./permission-gate/permission-gate.component";
|
|
3
3
|
import * as i2 from "./permission-gate/permission-gate.directive";
|
|
4
|
-
import * as i3 from "
|
|
5
|
-
import * as i4 from "@angular/
|
|
4
|
+
import * as i3 from "./permission-gate/permission-gate-structural.directive";
|
|
5
|
+
import * as i4 from "@angular/common";
|
|
6
|
+
import * as i5 from "@angular/material/tooltip";
|
|
6
7
|
export declare class PermissionsModule {
|
|
7
8
|
static ɵfac: i0.ɵɵFactoryDeclaration<PermissionsModule, never>;
|
|
8
|
-
static ɵmod: i0.ɵɵNgModuleDeclaration<PermissionsModule, [typeof i1.PermissionGateComponent, typeof i2.PermissionGateDirective], [typeof
|
|
9
|
+
static ɵmod: i0.ɵɵNgModuleDeclaration<PermissionsModule, [typeof i1.PermissionGateComponent, typeof i2.PermissionGateDirective, typeof i3.ApipassGateDirective], [typeof i4.CommonModule, typeof i5.MatTooltipModule], [typeof i1.PermissionGateComponent, typeof i2.PermissionGateDirective, typeof i3.ApipassGateDirective]>;
|
|
9
10
|
static ɵinj: i0.ɵɵInjectorDeclaration<PermissionsModule>;
|
|
10
11
|
}
|
package/public-api.d.ts
CHANGED
|
@@ -1,7 +1,9 @@
|
|
|
1
1
|
export * from './permissions.module';
|
|
2
2
|
export * from './permission-gate/permission-gate.component';
|
|
3
3
|
export * from './permission-gate/permission-gate.directive';
|
|
4
|
+
export * from './permission-gate/permission-gate-structural.directive';
|
|
4
5
|
export * from './service/permissions.service';
|
|
5
6
|
export * from './service/permissions.config';
|
|
7
|
+
export * from './service/permissions.providers';
|
|
6
8
|
export * from './model/permission-check.interface';
|
|
7
9
|
export * from './model/permission-result.interface';
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
import { Provider } from '@angular/core';
|
|
2
|
+
import { PermissionsConfig } from './permissions.config';
|
|
3
|
+
export interface ProvidePermissionsOptions {
|
|
4
|
+
/** Static config (registered with `useValue`). Mutually exclusive with `configFactory`. */
|
|
5
|
+
config?: PermissionsConfig;
|
|
6
|
+
/** Config factory with DI (registered with `useFactory`). Mutually exclusive with `config`. */
|
|
7
|
+
configFactory?: (...deps: never[]) => PermissionsConfig;
|
|
8
|
+
/** Injection tokens passed to `configFactory`, in parameter order. */
|
|
9
|
+
deps?: unknown[];
|
|
10
|
+
/** Optional Cerbos PDP url; when set, also registers `CERBOS_SERVICE` via `provideCerbos`. */
|
|
11
|
+
cerbosUrl?: string;
|
|
12
|
+
}
|
|
13
|
+
/**
|
|
14
|
+
* One-call setup for `@apipass/permissions`: registers the {@link PERMISSIONS_CONFIG},
|
|
15
|
+
* the {@link PermissionsService} and (optionally) the Cerbos service.
|
|
16
|
+
*
|
|
17
|
+
* @example
|
|
18
|
+
* ```ts
|
|
19
|
+
* @NgModule({
|
|
20
|
+
* providers: [
|
|
21
|
+
* providePermissions({
|
|
22
|
+
* cerbosUrl: $ENV.CERBOS_PDP_URL,
|
|
23
|
+
* configFactory: permissionsConfigFactory,
|
|
24
|
+
* deps: [FEATURE_TOGGLE_SERVICE, IAM_SERVICE]
|
|
25
|
+
* })
|
|
26
|
+
* ]
|
|
27
|
+
* })
|
|
28
|
+
* ```
|
|
29
|
+
*/
|
|
30
|
+
export declare function providePermissions(options: ProvidePermissionsOptions): Provider[];
|