@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 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
- import { PermissionsModule, providePermissionsConfig } from '@apipass/permissions'
29
- import { provideCerbos } from '@apipass/frontend-utils'
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
- provideCerbos('https://cerbos.example.com'),
35
- providePermissionsConfig({
36
- cerbosEnabled: () => featureToggle.getFeatures().then(f => f.isCerbosEnabled()),
37
- userInfo: () => iamService.getLoggedUserInfo().then(u => u.cerbosUserInfo),
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
- `HttpClient` must be available (e.g. via `provideHttpClient()`), as required by `provideCerbos`.
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
- | `PERMISSIONS_CONFIG` / `providePermissionsConfig` | token / fn | Per-app wiring |
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], imports: [CommonModule,
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,eyJ2ZXJzaW9uIjozLCJmaWxlIjoicGVybWlzc2lvbnMubW9kdWxlLmpzIiwic291cmNlUm9vdCI6IiIsInNvdXJjZXMiOlsiLi4vLi4vLi4vcHJvamVjdHMvcGVybWlzc2lvbnMvc3JjL3Blcm1pc3Npb25zLm1vZHVsZS50cyJdLCJuYW1lcyI6W10sIm1hcHBpbmdzIjoiQUFBQSxPQUFPLEVBQUUsUUFBUSxFQUFFLE1BQU0sZUFBZSxDQUFBO0FBQ3hDLE9BQU8sRUFBRSxZQUFZLEVBQUUsTUFBTSxpQkFBaUIsQ0FBQTtBQUM5QyxPQUFPLEVBQUUsZ0JBQWdCLEVBQUUsTUFBTSwyQkFBMkIsQ0FBQTtBQUM1RCxPQUFPLEVBQUUsdUJBQXVCLEVBQUUsTUFBTSw2Q0FBNkMsQ0FBQTtBQUNyRixPQUFPLEVBQUUsdUJBQXVCLEVBQUUsTUFBTSw2Q0FBNkMsQ0FBQTtBQUNyRixPQUFPLEVBQUUsa0JBQWtCLEVBQUUsTUFBTSwrQkFBK0IsQ0FBQTs7QUFFbEUsTUFpQmEsaUJBQWlCO3VHQUFqQixpQkFBaUI7d0dBQWpCLGlCQUFpQixpQkFYMUIsdUJBQXVCO1lBQ3ZCLHVCQUF1QixhQUx2QixZQUFZO1lBQ1osZ0JBQWdCLGFBT2hCLHVCQUF1QjtZQUN2Qix1QkFBdUI7d0dBTWQsaUJBQWlCLGFBSmpCO1lBQ1Qsa0JBQWtCO1NBQ25CLFlBYkMsWUFBWTtZQUNaLGdCQUFnQjs7U0FjUCxpQkFBaUI7MkZBQWpCLGlCQUFpQjtrQkFqQjdCLFFBQVE7bUJBQUM7b0JBQ1IsT0FBTyxFQUFFO3dCQUNQLFlBQVk7d0JBQ1osZ0JBQWdCO3FCQUNqQjtvQkFDRCxZQUFZLEVBQUU7d0JBQ1osdUJBQXVCO3dCQUN2Qix1QkFBdUI7cUJBQ3hCO29CQUNELE9BQU8sRUFBRTt3QkFDUCx1QkFBdUI7d0JBQ3ZCLHVCQUF1QjtxQkFDeEI7b0JBQ0QsU0FBUyxFQUFFO3dCQUNULGtCQUFrQjtxQkFDbkI7aUJBQ0YiLCJzb3VyY2VzQ29udGVudCI6WyJpbXBvcnQgeyBOZ01vZHVsZSB9IGZyb20gJ0Bhbmd1bGFyL2NvcmUnXG5pbXBvcnQgeyBDb21tb25Nb2R1bGUgfSBmcm9tICdAYW5ndWxhci9jb21tb24nXG5pbXBvcnQgeyBNYXRUb29sdGlwTW9kdWxlIH0gZnJvbSAnQGFuZ3VsYXIvbWF0ZXJpYWwvdG9vbHRpcCdcbmltcG9ydCB7IFBlcm1pc3Npb25HYXRlQ29tcG9uZW50IH0gZnJvbSAnLi9wZXJtaXNzaW9uLWdhdGUvcGVybWlzc2lvbi1nYXRlLmNvbXBvbmVudCdcbmltcG9ydCB7IFBlcm1pc3Npb25HYXRlRGlyZWN0aXZlIH0gZnJvbSAnLi9wZXJtaXNzaW9uLWdhdGUvcGVybWlzc2lvbi1nYXRlLmRpcmVjdGl2ZSdcbmltcG9ydCB7IFBlcm1pc3Npb25zU2VydmljZSB9IGZyb20gJy4vc2VydmljZS9wZXJtaXNzaW9ucy5zZXJ2aWNlJ1xuXG5ATmdNb2R1bGUoe1xuICBpbXBvcnRzOiBbXG4gICAgQ29tbW9uTW9kdWxlLFxuICAgIE1hdFRvb2x0aXBNb2R1bGVcbiAgXSxcbiAgZGVjbGFyYXRpb25zOiBbXG4gICAgUGVybWlzc2lvbkdhdGVDb21wb25lbnQsXG4gICAgUGVybWlzc2lvbkdhdGVEaXJlY3RpdmVcbiAgXSxcbiAgZXhwb3J0czogW1xuICAgIFBlcm1pc3Npb25HYXRlQ29tcG9uZW50LFxuICAgIFBlcm1pc3Npb25HYXRlRGlyZWN0aXZlXG4gIF0sXG4gIHByb3ZpZGVyczogW1xuICAgIFBlcm1pc3Npb25zU2VydmljZVxuICBdXG59KVxuZXhwb3J0IGNsYXNzIFBlcm1pc3Npb25zTW9kdWxlIHt9XG4iXX0=
45
+ //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoicGVybWlzc2lvbnMubW9kdWxlLmpzIiwic291cmNlUm9vdCI6IiIsInNvdXJjZXMiOlsiLi4vLi4vLi4vcHJvamVjdHMvcGVybWlzc2lvbnMvc3JjL3Blcm1pc3Npb25zLm1vZHVsZS50cyJdLCJuYW1lcyI6W10sIm1hcHBpbmdzIjoiQUFBQSxPQUFPLEVBQUUsUUFBUSxFQUFFLE1BQU0sZUFBZSxDQUFBO0FBQ3hDLE9BQU8sRUFBRSxZQUFZLEVBQUUsTUFBTSxpQkFBaUIsQ0FBQTtBQUM5QyxPQUFPLEVBQUUsZ0JBQWdCLEVBQUUsTUFBTSwyQkFBMkIsQ0FBQTtBQUM1RCxPQUFPLEVBQUUsdUJBQXVCLEVBQUUsTUFBTSw2Q0FBNkMsQ0FBQTtBQUNyRixPQUFPLEVBQUUsdUJBQXVCLEVBQUUsTUFBTSw2Q0FBNkMsQ0FBQTtBQUNyRixPQUFPLEVBQUUsb0JBQW9CLEVBQUUsTUFBTSx3REFBd0QsQ0FBQTtBQUM3RixPQUFPLEVBQUUsa0JBQWtCLEVBQUUsTUFBTSwrQkFBK0IsQ0FBQTs7QUFFbEUsTUFtQmEsaUJBQWlCO3VHQUFqQixpQkFBaUI7d0dBQWpCLGlCQUFpQixpQkFiMUIsdUJBQXVCO1lBQ3ZCLHVCQUF1QjtZQUN2QixvQkFBb0IsYUFOcEIsWUFBWTtZQUNaLGdCQUFnQixhQVFoQix1QkFBdUI7WUFDdkIsdUJBQXVCO1lBQ3ZCLG9CQUFvQjt3R0FNWCxpQkFBaUIsYUFKakI7WUFDVCxrQkFBa0I7U0FDbkIsWUFmQyxZQUFZO1lBQ1osZ0JBQWdCOztTQWdCUCxpQkFBaUI7MkZBQWpCLGlCQUFpQjtrQkFuQjdCLFFBQVE7bUJBQUM7b0JBQ1IsT0FBTyxFQUFFO3dCQUNQLFlBQVk7d0JBQ1osZ0JBQWdCO3FCQUNqQjtvQkFDRCxZQUFZLEVBQUU7d0JBQ1osdUJBQXVCO3dCQUN2Qix1QkFBdUI7d0JBQ3ZCLG9CQUFvQjtxQkFDckI7b0JBQ0QsT0FBTyxFQUFFO3dCQUNQLHVCQUF1Qjt3QkFDdkIsdUJBQXVCO3dCQUN2QixvQkFBb0I7cUJBQ3JCO29CQUNELFNBQVMsRUFBRTt3QkFDVCxrQkFBa0I7cUJBQ25CO2lCQUNGIiwic291cmNlc0NvbnRlbnQiOlsiaW1wb3J0IHsgTmdNb2R1bGUgfSBmcm9tICdAYW5ndWxhci9jb3JlJ1xuaW1wb3J0IHsgQ29tbW9uTW9kdWxlIH0gZnJvbSAnQGFuZ3VsYXIvY29tbW9uJ1xuaW1wb3J0IHsgTWF0VG9vbHRpcE1vZHVsZSB9IGZyb20gJ0Bhbmd1bGFyL21hdGVyaWFsL3Rvb2x0aXAnXG5pbXBvcnQgeyBQZXJtaXNzaW9uR2F0ZUNvbXBvbmVudCB9IGZyb20gJy4vcGVybWlzc2lvbi1nYXRlL3Blcm1pc3Npb24tZ2F0ZS5jb21wb25lbnQnXG5pbXBvcnQgeyBQZXJtaXNzaW9uR2F0ZURpcmVjdGl2ZSB9IGZyb20gJy4vcGVybWlzc2lvbi1nYXRlL3Blcm1pc3Npb24tZ2F0ZS5kaXJlY3RpdmUnXG5pbXBvcnQgeyBBcGlwYXNzR2F0ZURpcmVjdGl2ZSB9IGZyb20gJy4vcGVybWlzc2lvbi1nYXRlL3Blcm1pc3Npb24tZ2F0ZS1zdHJ1Y3R1cmFsLmRpcmVjdGl2ZSdcbmltcG9ydCB7IFBlcm1pc3Npb25zU2VydmljZSB9IGZyb20gJy4vc2VydmljZS9wZXJtaXNzaW9ucy5zZXJ2aWNlJ1xuXG5ATmdNb2R1bGUoe1xuICBpbXBvcnRzOiBbXG4gICAgQ29tbW9uTW9kdWxlLFxuICAgIE1hdFRvb2x0aXBNb2R1bGVcbiAgXSxcbiAgZGVjbGFyYXRpb25zOiBbXG4gICAgUGVybWlzc2lvbkdhdGVDb21wb25lbnQsXG4gICAgUGVybWlzc2lvbkdhdGVEaXJlY3RpdmUsXG4gICAgQXBpcGFzc0dhdGVEaXJlY3RpdmVcbiAgXSxcbiAgZXhwb3J0czogW1xuICAgIFBlcm1pc3Npb25HYXRlQ29tcG9uZW50LFxuICAgIFBlcm1pc3Npb25HYXRlRGlyZWN0aXZlLFxuICAgIEFwaXBhc3NHYXRlRGlyZWN0aXZlXG4gIF0sXG4gIHByb3ZpZGVyczogW1xuICAgIFBlcm1pc3Npb25zU2VydmljZVxuICBdXG59KVxuZXhwb3J0IGNsYXNzIFBlcm1pc3Npb25zTW9kdWxlIHt9XG4iXX0=
@@ -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,eyJ2ZXJzaW9uIjozLCJmaWxlIjoicHVibGljLWFwaS5qcyIsInNvdXJjZVJvb3QiOiIiLCJzb3VyY2VzIjpbIi4uLy4uLy4uL3Byb2plY3RzL3Blcm1pc3Npb25zL3NyYy9wdWJsaWMtYXBpLnRzIl0sIm5hbWVzIjpbXSwibWFwcGluZ3MiOiJBQUFBLGNBQWMsc0JBQXNCLENBQUE7QUFDcEMsY0FBYyw2Q0FBNkMsQ0FBQTtBQUMzRCxjQUFjLDZDQUE2QyxDQUFBO0FBQzNELGNBQWMsK0JBQStCLENBQUE7QUFDN0MsY0FBYyw4QkFBOEIsQ0FBQTtBQUM1QyxjQUFjLG9DQUFvQyxDQUFBO0FBQ2xELGNBQWMscUNBQXFDLENBQUEiLCJzb3VyY2VzQ29udGVudCI6WyJleHBvcnQgKiBmcm9tICcuL3Blcm1pc3Npb25zLm1vZHVsZSdcbmV4cG9ydCAqIGZyb20gJy4vcGVybWlzc2lvbi1nYXRlL3Blcm1pc3Npb24tZ2F0ZS5jb21wb25lbnQnXG5leHBvcnQgKiBmcm9tICcuL3Blcm1pc3Npb24tZ2F0ZS9wZXJtaXNzaW9uLWdhdGUuZGlyZWN0aXZlJ1xuZXhwb3J0ICogZnJvbSAnLi9zZXJ2aWNlL3Blcm1pc3Npb25zLnNlcnZpY2UnXG5leHBvcnQgKiBmcm9tICcuL3NlcnZpY2UvcGVybWlzc2lvbnMuY29uZmlnJ1xuZXhwb3J0ICogZnJvbSAnLi9tb2RlbC9wZXJtaXNzaW9uLWNoZWNrLmludGVyZmFjZSdcbmV4cG9ydCAqIGZyb20gJy4vbW9kZWwvcGVybWlzc2lvbi1yZXN1bHQuaW50ZXJmYWNlJ1xuIl19
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], imports: [CommonModule,
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":";;;;;;;;AAOA;;;;;;;;;;;;;;;AAeG;AACH,MAKa,uBAAuB,CAAA;;IAGzB,SAAS,GAAG,KAAK,CAAA;;IAGjB,aAAa,GAAG,EAAE,CAAA;AAEA,IAAA,QAAQ,CAAqC;uGAR7D,uBAAuB,EAAA,IAAA,EAAA,EAAA,EAAA,MAAA,EAAA,EAAA,CAAA,eAAA,CAAA,SAAA,EAAA,CAAA,CAAA;2FAAvB,uBAAuB,EAAA,QAAA,EAAA,yBAAA,EAAA,MAAA,EAAA,EAAA,SAAA,EAAA,WAAA,EAAA,aAAA,EAAA,eAAA,EAAA,EAAA,OAAA,EAAA,CAAA,EAAA,YAAA,EAAA,UAAA,EAAA,KAAA,EAAA,IAAA,EAAA,SAAA,EAQpB,WAAW,EAAA,WAAA,EAAA,IAAA,EAAA,CAAA,EAAA,QAAA,EAAA,EAAA,EAAA,QAAA,ECpC3B,2cAYA,EAAA,MAAA,EAAA,CAAA,oEAAA,CAAA,EAAA,YAAA,EAAA,CAAA,EAAA,IAAA,EAAA,WAAA,EAAA,IAAA,EAAA,EAAA,CAAA,IAAA,EAAA,QAAA,EAAA,QAAA,EAAA,MAAA,EAAA,CAAA,MAAA,EAAA,UAAA,EAAA,UAAA,CAAA,EAAA,EAAA,EAAA,IAAA,EAAA,WAAA,EAAA,IAAA,EAAA,EAAA,CAAA,gBAAA,EAAA,QAAA,EAAA,oBAAA,EAAA,MAAA,EAAA,CAAA,yBAAA,EAAA,kBAAA,EAAA,0BAAA,CAAA,EAAA,EAAA,EAAA,IAAA,EAAA,WAAA,EAAA,IAAA,EAAA,EAAA,CAAA,UAAA,EAAA,QAAA,EAAA,cAAA,EAAA,QAAA,EAAA,CAAA,YAAA,CAAA,EAAA,CAAA,EAAA,CAAA,CAAA;;2FDgBa,uBAAuB,EAAA,UAAA,EAAA,CAAA;kBALnC,SAAS;+BACE,yBAAyB,EAAA,QAAA,EAAA,2cAAA,EAAA,MAAA,EAAA,CAAA,oEAAA,CAAA,EAAA,CAAA;8BAO1B,SAAS,EAAA,CAAA;sBAAjB,KAAK;gBAGG,aAAa,EAAA,CAAA;sBAArB,KAAK;gBAEqB,QAAQ,EAAA,CAAA;sBAAlC,YAAY;uBAAC,WAAW,CAAA;;;AElC3B;;;;;;;;;;;;;AAaG;AACH,MAGa,uBAAuB,CAAA;AASf,IAAA,EAAA,CAAA;AACA,IAAA,QAAA,CAAA;;IAPa,SAAS,GAAG,KAAK,CAAA;;IAGxC,aAAa,GAAG,EAAE,CAAA;IAE3B,WACmB,CAAA,EAA2B,EAC3B,QAAmB,EAAA;QADnB,IAAE,CAAA,EAAA,GAAF,EAAE,CAAyB;QAC3B,IAAQ,CAAA,QAAA,GAAR,QAAQ,CAAW;KAClC;IAEJ,WAAW,GAAA;AACT,QAAA,MAAM,IAAI,GAAG,IAAI,CAAC,EAAE,CAAC,aAAa,CAAA;QAClC,IAAI,IAAI,CAAC,SAAS,EAAE;YAClB,IAAI,CAAC,QAAQ,CAAC,eAAe,CAAC,IAAI,EAAE,UAAU,CAAC,CAAA;YAC/C,IAAI,CAAC,QAAQ,CAAC,eAAe,CAAC,IAAI,EAAE,OAAO,CAAC,CAAA;YAC5C,IAAI,CAAC,QAAQ,CAAC,WAAW,CAAC,IAAI,EAAE,oBAAoB,CAAC,CAAA;YACrD,OAAM;AACP,SAAA;QACD,IAAI,CAAC,QAAQ,CAAC,YAAY,CAAC,IAAI,EAAE,UAAU,EAAE,MAAM,CAAC,CAAA;QACpD,IAAI,CAAC,QAAQ,CAAC,QAAQ,CAAC,IAAI,EAAE,oBAAoB,CAAC,CAAA;QAClD,IAAI,IAAI,CAAC,aAAa,EAAE;AACtB,YAAA,IAAI,CAAC,QAAQ,CAAC,YAAY,CAAC,IAAI,EAAE,OAAO,EAAE,IAAI,CAAC,aAAa,CAAC,CAAA;AAC9D,SAAA;AAAM,aAAA;YACL,IAAI,CAAC,QAAQ,CAAC,eAAe,CAAC,IAAI,EAAE,OAAO,CAAC,CAAA;AAC7C,SAAA;KACF;uGA5BU,uBAAuB,EAAA,IAAA,EAAA,CAAA,EAAA,KAAA,EAAA,EAAA,CAAA,UAAA,EAAA,EAAA,EAAA,KAAA,EAAA,EAAA,CAAA,SAAA,EAAA,CAAA,EAAA,MAAA,EAAA,EAAA,CAAA,eAAA,CAAA,SAAA,EAAA,CAAA,CAAA;2FAAvB,uBAAuB,EAAA,QAAA,EAAA,yBAAA,EAAA,MAAA,EAAA,EAAA,SAAA,EAAA,CAAA,uBAAA,EAAA,WAAA,CAAA,EAAA,aAAA,EAAA,eAAA,EAAA,EAAA,aAAA,EAAA,IAAA,EAAA,QAAA,EAAA,EAAA,EAAA,CAAA,CAAA;;2FAAvB,uBAAuB,EAAA,UAAA,EAAA,CAAA;kBAHnC,SAAS;AAAC,YAAA,IAAA,EAAA,CAAA;AACT,oBAAA,QAAQ,EAAE,yBAAyB;AACpC,iBAAA,CAAA;yHAIiC,SAAS,EAAA,CAAA;sBAAxC,KAAK;uBAAC,uBAAuB,CAAA;gBAGrB,aAAa,EAAA,CAAA;sBAArB,KAAK;;;MCKK,kBAAkB,GAAG,IAAI,cAAc,CAAoB,oBAAoB,EAAC;AAE7F;AACM,SAAU,wBAAwB,CAAC,MAAyB,EAAA;IAChE,OAAO,EAAE,OAAO,EAAE,kBAAkB,EAAE,QAAQ,EAAE,MAAM,EAAE,CAAA;AAC1D;;AC3BA;;;;;;;AAOG;AACH,MACa,kBAAkB,CAAA;AAGkB,IAAA,MAAA,CAAA;AACQ,IAAA,MAAA,CAAA;IAFvD,WAC+C,CAAA,MAAyB,EACjB,MAA6B,EAAA;QADrC,IAAM,CAAA,MAAA,GAAN,MAAM,CAAmB;QACjB,IAAM,CAAA,MAAA,GAAN,MAAM,CAAuB;KAChF;;IAGJ,MAAM,cAAc,CAAC,MAAyB,EAAA;QAC5C,MAAM,aAAa,GAAG,MAAM,IAAI,CAAC,MAAM,CAAC,aAAa,EAAE,CAAA;QACvD,MAAM,SAAS,GAAG,aAAa;AAC7B,cAAE,MAAM,IAAI,CAAC,iBAAiB,CAAC,MAAM,CAAC;cACpC,MAAM,IAAI,CAAC,iBAAiB,CAAC,MAAM,CAAC,CAAA;AACxC,QAAA,OAAO,IAAI,CAAC,cAAc,CAAC,SAAS,CAAC,CAAA;KACtC;;IAGD,MAAM,aAAa,CAAC,KAAsB,EAAA;QACxC,MAAM,GAAG,GAAG,MAAM,IAAI,CAAC,cAAc,CAAC,CAAC,KAAK,CAAC,CAAC,CAAA;QAC9C,OAAO,GAAG,CAAC,GAAG,CAAC,KAAK,CAAC,IAAI,CAAC,CAAA;KAC3B;IAEO,MAAM,iBAAiB,CAAC,MAAyB,EAAA;;AAEvD,QAAA,IAAI,CAAC,IAAI,CAAC,MAAM,EAAE;YAChB,OAAO,CAAC,KAAK,CAAC,CAAA,EAAG,kBAAkB,CAAC,IAAI,CAA2D,yDAAA,CAAA,CAAC,CAAA;AACpG,YAAA,OAAO,IAAI,CAAC,OAAO,CAAC,MAAM,CAAC,CAAA;AAC5B,SAAA;QACD,IAAI;YACF,MAAM,QAAQ,GAAG,MAAM,IAAI,CAAC,MAAM,CAAC,QAAQ,EAAE,CAAA;AAC7C,YAAA,OAAO,MAAM,IAAI,CAAC,MAAM,CAAC,cAAc,CACrC,QAAQ,EACR,MAAM,CAAC,GAAG,CAAC,CAAC,EAAE,IAAI,EAAE,OAAO,EAAE,EAAE,EAAE,MAAM,EAAE,IAAI,EAAE,OAAO,EAAE,EAAE,EAAE,CAAC,CAAC,CAC/D,CAAA;AACF,SAAA;AAAC,QAAA,OAAO,CAAC,EAAE;YACV,OAAO,CAAC,KAAK,CAAC,CAAG,EAAA,kBAAkB,CAAC,IAAI,CAAmD,iDAAA,CAAA,EAAE,CAAC,CAAC,CAAA;AAC/F,YAAA,OAAO,IAAI,CAAC,OAAO,CAAC,MAAM,CAAC,CAAA;AAC5B,SAAA;KACF;IAEO,MAAM,iBAAiB,CAAC,MAAyB,EAAA;QACvD,MAAM,SAAS,GAAgB,EAAE,CAAA;AACjC,QAAA,KAAK,MAAM,KAAK,IAAI,MAAM,EAAE;YAC1B,MAAM,OAAO,GAA4B,EAAE,CAAA;AAC3C,YAAA,KAAK,MAAM,MAAM,IAAI,KAAK,CAAC,OAAO,EAAE;gBAClC,OAAO,CAAC,MAAM,CAAC,GAAG,MAAM,IAAI,CAAC,MAAM,CAAC,cAAc,CAAC,EAAE,IAAI,EAAE,KAAK,CAAC,IAAI,EAAE,MAAM,EAAE,EAAE,KAAK,CAAC,aAAa,CAAC,CAAA;AACtG,aAAA;AACD,YAAA,SAAS,CAAC,KAAK,CAAC,IAAI,CAAC,GAAG,OAAO,CAAA;AAChC,SAAA;AACD,QAAA,OAAO,SAAS,CAAA;KACjB;AAEO,IAAA,OAAO,CAAC,MAAyB,EAAA;QACvC,OAAO,MAAM,CAAC,MAAM,CAAc,CAAC,GAAG,EAAE,KAAK,KAAI;AAC/C,YAAA,GAAG,CAAC,KAAK,CAAC,IAAI,CAAC,GAAG,KAAK,CAAC,OAAO,CAAC,MAAM,CAA0B,CAAC,OAAO,EAAE,MAAM,KAAI;AAClF,gBAAA,OAAO,CAAC,MAAM,CAAC,GAAG,KAAK,CAAA;AACvB,gBAAA,OAAO,OAAO,CAAA;aACf,EAAE,EAAE,CAAC,CAAA;AACN,YAAA,OAAO,GAAG,CAAA;SACX,EAAE,EAAE,CAAC,CAAA;KACP;AAEO,IAAA,cAAc,CAAC,SAAsB,EAAA;AAC3C,QAAA,MAAM,GAAG,GAAG,CAAC,IAAY,EAAE,MAAc,KAAc,SAAS,CAAC,IAAI,CAAC,GAAG,MAAM,CAAC,IAAI,KAAK,CAAA;QACzF,OAAO;YACL,GAAG;YACH,GAAG,EAAE,CAAC,IAAY,MAAwB,EAAE,GAAG,EAAE,CAAC,MAAc,KAAK,GAAG,CAAC,IAAI,EAAE,MAAM,CAAC,EAAE,CAAC;SAC1F,CAAA;KACF;uGApEU,kBAAkB,EAAA,IAAA,EAAA,CAAA,EAAA,KAAA,EAGnB,kBAAkB,EAAA,EAAA,EAAA,KAAA,EACN,cAAc,EAAA,QAAA,EAAA,IAAA,EAAA,CAAA,EAAA,MAAA,EAAA,EAAA,CAAA,eAAA,CAAA,UAAA,EAAA,CAAA,CAAA;2GAJzB,kBAAkB,EAAA,CAAA,CAAA;;2FAAlB,kBAAkB,EAAA,UAAA,EAAA,CAAA;kBAD9B,UAAU;;0BAIN,MAAM;2BAAC,kBAAkB,CAAA;;0BACzB,QAAQ;;0BAAI,MAAM;2BAAC,cAAc,CAAA;;;ACdtC,MAiBa,iBAAiB,CAAA;uGAAjB,iBAAiB,EAAA,IAAA,EAAA,EAAA,EAAA,MAAA,EAAA,EAAA,CAAA,eAAA,CAAA,QAAA,EAAA,CAAA,CAAA;AAAjB,IAAA,OAAA,IAAA,GAAA,EAAA,CAAA,mBAAA,CAAA,EAAA,UAAA,EAAA,QAAA,EAAA,OAAA,EAAA,QAAA,EAAA,QAAA,EAAA,EAAA,EAAA,IAAA,EAAA,iBAAiB,iBAX1B,uBAAuB;AACvB,YAAA,uBAAuB,aALvB,YAAY;AACZ,YAAA,gBAAgB,aAOhB,uBAAuB;YACvB,uBAAuB,CAAA,EAAA,CAAA,CAAA;AAMd,IAAA,OAAA,IAAA,GAAA,EAAA,CAAA,mBAAA,CAAA,EAAA,UAAA,EAAA,QAAA,EAAA,OAAA,EAAA,QAAA,EAAA,QAAA,EAAA,EAAA,EAAA,IAAA,EAAA,iBAAiB,EAJjB,SAAA,EAAA;YACT,kBAAkB;AACnB,SAAA,EAAA,OAAA,EAAA,CAbC,YAAY;YACZ,gBAAgB,CAAA,EAAA,CAAA,CAAA;;2FAcP,iBAAiB,EAAA,UAAA,EAAA,CAAA;kBAjB7B,QAAQ;AAAC,YAAA,IAAA,EAAA,CAAA;AACR,oBAAA,OAAO,EAAE;wBACP,YAAY;wBACZ,gBAAgB;AACjB,qBAAA;AACD,oBAAA,YAAY,EAAE;wBACZ,uBAAuB;wBACvB,uBAAuB;AACxB,qBAAA;AACD,oBAAA,OAAO,EAAE;wBACP,uBAAuB;wBACvB,uBAAuB;AACxB,qBAAA;AACD,oBAAA,SAAS,EAAE;wBACT,kBAAkB;AACnB,qBAAA;AACF,iBAAA,CAAA;;;ACvBD;;AAEG;;;;"}
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
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@apipass/permissions",
3
- "version": "1.0.192",
3
+ "version": "1.0.194",
4
4
  "peerDependencies": {
5
5
  "@angular/common": "16.0.2",
6
6
  "@angular/core": "16.0.2",
@@ -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
+ }
@@ -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 "@angular/common";
5
- import * as i4 from "@angular/material/tooltip";
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 i3.CommonModule, typeof i4.MatTooltipModule], [typeof i1.PermissionGateComponent, typeof i2.PermissionGateDirective]>;
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[];