@apipass/permissions 1.0.196 → 1.0.198

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.
@@ -0,0 +1,157 @@
1
+ import { Directive, ElementRef, HostBinding, HostListener, Input, Renderer2, ViewContainerRef } from '@angular/core';
2
+ import { Overlay } from '@angular/cdk/overlay';
3
+ import { ComponentPortal } from '@angular/cdk/portal';
4
+ import { ApipassGateTooltipComponent } from './permission-gate-tooltip.component';
5
+ import * as i0 from "@angular/core";
6
+ import * as i1 from "@angular/cdk/overlay";
7
+ /**
8
+ * Gates an action element by user permission — single, unified API.
9
+ *
10
+ * Modes:
11
+ * - `shadow` (default): element stays visible but visually grayed out, cursor not-allowed, clicks blocked.
12
+ * - `hide`: element removed from layout (display:none) when blocked.
13
+ *
14
+ * Tooltip: uses CDK Overlay to render a Material-styled tooltip.
15
+ * - blocked + label: shows the permission label (why access is denied)
16
+ * - allowed + tooltip: shows the action tooltip (what the element does)
17
+ * - otherwise: no tooltip
18
+ */
19
+ class ApipassGateDirective {
20
+ el;
21
+ renderer;
22
+ overlay;
23
+ vcr;
24
+ isAllowed = true;
25
+ label = '';
26
+ tooltip = '';
27
+ mode = 'shadow';
28
+ overlayRef = null;
29
+ tooltipRef = null;
30
+ constructor(el, renderer, overlay, vcr) {
31
+ this.el = el;
32
+ this.renderer = renderer;
33
+ this.overlay = overlay;
34
+ this.vcr = vcr;
35
+ }
36
+ get blocked() {
37
+ return !this.isAllowed;
38
+ }
39
+ get tooltipMessage() {
40
+ return this.blocked ? this.label : this.tooltip;
41
+ }
42
+ ngOnChanges() {
43
+ this.applyVisualState();
44
+ if (this.tooltipRef) {
45
+ this.tooltipRef.instance.message = this.tooltipMessage;
46
+ }
47
+ }
48
+ ngOnDestroy() {
49
+ this.destroyOverlay();
50
+ }
51
+ onMouseEnter() {
52
+ if (!this.tooltipMessage || this.overlayRef)
53
+ return;
54
+ this.showTooltip();
55
+ }
56
+ onMouseLeave() {
57
+ this.destroyOverlay();
58
+ }
59
+ get _ariaDisabled() {
60
+ return this.blocked ? true : null;
61
+ }
62
+ get _blockedClass() {
63
+ return this.blocked;
64
+ }
65
+ block(e) {
66
+ if (this.blocked) {
67
+ e.preventDefault();
68
+ e.stopImmediatePropagation();
69
+ }
70
+ }
71
+ applyVisualState() {
72
+ const el = this.el.nativeElement;
73
+ if (this.blocked && this.mode === 'shadow') {
74
+ this.renderer.setStyle(el, 'filter', 'grayscale(1) opacity(0.5)');
75
+ this.renderer.setStyle(el, 'cursor', 'not-allowed');
76
+ this.renderer.setStyle(el, 'pointer-events', 'auto');
77
+ this.renderer.removeStyle(el, 'display');
78
+ }
79
+ else if (this.blocked && this.mode === 'hide') {
80
+ this.renderer.setStyle(el, 'display', 'none');
81
+ this.renderer.removeStyle(el, 'filter');
82
+ this.renderer.removeStyle(el, 'cursor');
83
+ this.renderer.removeStyle(el, 'pointer-events');
84
+ }
85
+ else {
86
+ this.renderer.removeStyle(el, 'filter');
87
+ this.renderer.removeStyle(el, 'cursor');
88
+ this.renderer.removeStyle(el, 'pointer-events');
89
+ this.renderer.removeStyle(el, 'display');
90
+ }
91
+ }
92
+ showTooltip() {
93
+ const positionStrategy = this.overlay
94
+ .position()
95
+ .flexibleConnectedTo(this.el)
96
+ .withPositions([
97
+ { originX: 'center', originY: 'top', overlayX: 'center', overlayY: 'bottom', offsetY: -8 },
98
+ { originX: 'center', originY: 'bottom', overlayX: 'center', overlayY: 'top', offsetY: 8 }
99
+ ]);
100
+ this.overlayRef = this.overlay.create({
101
+ positionStrategy,
102
+ scrollStrategy: this.overlay.scrollStrategies.close()
103
+ });
104
+ const portal = new ComponentPortal(ApipassGateTooltipComponent, this.vcr);
105
+ this.tooltipRef = this.overlayRef.attach(portal);
106
+ this.tooltipRef.instance.message = this.tooltipMessage;
107
+ this.tooltipRef.changeDetectorRef.detectChanges();
108
+ }
109
+ destroyOverlay() {
110
+ this.overlayRef?.dispose();
111
+ this.overlayRef = null;
112
+ this.tooltipRef = null;
113
+ }
114
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "16.0.2", ngImport: i0, type: ApipassGateDirective, deps: [{ token: i0.ElementRef }, { token: i0.Renderer2 }, { token: i1.Overlay }, { token: i0.ViewContainerRef }], target: i0.ɵɵFactoryTarget.Directive });
115
+ static ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "14.0.0", version: "16.0.2", type: ApipassGateDirective, selector: "[apipassGate]", inputs: { isAllowed: ["apipassGate", "isAllowed"], label: ["apipassGateLabel", "label"], tooltip: ["apipassGateTooltip", "tooltip"], mode: ["apipassGateMode", "mode"] }, host: { listeners: { "mouseenter": "onMouseEnter()", "mouseleave": "onMouseLeave()", "click": "block($event)", "keydown.enter": "block($event)", "keydown.space": "block($event)" }, properties: { "attr.aria-disabled": "this._ariaDisabled", "class.apipass-gate-blocked": "this._blockedClass" } }, usesOnChanges: true, ngImport: i0 });
116
+ }
117
+ export { ApipassGateDirective };
118
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "16.0.2", ngImport: i0, type: ApipassGateDirective, decorators: [{
119
+ type: Directive,
120
+ args: [{
121
+ selector: '[apipassGate]'
122
+ }]
123
+ }], ctorParameters: function () { return [{ type: i0.ElementRef }, { type: i0.Renderer2 }, { type: i1.Overlay }, { type: i0.ViewContainerRef }]; }, propDecorators: { isAllowed: [{
124
+ type: Input,
125
+ args: ['apipassGate']
126
+ }], label: [{
127
+ type: Input,
128
+ args: ['apipassGateLabel']
129
+ }], tooltip: [{
130
+ type: Input,
131
+ args: ['apipassGateTooltip']
132
+ }], mode: [{
133
+ type: Input,
134
+ args: ['apipassGateMode']
135
+ }], onMouseEnter: [{
136
+ type: HostListener,
137
+ args: ['mouseenter']
138
+ }], onMouseLeave: [{
139
+ type: HostListener,
140
+ args: ['mouseleave']
141
+ }], _ariaDisabled: [{
142
+ type: HostBinding,
143
+ args: ['attr.aria-disabled']
144
+ }], _blockedClass: [{
145
+ type: HostBinding,
146
+ args: ['class.apipass-gate-blocked']
147
+ }], block: [{
148
+ type: HostListener,
149
+ args: ['click', ['$event']]
150
+ }, {
151
+ type: HostListener,
152
+ args: ['keydown.enter', ['$event']]
153
+ }, {
154
+ type: HostListener,
155
+ args: ['keydown.space', ['$event']]
156
+ }] } });
157
+ //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoicGVybWlzc2lvbi1nYXRlLXN0cnVjdHVyYWwuZGlyZWN0aXZlLmpzIiwic291cmNlUm9vdCI6IiIsInNvdXJjZXMiOlsiLi4vLi4vLi4vLi4vcHJvamVjdHMvcGVybWlzc2lvbnMvc3JjL3Blcm1pc3Npb24tZ2F0ZS9wZXJtaXNzaW9uLWdhdGUtc3RydWN0dXJhbC5kaXJlY3RpdmUudHMiXSwibmFtZXMiOltdLCJtYXBwaW5ncyI6IkFBQUEsT0FBTyxFQUVMLFNBQVMsRUFDVCxVQUFVLEVBQ1YsV0FBVyxFQUNYLFlBQVksRUFDWixLQUFLLEVBR0wsU0FBUyxFQUNULGdCQUFnQixFQUNqQixNQUFNLGVBQWUsQ0FBQTtBQUN0QixPQUFPLEVBQUUsT0FBTyxFQUFjLE1BQU0sc0JBQXNCLENBQUE7QUFDMUQsT0FBTyxFQUFFLGVBQWUsRUFBRSxNQUFNLHFCQUFxQixDQUFBO0FBQ3JELE9BQU8sRUFBRSwyQkFBMkIsRUFBRSxNQUFNLHFDQUFxQyxDQUFBOzs7QUFFakY7Ozs7Ozs7Ozs7O0dBV0c7QUFDSCxNQUdhLG9CQUFvQjtJQVdaO0lBQ0E7SUFDQTtJQUNBO0lBWkcsU0FBUyxHQUFHLElBQUksQ0FBQTtJQUNYLEtBQUssR0FBRyxFQUFFLENBQUE7SUFDUixPQUFPLEdBQUcsRUFBRSxDQUFBO0lBQ2YsSUFBSSxHQUFzQixRQUFRLENBQUE7SUFFcEQsVUFBVSxHQUFzQixJQUFJLENBQUE7SUFDcEMsVUFBVSxHQUFxRCxJQUFJLENBQUE7SUFFM0UsWUFDbUIsRUFBYyxFQUNkLFFBQW1CLEVBQ25CLE9BQWdCLEVBQ2hCLEdBQXFCO1FBSHJCLE9BQUUsR0FBRixFQUFFLENBQVk7UUFDZCxhQUFRLEdBQVIsUUFBUSxDQUFXO1FBQ25CLFlBQU8sR0FBUCxPQUFPLENBQVM7UUFDaEIsUUFBRyxHQUFILEdBQUcsQ0FBa0I7SUFDckMsQ0FBQztJQUVKLElBQVksT0FBTztRQUNqQixPQUFPLENBQUMsSUFBSSxDQUFDLFNBQVMsQ0FBQTtJQUN4QixDQUFDO0lBRUQsSUFBWSxjQUFj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MkZBQXBCLG9CQUFvQjtrQkFIaEMsU0FBUzttQkFBQztvQkFDVCxRQUFRLEVBQUUsZUFBZTtpQkFDMUI7OEtBR3VCLFNBQVM7c0JBQTlCLEtBQUs7dUJBQUMsYUFBYTtnQkFDTyxLQUFLO3NCQUEvQixLQUFLO3VCQUFDLGtCQUFrQjtnQkFDSSxPQUFPO3NCQUFuQyxLQUFLO3VCQUFDLG9CQUFvQjtnQkFDRCxJQUFJO3NCQUE3QixLQUFLO3VCQUFDLGlCQUFpQjtnQkFnQ3hCLFlBQVk7c0JBRFgsWUFBWTt1QkFBQyxZQUFZO2dCQU8xQixZQUFZO3NCQURYLFlBQVk7dUJBQUMsWUFBWTtnQkFNdEIsYUFBYTtzQkFEaEIsV0FBVzt1QkFBQyxvQkFBb0I7Z0JBTTdCLGFBQWE7c0JBRGhCLFdBQVc7dUJBQUMsNEJBQTRCO2dCQVF6QyxLQUFLO3NCQUhKLFlBQVk7dUJBQUMsT0FBTyxFQUFFLENBQUMsUUFBUSxDQUFDOztzQkFDaEMsWUFBWTt1QkFBQyxlQUFlLEVBQUUsQ0FBQyxRQUFRLENBQUM7O3NCQUN4QyxZQUFZO3VCQUFDLGVBQWUsRUFBRSxDQUFDLFFBQVEsQ0FBQyIsInNvdXJjZXNDb250ZW50IjpbImltcG9ydCB7XG4gIENvbXBvbmVudFJlZixcbiAgRGlyZWN0aXZlLFxuICBFbGVtZW50UmVmLFxuICBIb3N0QmluZGluZyxcbiAgSG9zdExpc3RlbmVyLFxuICBJbnB1dCxcbiAgT25DaGFuZ2VzLFxuICBPbkRlc3Ryb3ksXG4gIFJlbmRlcmVyMixcbiAgVmlld0NvbnRhaW5lclJlZlxufSBmcm9tICdAYW5ndWxhci9jb3JlJ1xuaW1wb3J0IHsgT3ZlcmxheSwgT3ZlcmxheVJlZiB9IGZyb20gJ0Bhbmd1bGFyL2Nkay9vdmVybGF5J1xuaW1wb3J0IHsgQ29tcG9uZW50UG9ydGFsIH0gZnJvbSAnQGFuZ3VsYXIvY2RrL3BvcnRhbCdcbmltcG9ydCB7IEFwaXBhc3NHYXRlVG9vbHRpcENvbXBvbmVudCB9IGZyb20gJy4vcGVybWlzc2lvbi1nYXRlLXRvb2x0aXAuY29tcG9uZW50J1xuXG4vKipcbiAqIEdhdGVzIGFuIGFjdGlvbiBlbGVtZW50IGJ5IHVzZXIgcGVybWlzc2lvbiDigJQgc2luZ2xlLCB1bmlmaWVkIEFQSS5cbiAqXG4gKiBNb2RlczpcbiAqICAtIGBzaGFkb3dgIChkZWZhdWx0KTogZWxlbWVudCBzdGF5cyB2aXNpYmxlIGJ1dCB2aXN1YWxseSBncmF5ZWQgb3V0LCBjdXJzb3Igbm90LWFsbG93ZWQsIGNsaWNrcyBibG9ja2VkLlxuICogIC0gYGhpZGVgOiBlbGVtZW50IHJlbW92ZWQgZnJvbSBsYXlvdXQgKGRpc3BsYXk6bm9uZSkgd2hlbiBibG9ja2VkLlxuICpcbiAqIFRvb2x0aXA6IHVzZXMgQ0RLIE92ZXJsYXkgdG8gcmVuZGVyIGEgTWF0ZXJpYWwtc3R5bGVkIHRvb2x0aXAuXG4gKiAgLSBibG9ja2VkICsgbGFiZWw6IHNob3dzIHRoZSBwZXJtaXNzaW9uIGxhYmVsICh3aHkgYWNjZXNzIGlzIGRlbmllZClcbiAqICAtIGFsbG93ZWQgKyB0b29sdGlwOiBzaG93cyB0aGUgYWN0aW9uIHRvb2x0aXAgKHdoYXQgdGhlIGVsZW1lbnQgZG9lcylcbiAqICAtIG90aGVyd2lzZTogbm8gdG9vbHRpcFxuICovXG5ARGlyZWN0aXZlKHtcbiAgc2VsZWN0b3I6ICdbYXBpcGFzc0dhdGVdJ1xufSlcbmV4cG9ydCBjbGFzcyBBcGlwYXNzR2F0ZURpcmVjdGl2ZSBpbXBsZW1lbnRzIE9uQ2hhbmdlcywgT25EZXN0cm95IHtcblxuICBASW5wdXQoJ2FwaXBhc3NHYXRlJykgaXNBbGxvd2VkID0gdHJ1ZVxuICBASW5wdXQoJ2FwaXBhc3NHYXRlTGFiZWwnKSBsYWJlbCA9ICcnXG4gIEBJbnB1dCgnYXBpcGFzc0dhdGVUb29sdGlwJykgdG9vbHRpcCA9ICcnXG4gIEBJbnB1dCgnYXBpcGFzc0dhdGVNb2RlJykgbW9kZTogJ3NoYWRvdycgfCAnaGlkZScgPSAnc2hhZG93J1xuXG4gIHByaXZhdGUgb3ZlcmxheVJlZjogT3ZlcmxheVJlZiB8IG51bGwgPSBudWxsXG4gIHByaXZhdGUgdG9vbHRpcFJlZjogQ29tcG9uZW50UmVmPEFwaXBhc3NHYXRlVG9vbHRpcENvbXBvbmVudD4gfCBudWxsID0gbnVsbFxuXG4gIGNvbnN0cnVjdG9yKFxuICAgIHByaXZhdGUgcmVhZG9ubHkgZWw6IEVsZW1lbnRSZWYsXG4gICAgcHJpdmF0ZSByZWFkb25seSByZW5kZXJlcjogUmVuZGVyZXIyLFxuICAgIHByaXZhdGUgcmVhZG9ubHkgb3ZlcmxheTogT3ZlcmxheSxcbiAgICBwcml2YXRlIHJlYWRvbmx5IHZjcjogVmlld0NvbnRhaW5lclJlZlxuICApIHt9XG5cbiAgcHJpdmF0ZSBnZXQgYmxvY2tlZCgpOiBib29sZWFuIHtcbiAgICByZXR1cm4gIXRoaXMuaXNBbGxvd2VkXG4gIH1cblxuICBwcml2YXRlIGdldCB0b29sdGlwTWVzc2FnZSgpOiBzdHJpbmcge1xuICAgIHJldHVybiB0aGlzLmJsb2NrZWQgPyB0aGlzLmxhYmVsIDogdGhpcy50b29sdGlwXG4gIH1cblxuICBuZ09uQ2hhbmdlcygpOiB2b2lkIHtcbiAgICB0aGlzLmFwcGx5VmlzdWFsU3RhdGUoKVxuICAgIGlmICh0aGlzLnRvb2x0aXBSZWYpIHtcbiAgICAgIHRoaXMudG9vbHRpcFJlZi5pbnN0YW5jZS5tZXNzYWdlID0gdGhpcy50b29sdGlwTWVzc2FnZVxuICAgIH1cbiAgfVxuXG4gIG5nT25EZXN0cm95KCk6IHZvaWQge1xuICAgIHRoaXMuZGVzdHJveU92ZXJsYXkoKVxuICB9XG5cbiAgQEhvc3RMaXN0ZW5lcignbW91c2VlbnRlcicpXG4gIG9uTW91c2VFbnRlcigpOiB2b2lkIHtcbiAgICBpZiAoIXRoaXMudG9vbHRpcE1lc3NhZ2UgfHwgdGhpcy5vdmVybGF5UmVmKSByZXR1cm5cbiAgICB0aGlzLnNob3dUb29sdGlwKClcbiAgfVxuXG4gIEBIb3N0TGlzdGVuZXIoJ21vdXNlbGVhdmUnKVxuICBvbk1vdXNlTGVhdmUoKTogdm9pZCB7XG4gICAgdGhpcy5kZXN0cm95T3ZlcmxheSgpXG4gIH1cblxuICBASG9zdEJpbmRpbmcoJ2F0dHIuYXJpYS1kaXNhYmxlZCcpXG4gIGdldCBfYXJpYURpc2FibGVkKCk6IHRydWUgfCBudWxsIHtcbiAgICByZXR1cm4gdGhpcy5ibG9ja2VkID8gdHJ1ZSA6IG51bGxcbiAgfVxuXG4gIEBIb3N0QmluZGluZygnY2xhc3MuYXBpcGFzcy1nYXRlLWJsb2NrZWQnKVxuICBnZXQgX2Jsb2NrZWRDbGFzcygpOiBib29sZWFuIHtcbiAgICByZXR1cm4gdGhpcy5ibG9ja2VkXG4gIH1cblxuICBASG9zdExpc3RlbmVyKCdjbGljaycsIFsnJGV2ZW50J10pXG4gIEBIb3N0TGlzdGVuZXIoJ2tleWRvd24uZW50ZXInLCBbJyRldmVudCddKVxuICBASG9zdExpc3RlbmVyKCdrZXlkb3duLnNwYWNlJywgWyckZXZlbnQnXSlcbiAgYmxvY2soZTogRXZlbnQpOiB2b2lkIHtcbiAgICBpZiAodGhpcy5ibG9ja2VkKSB7XG4gICAgICBlLnByZXZlbnREZWZhdWx0KClcbiAgICAgIGUuc3RvcEltbWVkaWF0ZVByb3BhZ2F0aW9uKClcbiAgICB9XG4gIH1cblxuICBwcml2YXRlIGFwcGx5VmlzdWFsU3RhdGUoKTogdm9pZCB7XG4gICAgY29uc3QgZWwgPSB0aGlzLmVsLm5hdGl2ZUVsZW1lbnRcbiAgICBpZiAodGhpcy5ibG9ja2VkICYmIHRoaXMubW9kZSA9PT0gJ3NoYWRvdycpIHtcbiAgICAgIHRoaXMucmVuZGVyZXIuc2V0U3R5bGUoZWwsICdmaWx0ZXInLCAnZ3JheXNjYWxlKDEpIG9wYWNpdHkoMC41KScpXG4gICAgICB0aGlzLnJlbmRlcmVyLnNldFN0eWxlKGVsLCAnY3Vyc29yJywgJ25vdC1hbGxvd2VkJylcbiAgICAgIHRoaXMucmVuZGVyZXIuc2V0U3R5bGUoZWwsICdwb2ludGVyLWV2ZW50cycsICdhdXRvJylcbiAgICAgIHRoaXMucmVuZGVyZXIucmVtb3ZlU3R5bGUoZWwsICdkaXNwbGF5JylcbiAgICB9IGVsc2UgaWYgKHRoaXMuYmxvY2tlZCAmJiB0aGlzLm1vZGUgPT09ICdoaWRlJykge1xuICAgICAgdGhpcy5yZW5kZXJlci5zZXRTdHlsZShlbCwgJ2Rpc3BsYXknLCAnbm9uZScpXG4gICAgICB0aGlzLnJlbmRlcmVyLnJlbW92ZVN0eWxlKGVsLCAnZmlsdGVyJylcbiAgICAgIHRoaXMucmVuZGVyZXIucmVtb3ZlU3R5bGUoZWwsICdjdXJzb3InKVxuICAgICAgdGhpcy5yZW5kZXJlci5yZW1vdmVTdHlsZShlbCwgJ3BvaW50ZXItZXZlbnRzJylcbiAgICB9IGVsc2Uge1xuICAgICAgdGhpcy5yZW5kZXJlci5yZW1vdmVTdHlsZShlbCwgJ2ZpbHRlcicpXG4gICAgICB0aGlzLnJlbmRlcmVyLnJlbW92ZVN0eWxlKGVsLCAnY3Vyc29yJylcbiAgICAgIHRoaXMucmVuZGVyZXIucmVtb3ZlU3R5bGUoZWwsICdwb2ludGVyLWV2ZW50cycpXG4gICAgICB0aGlzLnJlbmRlcmVyLnJlbW92ZVN0eWxlKGVsLCAnZGlzcGxheScpXG4gICAgfVxuICB9XG5cbiAgcHJpdmF0ZSBzaG93VG9vbHRpcCgpOiB2b2lkIHtcbiAgICBjb25zdCBwb3NpdGlvblN0cmF0ZWd5ID0gdGhpcy5vdmVybGF5XG4gICAgICAucG9zaXRpb24oKVxuICAgICAgLmZsZXhpYmxlQ29ubmVjdGVkVG8odGhpcy5lbClcbiAgICAgIC53aXRoUG9zaXRpb25zKFtcbiAgICAgICAgeyBvcmlnaW5YOiAnY2VudGVyJywgb3JpZ2luWTogJ3RvcCcsIG92ZXJsYXlYOiAnY2VudGVyJywgb3ZlcmxheVk6ICdib3R0b20nLCBvZmZzZXRZOiAtOCB9LFxuICAgICAgICB7IG9yaWdpblg6ICdjZW50ZXInLCBvcmlnaW5ZOiAnYm90dG9tJywgb3ZlcmxheVg6ICdjZW50ZXInLCBvdmVybGF5WTogJ3RvcCcsIG9mZnNldFk6IDggfVxuICAgICAgXSlcblxuICAgIHRoaXMub3ZlcmxheVJlZiA9IHRoaXMub3ZlcmxheS5jcmVhdGUoe1xuICAgICAgcG9zaXRpb25TdHJhdGVneSxcbiAgICAgIHNjcm9sbFN0cmF0ZWd5OiB0aGlzLm92ZXJsYXkuc2Nyb2xsU3RyYXRlZ2llcy5jbG9zZSgpXG4gICAgfSlcblxuICAgIGNvbnN0IHBvcnRhbCA9IG5ldyBDb21wb25lbnRQb3J0YWwoQXBpcGFzc0dhdGVUb29sdGlwQ29tcG9uZW50LCB0aGlzLnZjcilcbiAgICB0aGlzLnRvb2x0aXBSZWYgPSB0aGlzLm92ZXJsYXlSZWYuYXR0YWNoKHBvcnRhbClcbiAgICB0aGlzLnRvb2x0aXBSZWYuaW5zdGFuY2UubWVzc2FnZSA9IHRoaXMudG9vbHRpcE1lc3NhZ2VcbiAgICB0aGlzLnRvb2x0aXBSZWYuY2hhbmdlRGV0ZWN0b3JSZWYuZGV0ZWN0Q2hhbmdlcygpXG4gIH1cblxuICBwcml2YXRlIGRlc3Ryb3lPdmVybGF5KCk6IHZvaWQge1xuICAgIHRoaXMub3ZlcmxheVJlZj8uZGlzcG9zZSgpXG4gICAgdGhpcy5vdmVybGF5UmVmID0gbnVsbFxuICAgIHRoaXMudG9vbHRpcFJlZiA9IG51bGxcbiAgfVxuXG59XG4iXX0=
@@ -0,0 +1,17 @@
1
+ import { ChangeDetectionStrategy, Component } from '@angular/core';
2
+ import * as i0 from "@angular/core";
3
+ /**
4
+ * Internal tooltip overlay component used by ApipassGateDirective.
5
+ * Styled to match Angular Material's tooltip appearance.
6
+ */
7
+ class ApipassGateTooltipComponent {
8
+ message = '';
9
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "16.0.2", ngImport: i0, type: ApipassGateTooltipComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
10
+ static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "16.0.2", type: ApipassGateTooltipComponent, isStandalone: true, selector: "apipass-gate-tooltip", ngImport: i0, template: '<div class="apipass-gate-tooltip">{{ message }}</div>', isInline: true, styles: [":host{pointer-events:none}.apipass-gate-tooltip{background:rgba(97,97,97,.9);color:#fff;padding:4px 8px;border-radius:4px;font-size:12px;line-height:16px;max-width:250px;word-break:break-word;white-space:pre-line}\n"], changeDetection: i0.ChangeDetectionStrategy.OnPush });
11
+ }
12
+ export { ApipassGateTooltipComponent };
13
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "16.0.2", ngImport: i0, type: ApipassGateTooltipComponent, decorators: [{
14
+ type: Component,
15
+ args: [{ standalone: true, selector: 'apipass-gate-tooltip', template: '<div class="apipass-gate-tooltip">{{ message }}</div>', changeDetection: ChangeDetectionStrategy.OnPush, styles: [":host{pointer-events:none}.apipass-gate-tooltip{background:rgba(97,97,97,.9);color:#fff;padding:4px 8px;border-radius:4px;font-size:12px;line-height:16px;max-width:250px;word-break:break-word;white-space:pre-line}\n"] }]
16
+ }] });
17
+ //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoicGVybWlzc2lvbi1nYXRlLXRvb2x0aXAuY29tcG9uZW50LmpzIiwic291cmNlUm9vdCI6IiIsInNvdXJjZXMiOlsiLi4vLi4vLi4vLi4vcHJvamVjdHMvcGVybWlzc2lvbnMvc3JjL3Blcm1pc3Npb24tZ2F0ZS9wZXJtaXNzaW9uLWdhdGUtdG9vbHRpcC5jb21wb25lbnQudHMiXSwibmFtZXMiOltdLCJtYXBwaW5ncyI6IkFBQUEsT0FBTyxFQUFFLHVCQUF1QixFQUFFLFNBQVMsRUFBRSxNQUFNLGVBQWUsQ0FBQTs7QUFFbEU7OztHQUdHO0FBQ0gsTUFvQmEsMkJBQTJCO0lBRXRDLE9BQU8sR0FBRyxFQUFFLENBQUE7dUdBRkQsMkJBQTJCOzJGQUEzQiwyQkFBMkIsZ0ZBakI1Qix1REFBdUQ7O1NBaUJ0RCwyQkFBMkI7MkZBQTNCLDJCQUEyQjtrQkFwQnZDLFNBQVM7aUNBQ0ksSUFBSSxZQUNOLHNCQUFzQixZQUN0Qix1REFBdUQsbUJBZWhELHVCQUF1QixDQUFDLE1BQU0iLCJzb3VyY2VzQ29udGVudCI6WyJpbXBvcnQgeyBDaGFuZ2VEZXRlY3Rpb25TdHJhdGVneSwgQ29tcG9uZW50IH0gZnJvbSAnQGFuZ3VsYXIvY29yZSdcblxuLyoqXG4gKiBJbnRlcm5hbCB0b29sdGlwIG92ZXJsYXkgY29tcG9uZW50IHVzZWQgYnkgQXBpcGFzc0dhdGVEaXJlY3RpdmUuXG4gKiBTdHlsZWQgdG8gbWF0Y2ggQW5ndWxhciBNYXRlcmlhbCdzIHRvb2x0aXAgYXBwZWFyYW5jZS5cbiAqL1xuQENvbXBvbmVudCh7XG4gIHN0YW5kYWxvbmU6IHRydWUsXG4gIHNlbGVjdG9yOiAnYXBpcGFzcy1nYXRlLXRvb2x0aXAnLFxuICB0ZW1wbGF0ZTogJzxkaXYgY2xhc3M9XCJhcGlwYXNzLWdhdGUtdG9vbHRpcFwiPnt7IG1lc3NhZ2UgfX08L2Rpdj4nLFxuICBzdHlsZXM6IFtgXG4gICAgOmhvc3QgeyBwb2ludGVyLWV2ZW50czogbm9uZTsgfVxuICAgIC5hcGlwYXNzLWdhdGUtdG9vbHRpcCB7XG4gICAgICBiYWNrZ3JvdW5kOiByZ2JhKDk3LCA5NywgOTcsIDAuOSk7XG4gICAgICBjb2xvcjogI2ZmZjtcbiAgICAgIHBhZGRpbmc6IDRweCA4cHg7XG4gICAgICBib3JkZXItcmFkaXVzOiA0cHg7XG4gICAgICBmb250LXNpemU6IDEycHg7XG4gICAgICBsaW5lLWhlaWdodDogMTZweDtcbiAgICAgIG1heC13aWR0aDogMjUwcHg7XG4gICAgICB3b3JkLWJyZWFrOiBicmVhay13b3JkO1xuICAgICAgd2hpdGUtc3BhY2U6IHByZS1saW5lO1xuICAgIH1cbiAgYF0sXG4gIGNoYW5nZURldGVjdGlvbjogQ2hhbmdlRGV0ZWN0aW9uU3RyYXRlZ3kuT25QdXNoXG59KVxuZXhwb3J0IGNsYXNzIEFwaXBhc3NHYXRlVG9vbHRpcENvbXBvbmVudCB7XG5cbiAgbWVzc2FnZSA9ICcnXG5cbn1cbiJdfQ==
@@ -3,11 +3,13 @@ import * as i0 from "@angular/core";
3
3
  import * as i1 from "@angular/common";
4
4
  import * as i2 from "@angular/material/tooltip";
5
5
  /**
6
- * Angular equivalent of the React `PermissionGate`.
6
+ * Wraps projected content with a permission gate.
7
7
  *
8
- * It **never hides** the protected action: when blocked it still renders the
9
- * content, disabled, wrapped in a tooltip that explains why. The decision
10
- * (`isAllowed`) is supplied by the caller — this component only presents it.
8
+ * The decision (`isAllowed`) is supplied by the caller this component only presents it.
9
+ *
10
+ * Modes:
11
+ * - `shadow` (default): renders content grayed out with a tooltip explaining why access is denied.
12
+ * - `hide`: removes the content entirely from the layout when blocked.
11
13
  *
12
14
  * @example
13
15
  * ```html
@@ -23,20 +25,24 @@ class PermissionGateComponent {
23
25
  isAllowed = false;
24
26
  /** Tooltip text shown when the action is blocked. */
25
27
  noAccessLabel = '';
28
+ /** Display mode when permission is denied. Default: `shadow`. */
29
+ mode = 'shadow';
26
30
  template;
27
31
  static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "16.0.2", ngImport: i0, type: PermissionGateComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
28
- static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "16.0.2", type: PermissionGateComponent, selector: "apipass-permission-gate", inputs: { isAllowed: "isAllowed", noAccessLabel: "noAccessLabel" }, queries: [{ propertyName: "template", first: true, predicate: TemplateRef, descendants: true }], ngImport: i0, template: "<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", styles: [".permission-gate-blocked{display:inline-flex;cursor:not-allowed}\n"], dependencies: [{ kind: "directive", type: i1.NgIf, selector: "[ngIf]", inputs: ["ngIf", "ngIfThen", "ngIfElse"] }, { kind: "directive", type: i1.NgTemplateOutlet, selector: "[ngTemplateOutlet]", inputs: ["ngTemplateOutletContext", "ngTemplateOutlet", "ngTemplateOutletInjector"] }, { kind: "directive", type: i2.MatTooltip, selector: "[matTooltip]", exportAs: ["matTooltip"] }] });
32
+ static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "16.0.2", type: PermissionGateComponent, selector: "apipass-permission-gate", inputs: { isAllowed: "isAllowed", noAccessLabel: "noAccessLabel", mode: "mode" }, queries: [{ propertyName: "template", first: true, predicate: TemplateRef, descendants: true }], ngImport: i0, template: "<ng-container *ngIf=\"isAllowed\">\n <ng-container *ngTemplateOutlet=\"template ?? null; context: { $implicit: true }\"></ng-container>\n</ng-container>\n\n<ng-container *ngIf=\"!isAllowed && mode === 'shadow'\">\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-container>\n", styles: [".permission-gate-blocked{display:inline-flex;cursor:not-allowed;filter:grayscale(1) opacity(.5)}.permission-gate-blocked ::ng-deep *{pointer-events:none}\n"], dependencies: [{ kind: "directive", type: i1.NgIf, selector: "[ngIf]", inputs: ["ngIf", "ngIfThen", "ngIfElse"] }, { kind: "directive", type: i1.NgTemplateOutlet, selector: "[ngTemplateOutlet]", inputs: ["ngTemplateOutletContext", "ngTemplateOutlet", "ngTemplateOutletInjector"] }, { kind: "directive", type: i2.MatTooltip, selector: "[matTooltip]", exportAs: ["matTooltip"] }] });
29
33
  }
30
34
  export { PermissionGateComponent };
31
35
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "16.0.2", ngImport: i0, type: PermissionGateComponent, decorators: [{
32
36
  type: Component,
33
- args: [{ selector: 'apipass-permission-gate', template: "<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", styles: [".permission-gate-blocked{display:inline-flex;cursor:not-allowed}\n"] }]
37
+ args: [{ selector: 'apipass-permission-gate', template: "<ng-container *ngIf=\"isAllowed\">\n <ng-container *ngTemplateOutlet=\"template ?? null; context: { $implicit: true }\"></ng-container>\n</ng-container>\n\n<ng-container *ngIf=\"!isAllowed && mode === 'shadow'\">\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-container>\n", styles: [".permission-gate-blocked{display:inline-flex;cursor:not-allowed;filter:grayscale(1) opacity(.5)}.permission-gate-blocked ::ng-deep *{pointer-events:none}\n"] }]
34
38
  }], propDecorators: { isAllowed: [{
35
39
  type: Input
36
40
  }], noAccessLabel: [{
37
41
  type: Input
42
+ }], mode: [{
43
+ type: Input
38
44
  }], template: [{
39
45
  type: ContentChild,
40
46
  args: [TemplateRef]
41
47
  }] } });
42
- //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoicGVybWlzc2lvbi1nYXRlLmNvbXBvbmVudC5qcyIsInNvdXJjZVJvb3QiOiIiLCJzb3VyY2VzIjpbIi4uLy4uLy4uLy4uL3Byb2plY3RzL3Blcm1pc3Npb25zL3NyYy9wZXJtaXNzaW9uLWdhdGUvcGVybWlzc2lvbi1nYXRlLmNvbXBvbmVudC50cyIsIi4uLy4uLy4uLy4uL3Byb2plY3RzL3Blcm1pc3Npb25zL3NyYy9wZXJtaXNzaW9uLWdhdGUvcGVybWlzc2lvbi1nYXRlLmNvbXBvbmVudC5odG1sIl0sIm5hbWVzIjpbXSwibWFwcGluZ3MiOiJBQUFBLE9BQU8sRUFBRSxTQUFTLEVBQUUsWUFBWSxFQUFFLEtBQUssRUFBRSxXQUFXLEVBQUUsTUFBTSxlQUFlLENBQUE7Ozs7QUFPM0U7Ozs7Ozs7Ozs7Ozs7OztHQWVHO0FBQ0gsTUFLYSx1QkFBdUI7SUFFbEMsMkNBQTJDO0lBQ2xDLFNBQVMsR0FBRyxLQUFLLENBQUE7SUFFMUIscURBQXFEO0lBQzVDLGFBQWEsR0FBRyxFQUFFLENBQUE7SUFFQSxRQUFRLENBQXFDO3VHQVI3RCx1QkFBdUI7MkZBQXZCLHVCQUF1Qix5S0FRcEIsV0FBVyxnRENwQzNCLDJjQVlBOztTRGdCYSx1QkFBdUI7MkZBQXZCLHVCQUF1QjtrQkFMbkMsU0FBUzsrQkFDRSx5QkFBeUI7OEJBTzFCLFNBQVM7c0JBQWpCLEtBQUs7Z0JBR0csYUFBYTtzQkFBckIsS0FBSztnQkFFcUIsUUFBUTtzQkFBbEMsWUFBWTt1QkFBQyxXQUFXIiwic291cmNlc0NvbnRlbnQiOlsiaW1wb3J0IHsgQ29tcG9uZW50LCBDb250ZW50Q2hpbGQsIElucHV0LCBUZW1wbGF0ZVJlZiB9IGZyb20gJ0Bhbmd1bGFyL2NvcmUnXG5cbi8qKiBDb250ZXh0IG9iamVjdCBwYXNzZWQgdG8gdGhlIHByb2plY3RlZCB0ZW1wbGF0ZSDigJQgYCRpbXBsaWNpdGAgaXMgdGhlIGBhbGxvd2VkYCBmbGFnLiAqL1xuZXhwb3J0IGludGVyZmFjZSBQZXJtaXNzaW9uR2F0ZUNvbnRleHQge1xuICAkaW1wbGljaXQ6IGJvb2xlYW5cbn1cblxuLyoqXG4gKiBBbmd1bGFyIGVxdWl2YWxlbnQgb2YgdGhlIFJlYWN0IGBQZXJtaXNzaW9uR2F0ZWAuXG4gKlxuICogSXQgKipuZXZlciBoaWRlcyoqIHRoZSBwcm90ZWN0ZWQgYWN0aW9uOiB3aGVuIGJsb2NrZWQgaXQgc3RpbGwgcmVuZGVycyB0aGVcbiAqIGNvbnRlbnQsIGRpc2FibGVkLCB3cmFwcGVkIGluIGEgdG9vbHRpcCB0aGF0IGV4cGxhaW5zIHdoeS4gVGhlIGRlY2lzaW9uXG4gKiAoYGlzQWxsb3dlZGApIGlzIHN1cHBsaWVkIGJ5IHRoZSBjYWxsZXIg4oCUIHRoaXMgY29tcG9uZW50IG9ubHkgcHJlc2VudHMgaXQuXG4gKlxuICogQGV4YW1wbGVcbiAqIGBgYGh0bWxcbiAqIDxhcGlwYXNzLXBlcm1pc3Npb24tZ2F0ZSBbaXNBbGxvd2VkXT1cImNhbkNyZWF0ZVwiIFtub0FjY2Vzc0xhYmVsXT1cIidQRVJNSVNTSU9OUy5OT19DUkVBVEUnIHwgdHJhbnNsYXRlXCI+XG4gKiAgIDxuZy10ZW1wbGF0ZSBsZXQtYWxsb3dlZD5cbiAqICAgICA8YnV0dG9uIG1hdC1yYWlzZWQtYnV0dG9uIFtkaXNhYmxlZF09XCIhYWxsb3dlZFwiIChjbGljayk9XCJhbGxvd2VkICYmIGNyZWF0ZSgpXCI+TmV3PC9idXR0b24+XG4gKiAgIDwvbmctdGVtcGxhdGU+XG4gKiA8L2FwaXBhc3MtcGVybWlzc2lvbi1nYXRlPlxuICogYGBgXG4gKi9cbkBDb21wb25lbnQoe1xuICBzZWxlY3RvcjogJ2FwaXBhc3MtcGVybWlzc2lvbi1nYXRlJyxcbiAgdGVtcGxhdGVVcmw6ICcuL3Blcm1pc3Npb24tZ2F0ZS5jb21wb25lbnQuaHRtbCcsXG4gIHN0eWxlVXJsczogWycuL3Blcm1pc3Npb24tZ2F0ZS5jb21wb25lbnQuc2NzcyddXG59KVxuZXhwb3J0IGNsYXNzIFBlcm1pc3Npb25HYXRlQ29tcG9uZW50IHtcblxuICAvKiogV2hldGhlciB0aGUgZ2F0ZWQgYWN0aW9uIGlzIGFsbG93ZWQuICovXG4gIEBJbnB1dCgpIGlzQWxsb3dlZCA9IGZhbHNlXG5cbiAgLyoqIFRvb2x0aXAgdGV4dCBzaG93biB3aGVuIHRoZSBhY3Rpb24gaXMgYmxvY2tlZC4gKi9cbiAgQElucHV0KCkgbm9BY2Nlc3NMYWJlbCA9ICcnXG5cbiAgQENvbnRlbnRDaGlsZChUZW1wbGF0ZVJlZikgdGVtcGxhdGU/OiBUZW1wbGF0ZVJlZjxQZXJtaXNzaW9uR2F0ZUNvbnRleHQ+XG5cbn1cbiIsIjxuZy1jb250YWluZXIgKm5nSWY9XCJpc0FsbG93ZWQ7IGVsc2UgYmxvY2tlZFwiPlxuICA8bmctY29udGFpbmVyICpuZ1RlbXBsYXRlT3V0bGV0PVwidGVtcGxhdGUgPz8gbnVsbDsgY29udGV4dDogeyAkaW1wbGljaXQ6IHRydWUgfVwiPjwvbmctY29udGFpbmVyPlxuPC9uZy1jb250YWluZXI+XG5cbjxuZy10ZW1wbGF0ZSAjYmxvY2tlZD5cbiAgPHNwYW5cbiAgICBjbGFzcz1cInBlcm1pc3Npb24tZ2F0ZS1ibG9ja2VkXCJcbiAgICBbbWF0VG9vbHRpcF09XCJub0FjY2Vzc0xhYmVsXCJcbiAgICBbbWF0VG9vbHRpcERpc2FibGVkXT1cIiFub0FjY2Vzc0xhYmVsXCI+XG4gICAgPG5nLWNvbnRhaW5lciAqbmdUZW1wbGF0ZU91dGxldD1cInRlbXBsYXRlID8/IG51bGw7IGNvbnRleHQ6IHsgJGltcGxpY2l0OiBmYWxzZSB9XCI+PC9uZy1jb250YWluZXI+XG4gIDwvc3Bhbj5cbjwvbmctdGVtcGxhdGU+XG4iXX0=
48
+ //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoicGVybWlzc2lvbi1nYXRlLmNvbXBvbmVudC5qcyIsInNvdXJjZVJvb3QiOiIiLCJzb3VyY2VzIjpbIi4uLy4uLy4uLy4uL3Byb2plY3RzL3Blcm1pc3Npb25zL3NyYy9wZXJtaXNzaW9uLWdhdGUvcGVybWlzc2lvbi1nYXRlLmNvbXBvbmVudC50cyIsIi4uLy4uLy4uLy4uL3Byb2plY3RzL3Blcm1pc3Npb25zL3NyYy9wZXJtaXNzaW9uLWdhdGUvcGVybWlzc2lvbi1nYXRlLmNvbXBvbmVudC5odG1sIl0sIm5hbWVzIjpbXSwibWFwcGluZ3MiOiJBQUFBLE9BQU8sRUFBRSxTQUFTLEVBQUUsWUFBWSxFQUFFLEtBQUssRUFBRSxXQUFXLEVBQUUsTUFBTSxlQUFlLENBQUE7Ozs7QUFPM0U7Ozs7Ozs7Ozs7Ozs7Ozs7O0dBaUJHO0FBQ0gsTUFLYSx1QkFBdUI7SUFFbEMsMkNBQTJDO0lBQ2xDLFNBQVMsR0FBRyxLQUFLLENBQUE7SUFFMUIscURBQXFEO0lBQzVDLGFBQWEsR0FBRyxFQUFFLENBQUE7SUFFM0IsaUVBQWlFO0lBQ3hELElBQUksR0FBc0IsUUFBUSxDQUFBO0lBRWhCLFFBQVEsQ0FBcUM7dUdBWDdELHVCQUF1QjsyRkFBdkIsdUJBQXVCLHVMQVdwQixXQUFXLGdEQ3pDM0IsZ2VBWUE7O1NEa0JhLHVCQUF1QjsyRkFBdkIsdUJBQXVCO2tCQUxuQyxTQUFTOytCQUNFLHlCQUF5Qjs4QkFPMUIsU0FBUztzQkFBakIsS0FBSztnQkFHRyxhQUFhO3NCQUFyQixLQUFLO2dCQUdHLElBQUk7c0JBQVosS0FBSztnQkFFcUIsUUFBUTtzQkFBbEMsWUFBWTt1QkFBQyxXQUFXIiwic291cmNlc0NvbnRlbnQiOlsiaW1wb3J0IHsgQ29tcG9uZW50LCBDb250ZW50Q2hpbGQsIElucHV0LCBUZW1wbGF0ZVJlZiB9IGZyb20gJ0Bhbmd1bGFyL2NvcmUnXG5cbi8qKiBDb250ZXh0IG9iamVjdCBwYXNzZWQgdG8gdGhlIHByb2plY3RlZCB0ZW1wbGF0ZSDigJQgYCRpbXBsaWNpdGAgaXMgdGhlIGBhbGxvd2VkYCBmbGFnLiAqL1xuZXhwb3J0IGludGVyZmFjZSBQZXJtaXNzaW9uR2F0ZUNvbnRleHQge1xuICAkaW1wbGljaXQ6IGJvb2xlYW5cbn1cblxuLyoqXG4gKiBXcmFwcyBwcm9qZWN0ZWQgY29udGVudCB3aXRoIGEgcGVybWlzc2lvbiBnYXRlLlxuICpcbiAqIFRoZSBkZWNpc2lvbiAoYGlzQWxsb3dlZGApIGlzIHN1cHBsaWVkIGJ5IHRoZSBjYWxsZXIg4oCUIHRoaXMgY29tcG9uZW50IG9ubHkgcHJlc2VudHMgaXQuXG4gKlxuICogTW9kZXM6XG4gKiAgLSBgc2hhZG93YCAoZGVmYXVsdCk6IHJlbmRlcnMgY29udGVudCBncmF5ZWQgb3V0IHdpdGggYSB0b29sdGlwIGV4cGxhaW5pbmcgd2h5IGFjY2VzcyBpcyBkZW5pZWQuXG4gKiAgLSBgaGlkZWA6IHJlbW92ZXMgdGhlIGNvbnRlbnQgZW50aXJlbHkgZnJvbSB0aGUgbGF5b3V0IHdoZW4gYmxvY2tlZC5cbiAqXG4gKiBAZXhhbXBsZVxuICogYGBgaHRtbFxuICogPGFwaXBhc3MtcGVybWlzc2lvbi1nYXRlIFtpc0FsbG93ZWRdPVwiY2FuQ3JlYXRlXCIgW25vQWNjZXNzTGFiZWxdPVwiJ1BFUk1JU1NJT05TLk5PX0NSRUFURScgfCB0cmFuc2xhdGVcIj5cbiAqICAgPG5nLXRlbXBsYXRlIGxldC1hbGxvd2VkPlxuICogICAgIDxidXR0b24gbWF0LXJhaXNlZC1idXR0b24gW2Rpc2FibGVkXT1cIiFhbGxvd2VkXCIgKGNsaWNrKT1cImFsbG93ZWQgJiYgY3JlYXRlKClcIj5OZXc8L2J1dHRvbj5cbiAqICAgPC9uZy10ZW1wbGF0ZT5cbiAqIDwvYXBpcGFzcy1wZXJtaXNzaW9uLWdhdGU+XG4gKiBgYGBcbiAqL1xuQENvbXBvbmVudCh7XG4gIHNlbGVjdG9yOiAnYXBpcGFzcy1wZXJtaXNzaW9uLWdhdGUnLFxuICB0ZW1wbGF0ZVVybDogJy4vcGVybWlzc2lvbi1nYXRlLmNvbXBvbmVudC5odG1sJyxcbiAgc3R5bGVVcmxzOiBbJy4vcGVybWlzc2lvbi1nYXRlLmNvbXBvbmVudC5zY3NzJ11cbn0pXG5leHBvcnQgY2xhc3MgUGVybWlzc2lvbkdhdGVDb21wb25lbnQge1xuXG4gIC8qKiBXaGV0aGVyIHRoZSBnYXRlZCBhY3Rpb24gaXMgYWxsb3dlZC4gKi9cbiAgQElucHV0KCkgaXNBbGxvd2VkID0gZmFsc2VcblxuICAvKiogVG9vbHRpcCB0ZXh0IHNob3duIHdoZW4gdGhlIGFjdGlvbiBpcyBibG9ja2VkLiAqL1xuICBASW5wdXQoKSBub0FjY2Vzc0xhYmVsID0gJydcblxuICAvKiogRGlzcGxheSBtb2RlIHdoZW4gcGVybWlzc2lvbiBpcyBkZW5pZWQuIERlZmF1bHQ6IGBzaGFkb3dgLiAqL1xuICBASW5wdXQoKSBtb2RlOiAnc2hhZG93JyB8ICdoaWRlJyA9ICdzaGFkb3cnXG5cbiAgQENvbnRlbnRDaGlsZChUZW1wbGF0ZVJlZikgdGVtcGxhdGU/OiBUZW1wbGF0ZVJlZjxQZXJtaXNzaW9uR2F0ZUNvbnRleHQ+XG5cbn1cbiIsIjxuZy1jb250YWluZXIgKm5nSWY9XCJpc0FsbG93ZWRcIj5cbiAgPG5nLWNvbnRhaW5lciAqbmdUZW1wbGF0ZU91dGxldD1cInRlbXBsYXRlID8/IG51bGw7IGNvbnRleHQ6IHsgJGltcGxpY2l0OiB0cnVlIH1cIj48L25nLWNvbnRhaW5lcj5cbjwvbmctY29udGFpbmVyPlxuXG48bmctY29udGFpbmVyICpuZ0lmPVwiIWlzQWxsb3dlZCAmJiBtb2RlID09PSAnc2hhZG93J1wiPlxuICA8c3BhblxuICAgIGNsYXNzPVwicGVybWlzc2lvbi1nYXRlLWJsb2NrZWRcIlxuICAgIFttYXRUb29sdGlwXT1cIm5vQWNjZXNzTGFiZWxcIlxuICAgIFttYXRUb29sdGlwRGlzYWJsZWRdPVwiIW5vQWNjZXNzTGFiZWxcIj5cbiAgICA8bmctY29udGFpbmVyICpuZ1RlbXBsYXRlT3V0bGV0PVwidGVtcGxhdGUgPz8gbnVsbDsgY29udGV4dDogeyAkaW1wbGljaXQ6IGZhbHNlIH1cIj48L25nLWNvbnRhaW5lcj5cbiAgPC9zcGFuPlxuPC9uZy1jb250YWluZXI+XG4iXX0=
@@ -1,20 +1,26 @@
1
1
  import { NgModule } from '@angular/core';
2
2
  import { CommonModule } from '@angular/common';
3
3
  import { MatTooltipModule } from '@angular/material/tooltip';
4
+ import { OverlayModule } from '@angular/cdk/overlay';
4
5
  import { PermissionGateComponent } from './permission-gate/permission-gate.component';
5
6
  import { PermissionGateDirective } from './permission-gate/permission-gate.directive';
7
+ import { ApipassGateDirective } from './permission-gate/permission-gate-structural.directive';
6
8
  import { PermissionsService } from './service/permissions.service';
7
9
  import * as i0 from "@angular/core";
8
10
  class PermissionsModule {
9
11
  static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "16.0.2", ngImport: i0, type: PermissionsModule, deps: [], target: i0.ɵɵFactoryTarget.NgModule });
10
12
  static ɵmod = i0.ɵɵngDeclareNgModule({ minVersion: "14.0.0", version: "16.0.2", ngImport: i0, type: PermissionsModule, declarations: [PermissionGateComponent,
11
- PermissionGateDirective], imports: [CommonModule,
12
- MatTooltipModule], exports: [PermissionGateComponent,
13
- PermissionGateDirective] });
13
+ PermissionGateDirective,
14
+ ApipassGateDirective], imports: [CommonModule,
15
+ MatTooltipModule,
16
+ OverlayModule], exports: [PermissionGateComponent,
17
+ PermissionGateDirective,
18
+ ApipassGateDirective] });
14
19
  static ɵinj = i0.ɵɵngDeclareInjector({ minVersion: "12.0.0", version: "16.0.2", ngImport: i0, type: PermissionsModule, providers: [
15
20
  PermissionsService
16
21
  ], imports: [CommonModule,
17
- MatTooltipModule] });
22
+ MatTooltipModule,
23
+ OverlayModule] });
18
24
  }
19
25
  export { PermissionsModule };
20
26
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "16.0.2", ngImport: i0, type: PermissionsModule, decorators: [{
@@ -22,19 +28,22 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "16.0.2", ngImpor
22
28
  args: [{
23
29
  imports: [
24
30
  CommonModule,
25
- MatTooltipModule
31
+ MatTooltipModule,
32
+ OverlayModule
26
33
  ],
27
34
  declarations: [
28
35
  PermissionGateComponent,
29
- PermissionGateDirective
36
+ PermissionGateDirective,
37
+ ApipassGateDirective
30
38
  ],
31
39
  exports: [
32
40
  PermissionGateComponent,
33
- PermissionGateDirective
41
+ PermissionGateDirective,
42
+ ApipassGateDirective
34
43
  ],
35
44
  providers: [
36
45
  PermissionsService
37
46
  ]
38
47
  }]
39
48
  }] });
40
- //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoicGVybWlzc2lvbnMubW9kdWxlLmpzIiwic291cmNlUm9vdCI6IiIsInNvdXJjZXMiOlsiLi4vLi4vLi4vcHJvamVjdHMvcGVybWlzc2lvbnMvc3JjL3Blcm1pc3Npb25zLm1vZHVsZS50cyJdLCJuYW1lcyI6W10sIm1hcHBpbmdzIjoiQUFBQSxPQUFPLEVBQUUsUUFBUSxFQUFFLE1BQU0sZUFBZSxDQUFBO0FBQ3hDLE9BQU8sRUFBRSxZQUFZLEVBQUUsTUFBTSxpQkFBaUIsQ0FBQTtBQUM5QyxPQUFPLEVBQUUsZ0JBQWdCLEVBQUUsTUFBTSwyQkFBMkIsQ0FBQTtBQUM1RCxPQUFPLEVBQUUsdUJBQXVCLEVBQUUsTUFBTSw2Q0FBNkMsQ0FBQTtBQUNyRixPQUFPLEVBQUUsdUJBQXVCLEVBQUUsTUFBTSw2Q0FBNkMsQ0FBQTtBQUNyRixPQUFPLEVBQUUsa0JBQWtCLEVBQUUsTUFBTSwrQkFBK0IsQ0FBQTs7QUFFbEUsTUFpQmEsaUJBQWlCO3VHQUFqQixpQkFBaUI7d0dBQWpCLGlCQUFpQixpQkFYMUIsdUJBQXVCO1lBQ3ZCLHVCQUF1QixhQUx2QixZQUFZO1lBQ1osZ0JBQWdCLGFBT2hCLHVCQUF1QjtZQUN2Qix1QkFBdUI7d0dBTWQsaUJBQWlCLGFBSmpCO1lBQ1Qsa0JBQWtCO1NBQ25CLFlBYkMsWUFBWTtZQUNaLGdCQUFnQjs7U0FjUCxpQkFBaUI7MkZBQWpCLGlCQUFpQjtrQkFqQjdCLFFBQVE7bUJBQUM7b0JBQ1IsT0FBTyxFQUFFO3dCQUNQLFlBQVk7d0JBQ1osZ0JBQWdCO3FCQUNqQjtvQkFDRCxZQUFZLEVBQUU7d0JBQ1osdUJBQXVCO3dCQUN2Qix1QkFBdUI7cUJBQ3hCO29CQUNELE9BQU8sRUFBRTt3QkFDUCx1QkFBdUI7d0JBQ3ZCLHVCQUF1QjtxQkFDeEI7b0JBQ0QsU0FBUyxFQUFFO3dCQUNULGtCQUFrQjtxQkFDbkI7aUJBQ0YiLCJzb3VyY2VzQ29udGVudCI6WyJpbXBvcnQgeyBOZ01vZHVsZSB9IGZyb20gJ0Bhbmd1bGFyL2NvcmUnXG5pbXBvcnQgeyBDb21tb25Nb2R1bGUgfSBmcm9tICdAYW5ndWxhci9jb21tb24nXG5pbXBvcnQgeyBNYXRUb29sdGlwTW9kdWxlIH0gZnJvbSAnQGFuZ3VsYXIvbWF0ZXJpYWwvdG9vbHRpcCdcbmltcG9ydCB7IFBlcm1pc3Npb25HYXRlQ29tcG9uZW50IH0gZnJvbSAnLi9wZXJtaXNzaW9uLWdhdGUvcGVybWlzc2lvbi1nYXRlLmNvbXBvbmVudCdcbmltcG9ydCB7IFBlcm1pc3Npb25HYXRlRGlyZWN0aXZlIH0gZnJvbSAnLi9wZXJtaXNzaW9uLWdhdGUvcGVybWlzc2lvbi1nYXRlLmRpcmVjdGl2ZSdcbmltcG9ydCB7IFBlcm1pc3Npb25zU2VydmljZSB9IGZyb20gJy4vc2VydmljZS9wZXJtaXNzaW9ucy5zZXJ2aWNlJ1xuXG5ATmdNb2R1bGUoe1xuICBpbXBvcnRzOiBbXG4gICAgQ29tbW9uTW9kdWxlLFxuICAgIE1hdFRvb2x0aXBNb2R1bGVcbiAgXSxcbiAgZGVjbGFyYXRpb25zOiBbXG4gICAgUGVybWlzc2lvbkdhdGVDb21wb25lbnQsXG4gICAgUGVybWlzc2lvbkdhdGVEaXJlY3RpdmVcbiAgXSxcbiAgZXhwb3J0czogW1xuICAgIFBlcm1pc3Npb25HYXRlQ29tcG9uZW50LFxuICAgIFBlcm1pc3Npb25HYXRlRGlyZWN0aXZlXG4gIF0sXG4gIHByb3ZpZGVyczogW1xuICAgIFBlcm1pc3Npb25zU2VydmljZVxuICBdXG59KVxuZXhwb3J0IGNsYXNzIFBlcm1pc3Npb25zTW9kdWxlIHt9XG4iXX0=
49
+ //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoicGVybWlzc2lvbnMubW9kdWxlLmpzIiwic291cmNlUm9vdCI6IiIsInNvdXJjZXMiOlsiLi4vLi4vLi4vcHJvamVjdHMvcGVybWlzc2lvbnMvc3JjL3Blcm1pc3Npb25zLm1vZHVsZS50cyJdLCJuYW1lcyI6W10sIm1hcHBpbmdzIjoiQUFBQSxPQUFPLEVBQUUsUUFBUSxFQUFFLE1BQU0sZUFBZSxDQUFBO0FBQ3hDLE9BQU8sRUFBRSxZQUFZLEVBQUUsTUFBTSxpQkFBaUIsQ0FBQTtBQUM5QyxPQUFPLEVBQUUsZ0JBQWdCLEVBQUUsTUFBTSwyQkFBMkIsQ0FBQTtBQUM1RCxPQUFPLEVBQUUsYUFBYSxFQUFFLE1BQU0sc0JBQXNCLENBQUE7QUFDcEQsT0FBTyxFQUFFLHVCQUF1QixFQUFFLE1BQU0sNkNBQTZDLENBQUE7QUFDckYsT0FBTyxFQUFFLHVCQUF1QixFQUFFLE1BQU0sNkNBQTZDLENBQUE7QUFDckYsT0FBTyxFQUFFLG9CQUFvQixFQUFFLE1BQU0sd0RBQXdELENBQUE7QUFDN0YsT0FBTyxFQUFFLGtCQUFrQixFQUFFLE1BQU0sK0JBQStCLENBQUE7O0FBRWxFLE1Bb0JhLGlCQUFpQjt1R0FBakIsaUJBQWlCO3dHQUFqQixpQkFBaUIsaUJBYjFCLHVCQUF1QjtZQUN2Qix1QkFBdUI7WUFDdkIsb0JBQW9CLGFBUHBCLFlBQVk7WUFDWixnQkFBZ0I7WUFDaEIsYUFBYSxhQVFiLHVCQUF1QjtZQUN2Qix1QkFBdUI7WUFDdkIsb0JBQW9CO3dHQU1YLGlCQUFpQixhQUpqQjtZQUNULGtCQUFrQjtTQUNuQixZQWhCQyxZQUFZO1lBQ1osZ0JBQWdCO1lBQ2hCLGFBQWE7O1NBZ0JKLGlCQUFpQjsyRkFBakIsaUJBQWlCO2tCQXBCN0IsUUFBUTttQkFBQztvQkFDUixPQUFPLEVBQUU7d0JBQ1AsWUFBWTt3QkFDWixnQkFBZ0I7d0JBQ2hCLGFBQWE7cUJBQ2Q7b0JBQ0QsWUFBWSxFQUFFO3dCQUNaLHVCQUF1Qjt3QkFDdkIsdUJBQXVCO3dCQUN2QixvQkFBb0I7cUJBQ3JCO29CQUNELE9BQU8sRUFBRTt3QkFDUCx1QkFBdUI7d0JBQ3ZCLHVCQUF1Qjt3QkFDdkIsb0JBQW9CO3FCQUNyQjtvQkFDRCxTQUFTLEVBQUU7d0JBQ1Qsa0JBQWtCO3FCQUNuQjtpQkFDRiIsInNvdXJjZXNDb250ZW50IjpbImltcG9ydCB7IE5nTW9kdWxlIH0gZnJvbSAnQGFuZ3VsYXIvY29yZSdcbmltcG9ydCB7IENvbW1vbk1vZHVsZSB9IGZyb20gJ0Bhbmd1bGFyL2NvbW1vbidcbmltcG9ydCB7IE1hdFRvb2x0aXBNb2R1bGUgfSBmcm9tICdAYW5ndWxhci9tYXRlcmlhbC90b29sdGlwJ1xuaW1wb3J0IHsgT3ZlcmxheU1vZHVsZSB9IGZyb20gJ0Bhbmd1bGFyL2Nkay9vdmVybGF5J1xuaW1wb3J0IHsgUGVybWlzc2lvbkdhdGVDb21wb25lbnQgfSBmcm9tICcuL3Blcm1pc3Npb24tZ2F0ZS9wZXJtaXNzaW9uLWdhdGUuY29tcG9uZW50J1xuaW1wb3J0IHsgUGVybWlzc2lvbkdhdGVEaXJlY3RpdmUgfSBmcm9tICcuL3Blcm1pc3Npb24tZ2F0ZS9wZXJtaXNzaW9uLWdhdGUuZGlyZWN0aXZlJ1xuaW1wb3J0IHsgQXBpcGFzc0dhdGVEaXJlY3RpdmUgfSBmcm9tICcuL3Blcm1pc3Npb24tZ2F0ZS9wZXJtaXNzaW9uLWdhdGUtc3RydWN0dXJhbC5kaXJlY3RpdmUnXG5pbXBvcnQgeyBQZXJtaXNzaW9uc1NlcnZpY2UgfSBmcm9tICcuL3NlcnZpY2UvcGVybWlzc2lvbnMuc2VydmljZSdcblxuQE5nTW9kdWxlKHtcbiAgaW1wb3J0czogW1xuICAgIENvbW1vbk1vZHVsZSxcbiAgICBNYXRUb29sdGlwTW9kdWxlLFxuICAgIE92ZXJsYXlNb2R1bGVcbiAgXSxcbiAgZGVjbGFyYXRpb25zOiBbXG4gICAgUGVybWlzc2lvbkdhdGVDb21wb25lbnQsXG4gICAgUGVybWlzc2lvbkdhdGVEaXJlY3RpdmUsXG4gICAgQXBpcGFzc0dhdGVEaXJlY3RpdmVcbiAgXSxcbiAgZXhwb3J0czogW1xuICAgIFBlcm1pc3Npb25HYXRlQ29tcG9uZW50LFxuICAgIFBlcm1pc3Npb25HYXRlRGlyZWN0aXZlLFxuICAgIEFwaXBhc3NHYXRlRGlyZWN0aXZlXG4gIF0sXG4gIHByb3ZpZGVyczogW1xuICAgIFBlcm1pc3Npb25zU2VydmljZVxuICBdXG59KVxuZXhwb3J0IGNsYXNzIFBlcm1pc3Npb25zTW9kdWxlIHt9XG4iXX0=
@@ -1,9 +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';
6
7
  export * from './service/permissions.providers';
7
8
  export * from './model/permission-check.interface';
8
9
  export * from './model/permission-result.interface';
9
- //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoicHVibGljLWFwaS5qcyIsInNvdXJjZVJvb3QiOiIiLCJzb3VyY2VzIjpbIi4uLy4uLy4uL3Byb2plY3RzL3Blcm1pc3Npb25zL3NyYy9wdWJsaWMtYXBpLnRzIl0sIm5hbWVzIjpbXSwibWFwcGluZ3MiOiJBQUFBLGNBQWMsc0JBQXNCLENBQUE7QUFDcEMsY0FBYyw2Q0FBNkMsQ0FBQTtBQUMzRCxjQUFjLDZDQUE2QyxDQUFBO0FBQzNELGNBQWMsK0JBQStCLENBQUE7QUFDN0MsY0FBYyw4QkFBOEIsQ0FBQTtBQUM1QyxjQUFjLGlDQUFpQyxDQUFBO0FBQy9DLGNBQWMsb0NBQW9DLENBQUE7QUFDbEQsY0FBYyxxQ0FBcUMsQ0FBQSIsInNvdXJjZXNDb250ZW50IjpbImV4cG9ydCAqIGZyb20gJy4vcGVybWlzc2lvbnMubW9kdWxlJ1xuZXhwb3J0ICogZnJvbSAnLi9wZXJtaXNzaW9uLWdhdGUvcGVybWlzc2lvbi1nYXRlLmNvbXBvbmVudCdcbmV4cG9ydCAqIGZyb20gJy4vcGVybWlzc2lvbi1nYXRlL3Blcm1pc3Npb24tZ2F0ZS5kaXJlY3RpdmUnXG5leHBvcnQgKiBmcm9tICcuL3NlcnZpY2UvcGVybWlzc2lvbnMuc2VydmljZSdcbmV4cG9ydCAqIGZyb20gJy4vc2VydmljZS9wZXJtaXNzaW9ucy5jb25maWcnXG5leHBvcnQgKiBmcm9tICcuL3NlcnZpY2UvcGVybWlzc2lvbnMucHJvdmlkZXJzJ1xuZXhwb3J0ICogZnJvbSAnLi9tb2RlbC9wZXJtaXNzaW9uLWNoZWNrLmludGVyZmFjZSdcbmV4cG9ydCAqIGZyb20gJy4vbW9kZWwvcGVybWlzc2lvbi1yZXN1bHQuaW50ZXJmYWNlJ1xuIl19
10
+ //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoicHVibGljLWFwaS5qcyIsInNvdXJjZVJvb3QiOiIiLCJzb3VyY2VzIjpbIi4uLy4uLy4uL3Byb2plY3RzL3Blcm1pc3Npb25zL3NyYy9wdWJsaWMtYXBpLnRzIl0sIm5hbWVzIjpbXSwibWFwcGluZ3MiOiJBQUFBLGNBQWMsc0JBQXNCLENBQUE7QUFDcEMsY0FBYyw2Q0FBNkMsQ0FBQTtBQUMzRCxjQUFjLDZDQUE2QyxDQUFBO0FBQzNELGNBQWMsd0RBQXdELENBQUE7QUFDdEUsY0FBYywrQkFBK0IsQ0FBQTtBQUM3QyxjQUFjLDhCQUE4QixDQUFBO0FBQzVDLGNBQWMsaUNBQWlDLENBQUE7QUFDL0MsY0FBYyxvQ0FBb0MsQ0FBQTtBQUNsRCxjQUFjLHFDQUFxQyxDQUFBIiwic291cmNlc0NvbnRlbnQiOlsiZXhwb3J0ICogZnJvbSAnLi9wZXJtaXNzaW9ucy5tb2R1bGUnXG5leHBvcnQgKiBmcm9tICcuL3Blcm1pc3Npb24tZ2F0ZS9wZXJtaXNzaW9uLWdhdGUuY29tcG9uZW50J1xuZXhwb3J0ICogZnJvbSAnLi9wZXJtaXNzaW9uLWdhdGUvcGVybWlzc2lvbi1nYXRlLmRpcmVjdGl2ZSdcbmV4cG9ydCAqIGZyb20gJy4vcGVybWlzc2lvbi1nYXRlL3Blcm1pc3Npb24tZ2F0ZS1zdHJ1Y3R1cmFsLmRpcmVjdGl2ZSdcbmV4cG9ydCAqIGZyb20gJy4vc2VydmljZS9wZXJtaXNzaW9ucy5zZXJ2aWNlJ1xuZXhwb3J0ICogZnJvbSAnLi9zZXJ2aWNlL3Blcm1pc3Npb25zLmNvbmZpZydcbmV4cG9ydCAqIGZyb20gJy4vc2VydmljZS9wZXJtaXNzaW9ucy5wcm92aWRlcnMnXG5leHBvcnQgKiBmcm9tICcuL21vZGVsL3Blcm1pc3Npb24tY2hlY2suaW50ZXJmYWNlJ1xuZXhwb3J0ICogZnJvbSAnLi9tb2RlbC9wZXJtaXNzaW9uLXJlc3VsdC5pbnRlcmZhY2UnXG4iXX0=
@@ -30,6 +30,35 @@ class PermissionsService {
30
30
  const map = await this.checkResources([check]);
31
31
  return map.for(check.kind);
32
32
  }
33
+ /**
34
+ * Whether the principal has access to **at least one** resource of `kind` for `action`,
35
+ * without naming a specific resource id. Use this instead of {@link checkResource} for
36
+ * kinds whose Cerbos policies can scope access by resource id (e.g. `R.id in [...]`) —
37
+ * {@link checkResource} would evaluate a synthetic id that never matches such a condition
38
+ * and incorrectly deny principals that do have access to real resources.
39
+ */
40
+ async hasAnyAccess(kind, action) {
41
+ const cerbosEnabled = await this.config.cerbosEnabled();
42
+ return cerbosEnabled
43
+ ? await this.resolveAnyAccessWithCerbos(kind, action)
44
+ : await this.config.legacyResolver({ kind, action });
45
+ }
46
+ async resolveAnyAccessWithCerbos(kind, action) {
47
+ // Fail-closed: no PDP wired in means no grants.
48
+ if (!this.cerbos) {
49
+ console.error(`${PermissionsService.name} - CERBOS_SERVICE not provided; denying access (fail-closed)`);
50
+ return false;
51
+ }
52
+ try {
53
+ const userInfo = await this.config.userInfo();
54
+ const result = await this.cerbos.planResources(userInfo, { kind, action });
55
+ return result.filter.kind !== 'KIND_ALWAYS_DENIED';
56
+ }
57
+ catch (e) {
58
+ console.error(`${PermissionsService.name} - Cerbos planResources failed; denying access (fail-closed)`, e);
59
+ return false;
60
+ }
61
+ }
33
62
  async resolveWithCerbos(checks) {
34
63
  // Fail-closed: no PDP wired in means no grants.
35
64
  if (!this.cerbos) {
@@ -87,4 +116,4 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "16.0.2", ngImpor
87
116
  type: Inject,
88
117
  args: [CERBOS_SERVICE]
89
118
  }] }]; } });
90
- //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoicGVybWlzc2lvbnMuc2VydmljZS5qcyIsInNvdXJjZVJvb3QiOiIiLCJzb3VyY2VzIjpbIi4uLy4uLy4uLy4uL3Byb2plY3RzL3Blcm1pc3Npb25zL3NyYy9zZXJ2aWNlL3Blcm1pc3Npb25zLnNlcnZpY2UudHMiXSwibmFtZXMiOltdLCJtYXBwaW5ncyI6IkFBQUEsT0FBTyxFQUFFLE1BQU0sRUFBRSxVQUFVLEVBQUUsUUFBUSxFQUFFLE1BQU0sZUFBZSxDQUFBO0FBQzVELE9BQU8sRUFBRSxjQUFjLEVBQWtCLE1BQU0seUJBQXlCLENBQUE7QUFDeEUsT0FBTyxFQUFFLGtCQUFrQixFQUFxQixNQUFNLHNCQUFzQixDQUFBOztBQU01RTs7Ozs7OztHQU9HO0FBQ0gsTUFDYSxrQkFBa0I7SUFHa0I7SUFDUTtJQUZ2RCxZQUMrQyxNQUF5QixFQUNqQixNQUE2QjtRQURyQyxXQUFNLEdBQU4sTUFBTSxDQUFtQjtRQUNqQixXQUFNLEdBQU4sTUFBTSxDQUF1QjtJQUNqRixDQUFDO0lBRUosaUdBQWlHO0lBQ2pHLEtBQUssQ0FBQyxjQUFjLENBQUMsTUFBeUI7UUFDNUMsTUFBTSxhQUFhLEdBQUcsTUFBTSxJQUFJLENBQUMsTUFBTSxDQUFDLGFBQWEsRUFBRSxDQUFBO1FBQ3ZELE1BQU0sU0FBUyxHQUFHLGFBQWE7WUFDN0IsQ0FBQyxDQUFDLE1BQU0sSUFBSSxDQUFDLGlCQUFpQixDQUFDLE1BQU0sQ0FBQztZQUN0QyxDQUFDLENBQUMsTUFBTSxJQUFJLENBQUMsaUJBQWlCLENBQUMsTUFBTSxDQUFDLENBQUE7UUFDeEMsT0FBTyxJQUFJLENBQUMsY0FBYyxDQUFDLFNBQVMsQ0FBQyxDQUFBO0lBQ3ZDLENBQUM7SUFFRCxtREFBbUQ7SUFDbkQsS0FBSyxDQUFDLGFBQWEsQ0FBQyxLQUFzQjtRQUN4QyxNQUFNLEdBQUcsR0FBRyxNQUFNLElBQUksQ0FBQyxjQUFjLENBQUMsQ0FBQyxLQUFLLENBQUMsQ0FBQyxDQUFBO1FBQzlDLE9BQU8sR0FBRyxDQUFDLEdBQUcsQ0FB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LFVBQVU7OzBCQUlOLE1BQU07MkJBQUMsa0JBQWtCOzswQkFDekIsUUFBUTs7MEJBQUksTUFBTTsyQkFBQyxjQUFjIiwic291cmNlc0NvbnRlbnQiOlsiaW1wb3J0IHsgSW5qZWN0LCBJbmplY3RhYmxlLCBPcHRpb25hbCB9IGZyb20gJ0Bhbmd1bGFyL2NvcmUnXG5pbXBvcnQgeyBDRVJCT1NfU0VSVklDRSwgSUNlcmJvc1NlcnZpY2UgfSBmcm9tICdAYXBpcGFzcy9mcm9udGVuZC11dGlscydcbmltcG9ydCB7IFBFUk1JU1NJT05TX0NPTkZJRywgUGVybWlzc2lvbnNDb25maWcgfSBmcm9tICcuL3Blcm1pc3Npb25zLmNvbmZpZydcbmltcG9ydCB7IFBlcm1pc3Npb25DaGVjayB9IGZyb20gJy4uL21vZGVsL3Blcm1pc3Npb24tY2hlY2suaW50ZXJmYWNlJ1xuaW1wb3J0IHsgUGVybWlzc2lvblJlc3VsdCwgUGVybWlzc2lvblJlc3VsdE1hcCB9IGZyb20gJy4uL21vZGVsL3Blcm1pc3Npb24tcmVzdWx0LmludGVyZmFjZSdcblxudHlwZSBEZWNpc2lvbk1hcCA9IFJlY29yZDxzdHJpbmcsIFJlY29yZDxzdHJpbmcsIGJvb2xlYW4+PlxuXG4vKipcbiAqIEh5YnJpZCBhdXRob3JpemF0aW9uIG9yY2hlc3RyYXRvcjogZGVjaWRlcyBiZXR3ZWVuIENlcmJvcyBhbmQgdGhlIGxlZ2FjeSByb2xlXG4gKiBtb2RlbCBiYXNlZCBvbiB7QGxpbmsgUGVybWlzc2lvbnNDb25maWcuY2VyYm9zRW5hYmxlZH0uXG4gKlxuICogLSAqKkNlcmJvcyBkaXNhYmxlZCoqIOKGkiBiZWhhdmlvciBpcyBpZGVudGljYWwgdG8gdGhlIHByZS1DZXJib3Mgc3lzdGVtIChsZWdhY3kgcmVzb2x2ZXIpLlxuICogLSAqKkNlcmJvcyBlbmFibGVkKiog4oaSIGFza3MgdGhlIFBEUDsgb24gYW55IGZhaWx1cmUgaXQgaXMgKipmYWlsLWNsb3NlZCoqIChldmVyeSBhY3Rpb24gZGVuaWVkKSxcbiAqICAgc28gYSBQRFAgb3V0YWdlIG5ldmVyIHNpbGVudGx5IGdyYW50cyBhY2Nlc3MuXG4gKi9cbkBJbmplY3RhYmxlKClcbmV4cG9ydCBjbGFzcyBQZXJtaXNzaW9uc1NlcnZpY2Uge1xuXG4gIGNvbnN0cnVjdG9yKFxuICAgIEBJbmplY3QoUEVSTUlTU0lPTlNfQ09ORklHKSBwcml2YXRlIHJlYWRvbmx5IGNvbmZpZzogUGVybWlzc2lvbnNDb25maWcsXG4gICAgQE9wdGlvbmFsKCkgQEluamVjdChDRVJCT1NfU0VSVklDRSkgcHJpdmF0ZSByZWFkb25seSBjZXJib3M6IElDZXJib3NTZXJ2aWNlIHwgbnVsbFxuICApIHt9XG5cbiAgLyoqIEV2YWx1YXRlcyBzZXZlcmFsIHJlc291cmNlIGtpbmRzIGF0IG9uY2UuIFdpdGggQ2VyYm9zIGVuYWJsZWQgdGhpcyBpcyBhIHNpbmdsZSByb3VuZC10cmlwLiAqL1xuICBhc3luYyBjaGVja1Jlc291cmNlcyhjaGVja3M6IFBlcm1pc3Npb25DaGVja1tdKTogUHJvbWlzZTxQZXJtaXNzaW9uUmVzdWx0TWFwPiB7XG4gICAgY29uc3QgY2VyYm9zRW5hYmxlZCA9IGF3YWl0IHRoaXMuY29uZmlnLmNlcmJvc0VuYWJsZWQoKVxuICAgIGNvbnN0IGRlY2lzaW9ucyA9IGNlcmJvc0VuYWJsZWRcbiAgICAgID8gYXdhaXQgdGhpcy5yZXNvbHZlV2l0aENlcmJvcyhjaGVja3MpXG4gICAgICA6IGF3YWl0IHRoaXMucmVzb2x2ZVdpdGhMZWdhY3koY2hlY2tzKVxuICAgIHJldHVybiB0aGlzLmJ1aWxkUmVzdWx0TWFwKGRlY2lzaW9ucylcbiAgfVxuXG4gIC8qKiBDb252ZW5pZW5jZSBmb3IgdGhlIGNvbW1vbiBzaW5nbGUta2luZCBjYXNlLiAqL1xuICBhc3luYyBjaGVja1Jlc291cmNlKGNoZWNrOiBQZXJtaXNzaW9uQ2hlY2spOiBQcm9taXNlPFBlcm1pc3Npb25SZXN1bHQ+IHtcbiAgICBjb25zdCBtYXAgPSBhd2FpdCB0aGlzLmNoZWNrUmVzb3VyY2VzKFtjaGVja10pXG4gICAgcmV0dXJuIG1hcC5mb3IoY2hlY2sua2luZClcbiAgfVxuXG4gIHByaXZhdGUgYXN5bmMgcmVzb2x2ZVdpdGhDZXJib3MoY2hlY2tzOiBQZXJtaXNzaW9uQ2hlY2tbXSk6IFByb21pc2U8RGVjaXNpb25NYXA+IHtcbiAgICAvLyBGYWlsLWNsb3NlZDogbm8gUERQIHdpcmVkIGluIG1lYW5zIG5vIGdyYW50cy5cbiAgICBpZiAoIXRoaXMuY2VyYm9zKSB7XG4gICAgICBjb25zb2xlLmVycm9yKGAke1Blcm1pc3Npb25zU2VydmljZS5uYW1lfSAtIENFUkJPU19TRVJWSUNFIG5vdCBwcm92aWRlZDsgZGVueWluZyBhbGwgKGZhaWwtY2xvc2VkKWApXG4gICAgICByZXR1cm4gdGhpcy5kZW55QWxsKGNoZWNrcylcbiAgICB9XG4gICAgdHJ5IHtcbiAgICAgIGNvbnN0IHVzZXJJbmZvID0gYXdhaXQgdGhpcy5jb25maWcudXNlckluZm8oKVxuICAgICAgcmV0dXJuIGF3YWl0IHRoaXMuY2VyYm9zLmNoZWNrUmVzb3VyY2VzKFxuICAgICAgICB1c2VySW5mbyxcbiAgICAgICAgY2hlY2tzLm1hcCgoeyBraW5kLCBhY3Rpb25zLCBpZCB9KSA9PiAoeyBraW5kLCBhY3Rpb25zLCBpZCB9KSlcbiAgICAgIClcbiAgICB9IGNhdGNoIChlKSB7XG4gICAgICBjb25zb2xlLmVycm9yKGAke1Blcm1pc3Npb25zU2VydmljZS5uYW1lfSAtIENlcmJvcyBjaGVjayBmYWlsZWQ7IGRlbnlpbmcgYWxsIChmYWlsLWNsb3NlZClgLCBlKVxuICAgICAgcmV0dXJuIHRoaXMuZGVueUFsbChjaGVja3MpXG4gICAgfVxuICB9XG5cbiAgcHJpdmF0ZSBhc3luYyByZXNvbHZlV2l0aExlZ2FjeShjaGVja3M6IFBlcm1pc3Npb25DaGVja1tdKTogUHJvbWlzZTxEZWNpc2lvbk1hcD4ge1xuICAgIGNvbnN0IGRlY2lzaW9uczogRGVjaXNpb25NYXAgPSB7fVxuICAgIGZvciAoY29uc3QgY2hlY2sgb2YgY2hlY2tzKSB7XG4gICAgICBjb25zdCBhY3Rpb25zOiBSZWNvcmQ8c3RyaW5nLCBib29sZWFuPiA9IHt9XG4gICAgICBmb3IgKGNvbnN0IGFjdGlvbiBvZiBjaGVjay5hY3Rpb25zKSB7XG4gICAgICAgIGFjdGlvbnNbYWN0aW9uXSA9IGF3YWl0IHRoaXMuY29uZmlnLmxlZ2FjeVJlc29sdmVyKHsga2luZDogY2hlY2sua2luZCwgYWN0aW9uIH0sIGNoZWNrLmxlZ2FjeUNvbnRleHQpXG4gICAgICB9XG4gICAgICBkZWNpc2lvbnNbY2hlY2sua2luZF0gPSBhY3Rpb25zXG4gICAgfVxuICAgIHJldHVybiBkZWNpc2lvbnNcbiAgfVxuXG4gIHByaXZhdGUgZGVueUFsbChjaGVja3M6IFBlcm1pc3Npb25DaGVja1tdKTogRGVjaXNpb25NYXAge1xuICAgIHJldHVybiBjaGVja3MucmVkdWNlPERlY2lzaW9uTWFwPigoYWNjLCBjaGVjaykgPT4ge1xuICAgICAgYWNjW2NoZWNrLmtpbmRdID0gY2hlY2suYWN0aW9ucy5yZWR1Y2U8UmVjb3JkPHN0cmluZywgYm9vbGVhbj4+KChhY3Rpb25zLCBhY3Rpb24pID0+IHtcbiAgICAgICAgYWN0aW9uc1thY3Rpb25dID0gZmFsc2VcbiAgICAgICAgcmV0dXJuIGFjdGlvbnNcbiAgICAgIH0sIHt9KVxuICAgICAgcmV0dXJuIGFjY1xuICAgIH0sIHt9KVxuICB9XG5cbiAgcHJpdmF0ZSBidWlsZFJlc3VsdE1hcChkZWNpc2lvbnM6IERlY2lzaW9uTWFwKTogUGVybWlzc2lvblJlc3VsdE1hcCB7XG4gICAgY29uc3QgY2FuID0gKGtpbmQ6IHN0cmluZywgYWN0aW9uOiBzdHJpbmcpOiBib29sZWFuID0+IGRlY2lzaW9uc1traW5kXT8uW2FjdGlvbl0gPz8gZmFsc2VcbiAgICByZXR1cm4ge1xuICAgICAgY2FuLFxuICAgICAgZm9yOiAoa2luZDogc3RyaW5nKTogUGVybWlzc2lvblJlc3VsdCA9PiAoeyBjYW46IChhY3Rpb246IHN0cmluZykgPT4gY2FuKGtpbmQsIGFjdGlvbikgfSlcbiAgICB9XG4gIH1cblxufVxuIl19
119
+ //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoicGVybWlzc2lvbnMuc2VydmljZS5qcyIsInNvdXJjZVJvb3QiOiIiLCJzb3VyY2VzIjpbIi4uLy4uLy4uLy4uL3Byb2plY3RzL3Blcm1pc3Npb25zL3NyYy9zZXJ2aWNlL3Blcm1pc3Npb25zLnNlcnZpY2UudHMiXSwibmFtZXMiOltdLCJtYXBwaW5ncyI6IkFBQUEsT0FBTyxFQUFFLE1BQU0sRUFBRSxVQUFVLEVBQUUsUUFBUSxFQUFFLE1BQU0sZUFBZSxDQUFBO0FBQzVELE9BQU8sRUFBRSxjQUFjLEVBQWtCLE1BQU0seUJBQXlCLENBQUE7QUFDeEUsT0FBTyxFQUFFLGtCQUFrQixFQUFxQixNQUFNLHNCQUFzQixDQUFBOztBQU01RTs7Ozs7OztHQU9HO0FBQ0gsTUFDYSxrQkFBa0I7SUFHa0I7SUFDUTtJQUZ2RCxZQUMrQyxNQUF5QixFQUNqQixNQUE2QjtRQURyQyxXQUFNLEdBQU4sTUFBTSxDQUFtQjtRQUNqQixXQUFNLEdBQU4sTUFBTSxDQUF1QjtJQUNqRixDQUFDO0lBRUosaUdBQWlHO0lBQ2pHLEtBQUssQ0FBQyxjQUFjLENBQUMsTUFBeUI7UUFDNUMsTUFBTSxhQUFhLEdBQUcsTUFBTSxJQUFJLENBQUMsTUFBTSxDQUFDLGFBQWEsRUFBRSxDQUFBO1FBQ3ZELE1BQU0sU0FBUyxHQUFHLGFBQWE7WUFDN0IsQ0FBQyxDQUFDLE1BQU0sSUFBSSxDQUFDLGlCQUFpQixDQUFDLE1BQU0sQ0FBQztZQUN0QyxDQUFDLENBQUMsTUFBTSxJQUFJLENBQUMsaUJBQWlCLENBQUMsTUFBTSxDQUFDLENBQUE7UUFDeEMsT0FBTyxJQUFJLENBQUMsY0FBYyxDQUFDLFNBQVMsQ0FBQyxDQUFBO0lBQ3ZDLENBQUM7SUFFRCxtREFBbUQ7SUFDbkQsS0FBSyxDQUFDLGFBQWEsQ0FBQyxLQUFzQjtRQUN4QyxNQUFNLEdBQUcsR0FBRyxNQUFNLElBQUksQ0FBQyxjQUFjLENBQUMsQ0FBQyxLQUFLLENBQUMsQ0FBQyxDQUFBO1FBQzlDLE9BQU8sR0FBRyxDQUFDLEdBQUcsQ0FB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Iiwic291cmNlc0NvbnRlbnQiOlsiaW1wb3J0IHsgSW5qZWN0LCBJbmplY3RhYmxlLCBPcHRpb25hbCB9IGZyb20gJ0Bhbmd1bGFyL2NvcmUnXG5pbXBvcnQgeyBDRVJCT1NfU0VSVklDRSwgSUNlcmJvc1NlcnZpY2UgfSBmcm9tICdAYXBpcGFzcy9mcm9udGVuZC11dGlscydcbmltcG9ydCB7IFBFUk1JU1NJT05TX0NPTkZJRywgUGVybWlzc2lvbnNDb25maWcgfSBmcm9tICcuL3Blcm1pc3Npb25zLmNvbmZpZydcbmltcG9ydCB7IFBlcm1pc3Npb25DaGVjayB9IGZyb20gJy4uL21vZGVsL3Blcm1pc3Npb24tY2hlY2suaW50ZXJmYWNlJ1xuaW1wb3J0IHsgUGVybWlzc2lvblJlc3VsdCwgUGVybWlzc2lvblJlc3VsdE1hcCB9IGZyb20gJy4uL21vZGVsL3Blcm1pc3Npb24tcmVzdWx0LmludGVyZmFjZSdcblxudHlwZSBEZWNpc2lvbk1hcCA9IFJlY29yZDxzdHJpbmcsIFJlY29yZDxzdHJpbmcsIGJvb2xlYW4+PlxuXG4vKipcbiAqIEh5YnJpZCBhdXRob3JpemF0aW9uIG9yY2hlc3RyYXRvcjogZGVjaWRlcyBiZXR3ZWVuIENlcmJvcyBhbmQgdGhlIGxlZ2FjeSByb2xlXG4gKiBtb2RlbCBiYXNlZCBvbiB7QGxpbmsgUGVybWlzc2lvbnNDb25maWcuY2VyYm9zRW5hYmxlZH0uXG4gKlxuICogLSAqKkNlcmJvcyBkaXNhYmxlZCoqIOKGkiBiZWhhdmlvciBpcyBpZGVudGljYWwgdG8gdGhlIHByZS1DZXJib3Mgc3lzdGVtIChsZWdhY3kgcmVzb2x2ZXIpLlxuICogLSAqKkNlcmJvcyBlbmFibGVkKiog4oaSIGFza3MgdGhlIFBEUDsgb24gYW55IGZhaWx1cmUgaXQgaXMgKipmYWlsLWNsb3NlZCoqIChldmVyeSBhY3Rpb24gZGVuaWVkKSxcbiAqICAgc28gYSBQRFAgb3V0YWdlIG5ldmVyIHNpbGVudGx5IGdyYW50cyBhY2Nlc3MuXG4gKi9cbkBJbmplY3RhYmxlKClcbmV4cG9ydCBjbGFzcyBQZXJtaXNzaW9uc1NlcnZpY2Uge1xuXG4gIGNvbnN0cnVjdG9yKFxuICAgIEBJbmplY3QoUEVSTUlTU0lPTlNfQ09ORklHKSBwcml2YXRlIHJlYWRvbmx5IGNvbmZpZzogUGVybWlzc2lvbnNDb25maWcsXG4gICAgQE9wdGlvbmFsKCkgQEluamVjdChDRVJCT1NfU0VSVklDRSkgcHJpdmF0ZSByZWFkb25seSBjZXJib3M6IElDZXJib3NTZXJ2aWNlIHwgbnVsbFxuICApIHt9XG5cbiAgLyoqIEV2YWx1YXRlcyBzZXZlcmFsIHJlc291cmNlIGtpbmRzIGF0IG9uY2UuIFdpdGggQ2VyYm9zIGVuYWJsZWQgdGhpcyBpcyBhIHNpbmdsZSByb3VuZC10cmlwLiAqL1xuICBhc3luYyBjaGVja1Jlc291cmNlcyhjaGVja3M6IFBlcm1pc3Npb25DaGVja1tdKTogUHJvbWlzZTxQZXJtaXNzaW9uUmVzdWx0TWFwPiB7XG4gICAgY29uc3QgY2VyYm9zRW5hYmxlZCA9IGF3YWl0IHRoaXMuY29uZmlnLmNlcmJvc0VuYWJsZWQoKVxuICAgIGNvbnN0IGRlY2lzaW9ucyA9IGNlcmJvc0VuYWJsZWRcbiAgICAgID8gYXdhaXQgdGhpcy5yZXNvbHZlV2l0aENlcmJvcyhjaGVja3MpXG4gICAgICA6IGF3YWl0IHRoaXMucmVzb2x2ZVdpdGhMZWdhY3koY2hlY2tzKVxuICAgIHJldHVybiB0aGlzLmJ1aWxkUmVzdWx0TWFwKGRlY2lzaW9ucylcbiAgfVxuXG4gIC8qKiBDb252ZW5pZW5jZSBmb3IgdGhlIGNvbW1vbiBzaW5nbGUta2luZCBjYXNlLiAqL1xuICBhc3luYyBjaGVja1Jlc291cmNlKGNoZWNrOiBQZXJtaXNzaW9uQ2hlY2spOiBQcm9taXNlPFBlcm1pc3Npb25SZXN1bHQ+IHtcbiAgICBjb25zdCBtYXAgPSBhd2FpdCB0aGlzLmNoZWNrUmVzb3VyY2VzKFtjaGVja10pXG4gICAgcmV0dXJuIG1hcC5mb3IoY2hlY2sua2luZClcbiAgfVxuXG4gIC8qKlxuICAgKiBXaGV0aGVyIHRoZSBwcmluY2lwYWwgaGFzIGFjY2VzcyB0byAqKmF0IGxlYXN0IG9uZSoqIHJlc291cmNlIG9mIGBraW5kYCBmb3IgYGFjdGlvbmAsXG4gICAqIHdpdGhvdXQgbmFtaW5nIGEgc3BlY2lmaWMgcmVzb3VyY2UgaWQuIFVzZSB0aGlzIGluc3RlYWQgb2Yge0BsaW5rIGNoZWNrUmVzb3VyY2V9IGZvclxuICAgKiBraW5kcyB3aG9zZSBDZXJib3MgcG9saWNpZXMgY2FuIHNjb3BlIGFjY2VzcyBieSByZXNvdXJjZSBpZCAoZS5nLiBgUi5pZCBpbiBbLi4uXWApIOKAlFxuICAgKiB7QGxpbmsgY2hlY2tSZXNvdXJjZX0gd291bGQgZXZhbHVhdGUgYSBzeW50aGV0aWMgaWQgdGhhdCBuZXZlciBtYXRjaGVzIHN1Y2ggYSBjb25kaXRpb25cbiAgICogYW5kIGluY29ycmVjdGx5IGRlbnkgcHJpbmNpcGFscyB0aGF0IGRvIGhhdmUgYWNjZXNzIHRvIHJlYWwgcmVzb3VyY2VzLlxuICAgKi9cbiAgYXN5bmMgaGFzQW55QWNjZXNzKGtpbmQ6IHN0cmluZywgYWN0aW9uOiBzdHJpbmcpOiBQcm9taXNlPGJvb2xlYW4+IHtcbiAgICBjb25zdCBjZXJib3NFbmFibGVkID0gYXdhaXQgdGhpcy5jb25maWcuY2VyYm9zRW5hYmxlZCgpXG4gICAgcmV0dXJuIGNlcmJvc0VuYWJsZWRcbiAgICAgID8gYXdhaXQgdGhpcy5yZXNvbHZlQW55QWNjZXNzV2l0aENlcmJvcyhraW5kLCBhY3Rpb24pXG4gICAgICA6IGF3YWl0IHRoaXMuY29uZmlnLmxlZ2FjeVJlc29sdmVyKHsga2luZCwgYWN0aW9uIH0pXG4gIH1cblxuICBwcml2YXRlIGFzeW5jIHJlc29sdmVBbnlBY2Nlc3NXaXRoQ2VyYm9zKGtpbmQ6IHN0cmluZywgYWN0aW9uOiBzdHJpbmcpOiBQcm9taXNlPGJvb2xlYW4+IHtcbiAgICAvLyBGYWlsLWNsb3NlZDogbm8gUERQIHdpcmVkIGluIG1lYW5zIG5vIGdyYW50cy5cbiAgICBpZiAoIXRoaXMuY2VyYm9zKSB7XG4gICAgICBjb25zb2xlLmVycm9yKGAke1Blcm1pc3Npb25zU2VydmljZS5uYW1lfSAtIENFUkJPU19TRVJWSUNFIG5vdCBwcm92aWRlZDsgZGVueWluZyBhY2Nlc3MgKGZhaWwtY2xvc2VkKWApXG4gICAgICByZXR1cm4gZmFsc2VcbiAgICB9XG4gICAgdHJ5IHtcbiAgICAgIGNvbnN0IHVzZXJJbmZvID0gYXdhaXQgdGhpcy5jb25maWcudXNlckluZm8oKVxuICAgICAgY29uc3QgcmVzdWx0ID0gYXdhaXQgdGhpcy5jZXJib3MucGxhblJlc291cmNlcyh1c2VySW5mbywgeyBraW5kLCBhY3Rpb24gfSlcbiAgICAgIHJldHVybiByZXN1bHQuZmlsdGVyLmtpbmQgIT09ICdLSU5EX0FMV0FZU19ERU5JRUQnXG4gICAgfSBjYXRjaCAoZSkge1xuICAgICAgY29uc29sZS5lcnJvcihgJHtQZXJtaXNzaW9uc1NlcnZpY2UubmFtZX0gLSBDZXJib3MgcGxhblJlc291cmNlcyBmYWlsZWQ7IGRlbnlpbmcgYWNjZXNzIChmYWlsLWNsb3NlZClgLCBlKVxuICAgICAgcmV0dXJuIGZhbHNlXG4gICAgfVxuICB9XG5cbiAgcHJpdmF0ZSBhc3luYyByZXNvbHZlV2l0aENlcmJvcyhjaGVja3M6IFBlcm1pc3Npb25DaGVja1tdKTogUHJvbWlzZTxEZWNpc2lvbk1hcD4ge1xuICAgIC8vIEZhaWwtY2xvc2VkOiBubyBQRFAgd2lyZWQgaW4gbWVhbnMgbm8gZ3JhbnRzLlxuICAgIGlmICghdGhpcy5jZXJib3MpIHtcbiAgICAgIGNvbnNvbGUuZXJyb3IoYCR7UGVybWlzc2lvbnNTZXJ2aWNlLm5hbWV9IC0gQ0VSQk9TX1NFUlZJQ0Ugbm90IHByb3ZpZGVkOyBkZW55aW5nIGFsbCAoZmFpbC1jbG9zZWQpYClcbiAgICAgIHJldHVybiB0aGlzLmRlbnlBbGwoY2hlY2tzKVxuICAgIH1cbiAgICB0cnkge1xuICAgICAgY29uc3QgdXNlckluZm8gPSBhd2FpdCB0aGlzLmNvbmZpZy51c2VySW5mbygpXG4gICAgICByZXR1cm4gYXdhaXQgdGhpcy5jZXJib3MuY2hlY2tSZXNvdXJjZXMoXG4gICAgICAgIHVzZXJJbmZvLFxuICAgICAgICBjaGVja3MubWFwKCh7IGtpbmQsIGFjdGlvbnMsIGlkIH0pID0+ICh7IGtpbmQsIGFjdGlvbnMsIGlkIH0pKVxuICAgICAgKVxuICAgIH0gY2F0Y2ggKGUpIHtcbiAgICAgIGNvbnNvbGUuZXJyb3IoYCR7UGVybWlzc2lvbnNTZXJ2aWNlLm5hbWV9IC0gQ2VyYm9zIGNoZWNrIGZhaWxlZDsgZGVueWluZyBhbGwgKGZhaWwtY2xvc2VkKWAsIGUpXG4gICAgICByZXR1cm4gdGhpcy5kZW55QWxsKGNoZWNrcylcbiAgICB9XG4gIH1cblxuICBwcml2YXRlIGFzeW5jIHJlc29sdmVXaXRoTGVnYWN5KGNoZWNrczogUGVybWlzc2lvbkNoZWNrW10pOiBQcm9taXNlPERlY2lzaW9uTWFwPiB7XG4gICAgY29uc3QgZGVjaXNpb25zOiBEZWNpc2lvbk1hcCA9IHt9XG4gICAgZm9yIChjb25zdCBjaGVjayBvZiBjaGVja3MpIHtcbiAgICAgIGNvbnN0IGFjdGlvbnM6IFJlY29yZDxzdHJpbmcsIGJvb2xlYW4+ID0ge31cbiAgICAgIGZvciAoY29uc3QgYWN0aW9uIG9mIGNoZWNrLmFjdGlvbnMpIHtcbiAgICAgICAgYWN0aW9uc1thY3Rpb25dID0gYXdhaXQgdGhpcy5jb25maWcubGVnYWN5UmVzb2x2ZXIoeyBraW5kOiBjaGVjay5raW5kLCBhY3Rpb24gfSwgY2hlY2subGVnYWN5Q29udGV4dClcbiAgICAgIH1cbiAgICAgIGRlY2lzaW9uc1tjaGVjay5raW5kXSA9IGFjdGlvbnNcbiAgICB9XG4gICAgcmV0dXJuIGRlY2lzaW9uc1xuICB9XG5cbiAgcHJpdmF0ZSBkZW55QWxsKGNoZWNrczogUGVybWlzc2lvbkNoZWNrW10pOiBEZWNpc2lvbk1hcCB7XG4gICAgcmV0dXJuIGNoZWNrcy5yZWR1Y2U8RGVjaXNpb25NYXA+KChhY2MsIGNoZWNrKSA9PiB7XG4gICAgICBhY2NbY2hlY2sua2luZF0gPSBjaGVjay5hY3Rpb25zLnJlZHVjZTxSZWNvcmQ8c3RyaW5nLCBib29sZWFuPj4oKGFjdGlvbnMsIGFjdGlvbikgPT4ge1xuICAgICAgICBhY3Rpb25zW2FjdGlvbl0gPSBmYWxzZVxuICAgICAgICByZXR1cm4gYWN0aW9uc1xuICAgICAgfSwge30pXG4gICAgICByZXR1cm4gYWNjXG4gICAgfSwge30pXG4gIH1cblxuICBwcml2YXRlIGJ1aWxkUmVzdWx0TWFwKGRlY2lzaW9uczogRGVjaXNpb25NYXApOiBQZXJtaXNzaW9uUmVzdWx0TWFwIHtcbiAgICBjb25zdCBjYW4gPSAoa2luZDogc3RyaW5nLCBhY3Rpb246IHN0cmluZyk6IGJvb2xlYW4gPT4gZGVjaXNpb25zW2tpbmRdPy5bYWN0aW9uXSA/PyBmYWxzZVxuICAgIHJldHVybiB7XG4gICAgICBjYW4sXG4gICAgICBmb3I6IChraW5kOiBzdHJpbmcpOiBQZXJtaXNzaW9uUmVzdWx0ID0+ICh7IGNhbjogKGFjdGlvbjogc3RyaW5nKSA9PiBjYW4oa2luZCwgYWN0aW9uKSB9KVxuICAgIH1cbiAgfVxuXG59XG4iXX0=
@@ -1,17 +1,22 @@
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, ChangeDetectionStrategy, HostListener, HostBinding, 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 * as i1$1 from '@angular/cdk/overlay';
8
+ import { OverlayModule } from '@angular/cdk/overlay';
9
+ import { ComponentPortal } from '@angular/cdk/portal';
7
10
  import { CERBOS_SERVICE, provideCerbos } from '@apipass/frontend-utils';
8
11
 
9
12
  /**
10
- * Angular equivalent of the React `PermissionGate`.
13
+ * Wraps projected content with a permission gate.
11
14
  *
12
- * It **never hides** the protected action: when blocked it still renders the
13
- * content, disabled, wrapped in a tooltip that explains why. The decision
14
- * (`isAllowed`) is supplied by the caller — this component only presents it.
15
+ * The decision (`isAllowed`) is supplied by the caller this component only presents it.
16
+ *
17
+ * Modes:
18
+ * - `shadow` (default): renders content grayed out with a tooltip explaining why access is denied.
19
+ * - `hide`: removes the content entirely from the layout when blocked.
15
20
  *
16
21
  * @example
17
22
  * ```html
@@ -27,17 +32,21 @@ class PermissionGateComponent {
27
32
  isAllowed = false;
28
33
  /** Tooltip text shown when the action is blocked. */
29
34
  noAccessLabel = '';
35
+ /** Display mode when permission is denied. Default: `shadow`. */
36
+ mode = 'shadow';
30
37
  template;
31
38
  static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "16.0.2", ngImport: i0, type: PermissionGateComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
32
- static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "16.0.2", type: PermissionGateComponent, selector: "apipass-permission-gate", inputs: { isAllowed: "isAllowed", noAccessLabel: "noAccessLabel" }, queries: [{ propertyName: "template", first: true, predicate: TemplateRef, descendants: true }], ngImport: i0, template: "<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", styles: [".permission-gate-blocked{display:inline-flex;cursor:not-allowed}\n"], dependencies: [{ kind: "directive", type: i1.NgIf, selector: "[ngIf]", inputs: ["ngIf", "ngIfThen", "ngIfElse"] }, { kind: "directive", type: i1.NgTemplateOutlet, selector: "[ngTemplateOutlet]", inputs: ["ngTemplateOutletContext", "ngTemplateOutlet", "ngTemplateOutletInjector"] }, { kind: "directive", type: i2.MatTooltip, selector: "[matTooltip]", exportAs: ["matTooltip"] }] });
39
+ static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "16.0.2", type: PermissionGateComponent, selector: "apipass-permission-gate", inputs: { isAllowed: "isAllowed", noAccessLabel: "noAccessLabel", mode: "mode" }, queries: [{ propertyName: "template", first: true, predicate: TemplateRef, descendants: true }], ngImport: i0, template: "<ng-container *ngIf=\"isAllowed\">\n <ng-container *ngTemplateOutlet=\"template ?? null; context: { $implicit: true }\"></ng-container>\n</ng-container>\n\n<ng-container *ngIf=\"!isAllowed && mode === 'shadow'\">\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-container>\n", styles: [".permission-gate-blocked{display:inline-flex;cursor:not-allowed;filter:grayscale(1) opacity(.5)}.permission-gate-blocked ::ng-deep *{pointer-events:none}\n"], dependencies: [{ kind: "directive", type: i1.NgIf, selector: "[ngIf]", inputs: ["ngIf", "ngIfThen", "ngIfElse"] }, { kind: "directive", type: i1.NgTemplateOutlet, selector: "[ngTemplateOutlet]", inputs: ["ngTemplateOutletContext", "ngTemplateOutlet", "ngTemplateOutletInjector"] }, { kind: "directive", type: i2.MatTooltip, selector: "[matTooltip]", exportAs: ["matTooltip"] }] });
33
40
  }
34
41
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "16.0.2", ngImport: i0, type: PermissionGateComponent, decorators: [{
35
42
  type: Component,
36
- args: [{ selector: 'apipass-permission-gate', template: "<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", styles: [".permission-gate-blocked{display:inline-flex;cursor:not-allowed}\n"] }]
43
+ args: [{ selector: 'apipass-permission-gate', template: "<ng-container *ngIf=\"isAllowed\">\n <ng-container *ngTemplateOutlet=\"template ?? null; context: { $implicit: true }\"></ng-container>\n</ng-container>\n\n<ng-container *ngIf=\"!isAllowed && mode === 'shadow'\">\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-container>\n", styles: [".permission-gate-blocked{display:inline-flex;cursor:not-allowed;filter:grayscale(1) opacity(.5)}.permission-gate-blocked ::ng-deep *{pointer-events:none}\n"] }]
37
44
  }], propDecorators: { isAllowed: [{
38
45
  type: Input
39
46
  }], noAccessLabel: [{
40
47
  type: Input
48
+ }], mode: [{
49
+ type: Input
41
50
  }], template: [{
42
51
  type: ContentChild,
43
52
  args: [TemplateRef]
@@ -100,6 +109,170 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "16.0.2", ngImpor
100
109
  type: Input
101
110
  }] } });
102
111
 
112
+ /**
113
+ * Internal tooltip overlay component used by ApipassGateDirective.
114
+ * Styled to match Angular Material's tooltip appearance.
115
+ */
116
+ class ApipassGateTooltipComponent {
117
+ message = '';
118
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "16.0.2", ngImport: i0, type: ApipassGateTooltipComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
119
+ static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "16.0.2", type: ApipassGateTooltipComponent, isStandalone: true, selector: "apipass-gate-tooltip", ngImport: i0, template: '<div class="apipass-gate-tooltip">{{ message }}</div>', isInline: true, styles: [":host{pointer-events:none}.apipass-gate-tooltip{background:rgba(97,97,97,.9);color:#fff;padding:4px 8px;border-radius:4px;font-size:12px;line-height:16px;max-width:250px;word-break:break-word;white-space:pre-line}\n"], changeDetection: i0.ChangeDetectionStrategy.OnPush });
120
+ }
121
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "16.0.2", ngImport: i0, type: ApipassGateTooltipComponent, decorators: [{
122
+ type: Component,
123
+ args: [{ standalone: true, selector: 'apipass-gate-tooltip', template: '<div class="apipass-gate-tooltip">{{ message }}</div>', changeDetection: ChangeDetectionStrategy.OnPush, styles: [":host{pointer-events:none}.apipass-gate-tooltip{background:rgba(97,97,97,.9);color:#fff;padding:4px 8px;border-radius:4px;font-size:12px;line-height:16px;max-width:250px;word-break:break-word;white-space:pre-line}\n"] }]
124
+ }] });
125
+
126
+ /**
127
+ * Gates an action element by user permission — single, unified API.
128
+ *
129
+ * Modes:
130
+ * - `shadow` (default): element stays visible but visually grayed out, cursor not-allowed, clicks blocked.
131
+ * - `hide`: element removed from layout (display:none) when blocked.
132
+ *
133
+ * Tooltip: uses CDK Overlay to render a Material-styled tooltip.
134
+ * - blocked + label: shows the permission label (why access is denied)
135
+ * - allowed + tooltip: shows the action tooltip (what the element does)
136
+ * - otherwise: no tooltip
137
+ */
138
+ class ApipassGateDirective {
139
+ el;
140
+ renderer;
141
+ overlay;
142
+ vcr;
143
+ isAllowed = true;
144
+ label = '';
145
+ tooltip = '';
146
+ mode = 'shadow';
147
+ overlayRef = null;
148
+ tooltipRef = null;
149
+ constructor(el, renderer, overlay, vcr) {
150
+ this.el = el;
151
+ this.renderer = renderer;
152
+ this.overlay = overlay;
153
+ this.vcr = vcr;
154
+ }
155
+ get blocked() {
156
+ return !this.isAllowed;
157
+ }
158
+ get tooltipMessage() {
159
+ return this.blocked ? this.label : this.tooltip;
160
+ }
161
+ ngOnChanges() {
162
+ this.applyVisualState();
163
+ if (this.tooltipRef) {
164
+ this.tooltipRef.instance.message = this.tooltipMessage;
165
+ }
166
+ }
167
+ ngOnDestroy() {
168
+ this.destroyOverlay();
169
+ }
170
+ onMouseEnter() {
171
+ if (!this.tooltipMessage || this.overlayRef)
172
+ return;
173
+ this.showTooltip();
174
+ }
175
+ onMouseLeave() {
176
+ this.destroyOverlay();
177
+ }
178
+ get _ariaDisabled() {
179
+ return this.blocked ? true : null;
180
+ }
181
+ get _blockedClass() {
182
+ return this.blocked;
183
+ }
184
+ block(e) {
185
+ if (this.blocked) {
186
+ e.preventDefault();
187
+ e.stopImmediatePropagation();
188
+ }
189
+ }
190
+ applyVisualState() {
191
+ const el = this.el.nativeElement;
192
+ if (this.blocked && this.mode === 'shadow') {
193
+ this.renderer.setStyle(el, 'filter', 'grayscale(1) opacity(0.5)');
194
+ this.renderer.setStyle(el, 'cursor', 'not-allowed');
195
+ this.renderer.setStyle(el, 'pointer-events', 'auto');
196
+ this.renderer.removeStyle(el, 'display');
197
+ }
198
+ else if (this.blocked && this.mode === 'hide') {
199
+ this.renderer.setStyle(el, 'display', 'none');
200
+ this.renderer.removeStyle(el, 'filter');
201
+ this.renderer.removeStyle(el, 'cursor');
202
+ this.renderer.removeStyle(el, 'pointer-events');
203
+ }
204
+ else {
205
+ this.renderer.removeStyle(el, 'filter');
206
+ this.renderer.removeStyle(el, 'cursor');
207
+ this.renderer.removeStyle(el, 'pointer-events');
208
+ this.renderer.removeStyle(el, 'display');
209
+ }
210
+ }
211
+ showTooltip() {
212
+ const positionStrategy = this.overlay
213
+ .position()
214
+ .flexibleConnectedTo(this.el)
215
+ .withPositions([
216
+ { originX: 'center', originY: 'top', overlayX: 'center', overlayY: 'bottom', offsetY: -8 },
217
+ { originX: 'center', originY: 'bottom', overlayX: 'center', overlayY: 'top', offsetY: 8 }
218
+ ]);
219
+ this.overlayRef = this.overlay.create({
220
+ positionStrategy,
221
+ scrollStrategy: this.overlay.scrollStrategies.close()
222
+ });
223
+ const portal = new ComponentPortal(ApipassGateTooltipComponent, this.vcr);
224
+ this.tooltipRef = this.overlayRef.attach(portal);
225
+ this.tooltipRef.instance.message = this.tooltipMessage;
226
+ this.tooltipRef.changeDetectorRef.detectChanges();
227
+ }
228
+ destroyOverlay() {
229
+ this.overlayRef?.dispose();
230
+ this.overlayRef = null;
231
+ this.tooltipRef = null;
232
+ }
233
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "16.0.2", ngImport: i0, type: ApipassGateDirective, deps: [{ token: i0.ElementRef }, { token: i0.Renderer2 }, { token: i1$1.Overlay }, { token: i0.ViewContainerRef }], target: i0.ɵɵFactoryTarget.Directive });
234
+ static ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "14.0.0", version: "16.0.2", type: ApipassGateDirective, selector: "[apipassGate]", inputs: { isAllowed: ["apipassGate", "isAllowed"], label: ["apipassGateLabel", "label"], tooltip: ["apipassGateTooltip", "tooltip"], mode: ["apipassGateMode", "mode"] }, host: { listeners: { "mouseenter": "onMouseEnter()", "mouseleave": "onMouseLeave()", "click": "block($event)", "keydown.enter": "block($event)", "keydown.space": "block($event)" }, properties: { "attr.aria-disabled": "this._ariaDisabled", "class.apipass-gate-blocked": "this._blockedClass" } }, usesOnChanges: true, ngImport: i0 });
235
+ }
236
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "16.0.2", ngImport: i0, type: ApipassGateDirective, decorators: [{
237
+ type: Directive,
238
+ args: [{
239
+ selector: '[apipassGate]'
240
+ }]
241
+ }], ctorParameters: function () { return [{ type: i0.ElementRef }, { type: i0.Renderer2 }, { type: i1$1.Overlay }, { type: i0.ViewContainerRef }]; }, propDecorators: { isAllowed: [{
242
+ type: Input,
243
+ args: ['apipassGate']
244
+ }], label: [{
245
+ type: Input,
246
+ args: ['apipassGateLabel']
247
+ }], tooltip: [{
248
+ type: Input,
249
+ args: ['apipassGateTooltip']
250
+ }], mode: [{
251
+ type: Input,
252
+ args: ['apipassGateMode']
253
+ }], onMouseEnter: [{
254
+ type: HostListener,
255
+ args: ['mouseenter']
256
+ }], onMouseLeave: [{
257
+ type: HostListener,
258
+ args: ['mouseleave']
259
+ }], _ariaDisabled: [{
260
+ type: HostBinding,
261
+ args: ['attr.aria-disabled']
262
+ }], _blockedClass: [{
263
+ type: HostBinding,
264
+ args: ['class.apipass-gate-blocked']
265
+ }], block: [{
266
+ type: HostListener,
267
+ args: ['click', ['$event']]
268
+ }, {
269
+ type: HostListener,
270
+ args: ['keydown.enter', ['$event']]
271
+ }, {
272
+ type: HostListener,
273
+ args: ['keydown.space', ['$event']]
274
+ }] } });
275
+
103
276
  const PERMISSIONS_CONFIG = new InjectionToken('PERMISSIONS_CONFIG');
104
277
  /** Registers the {@link PermissionsConfig} used by {@link PermissionsService}. */
105
278
  function providePermissionsConfig(config) {
@@ -134,6 +307,35 @@ class PermissionsService {
134
307
  const map = await this.checkResources([check]);
135
308
  return map.for(check.kind);
136
309
  }
310
+ /**
311
+ * Whether the principal has access to **at least one** resource of `kind` for `action`,
312
+ * without naming a specific resource id. Use this instead of {@link checkResource} for
313
+ * kinds whose Cerbos policies can scope access by resource id (e.g. `R.id in [...]`) —
314
+ * {@link checkResource} would evaluate a synthetic id that never matches such a condition
315
+ * and incorrectly deny principals that do have access to real resources.
316
+ */
317
+ async hasAnyAccess(kind, action) {
318
+ const cerbosEnabled = await this.config.cerbosEnabled();
319
+ return cerbosEnabled
320
+ ? await this.resolveAnyAccessWithCerbos(kind, action)
321
+ : await this.config.legacyResolver({ kind, action });
322
+ }
323
+ async resolveAnyAccessWithCerbos(kind, action) {
324
+ // Fail-closed: no PDP wired in means no grants.
325
+ if (!this.cerbos) {
326
+ console.error(`${PermissionsService.name} - CERBOS_SERVICE not provided; denying access (fail-closed)`);
327
+ return false;
328
+ }
329
+ try {
330
+ const userInfo = await this.config.userInfo();
331
+ const result = await this.cerbos.planResources(userInfo, { kind, action });
332
+ return result.filter.kind !== 'KIND_ALWAYS_DENIED';
333
+ }
334
+ catch (e) {
335
+ console.error(`${PermissionsService.name} - Cerbos planResources failed; denying access (fail-closed)`, e);
336
+ return false;
337
+ }
338
+ }
137
339
  async resolveWithCerbos(checks) {
138
340
  // Fail-closed: no PDP wired in means no grants.
139
341
  if (!this.cerbos) {
@@ -194,28 +396,35 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "16.0.2", ngImpor
194
396
  class PermissionsModule {
195
397
  static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "16.0.2", ngImport: i0, type: PermissionsModule, deps: [], target: i0.ɵɵFactoryTarget.NgModule });
196
398
  static ɵmod = i0.ɵɵngDeclareNgModule({ minVersion: "14.0.0", version: "16.0.2", ngImport: i0, type: PermissionsModule, declarations: [PermissionGateComponent,
197
- PermissionGateDirective], imports: [CommonModule,
198
- MatTooltipModule], exports: [PermissionGateComponent,
199
- PermissionGateDirective] });
399
+ PermissionGateDirective,
400
+ ApipassGateDirective], imports: [CommonModule,
401
+ MatTooltipModule,
402
+ OverlayModule], exports: [PermissionGateComponent,
403
+ PermissionGateDirective,
404
+ ApipassGateDirective] });
200
405
  static ɵinj = i0.ɵɵngDeclareInjector({ minVersion: "12.0.0", version: "16.0.2", ngImport: i0, type: PermissionsModule, providers: [
201
406
  PermissionsService
202
407
  ], imports: [CommonModule,
203
- MatTooltipModule] });
408
+ MatTooltipModule,
409
+ OverlayModule] });
204
410
  }
205
411
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "16.0.2", ngImport: i0, type: PermissionsModule, decorators: [{
206
412
  type: NgModule,
207
413
  args: [{
208
414
  imports: [
209
415
  CommonModule,
210
- MatTooltipModule
416
+ MatTooltipModule,
417
+ OverlayModule
211
418
  ],
212
419
  declarations: [
213
420
  PermissionGateComponent,
214
- PermissionGateDirective
421
+ PermissionGateDirective,
422
+ ApipassGateDirective
215
423
  ],
216
424
  exports: [
217
425
  PermissionGateComponent,
218
- PermissionGateDirective
426
+ PermissionGateDirective,
427
+ ApipassGateDirective
219
428
  ],
220
429
  providers: [
221
430
  PermissionsService
@@ -261,5 +470,5 @@ function providePermissions(options) {
261
470
  * Generated bundle index. Do not edit.
262
471
  */
263
472
 
264
- export { PERMISSIONS_CONFIG, PermissionGateComponent, PermissionGateDirective, PermissionsModule, PermissionsService, providePermissions, providePermissionsConfig };
473
+ export { ApipassGateDirective, PERMISSIONS_CONFIG, PermissionGateComponent, PermissionGateDirective, PermissionsModule, PermissionsService, providePermissions, providePermissionsConfig };
265
474
  //# 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/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 { 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","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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"}
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-tooltip.component.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 * Wraps projected content with a permission gate.\n *\n * The decision (`isAllowed`) is supplied by the caller — this component only presents it.\n *\n * Modes:\n * - `shadow` (default): renders content grayed out with a tooltip explaining why access is denied.\n * - `hide`: removes the content entirely from the layout when blocked.\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 /** Display mode when permission is denied. Default: `shadow`. */\n @Input() mode: 'shadow' | 'hide' = 'shadow'\n\n @ContentChild(TemplateRef) template?: TemplateRef<PermissionGateContext>\n\n}\n","<ng-container *ngIf=\"isAllowed\">\n <ng-container *ngTemplateOutlet=\"template ?? null; context: { $implicit: true }\"></ng-container>\n</ng-container>\n\n<ng-container *ngIf=\"!isAllowed && mode === 'shadow'\">\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-container>\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 { ChangeDetectionStrategy, Component } from '@angular/core'\n\n/**\n * Internal tooltip overlay component used by ApipassGateDirective.\n * Styled to match Angular Material's tooltip appearance.\n */\n@Component({\n standalone: true,\n selector: 'apipass-gate-tooltip',\n template: '<div class=\"apipass-gate-tooltip\">{{ message }}</div>',\n styles: [`\n :host { pointer-events: none; }\n .apipass-gate-tooltip {\n background: rgba(97, 97, 97, 0.9);\n color: #fff;\n padding: 4px 8px;\n border-radius: 4px;\n font-size: 12px;\n line-height: 16px;\n max-width: 250px;\n word-break: break-word;\n white-space: pre-line;\n }\n `],\n changeDetection: ChangeDetectionStrategy.OnPush\n})\nexport class ApipassGateTooltipComponent {\n\n message = ''\n\n}\n","import {\n ComponentRef,\n Directive,\n ElementRef,\n HostBinding,\n HostListener,\n Input,\n OnChanges,\n OnDestroy,\n Renderer2,\n ViewContainerRef\n} from '@angular/core'\nimport { Overlay, OverlayRef } from '@angular/cdk/overlay'\nimport { ComponentPortal } from '@angular/cdk/portal'\nimport { ApipassGateTooltipComponent } from './permission-gate-tooltip.component'\n\n/**\n * Gates an action element by user permission — single, unified API.\n *\n * Modes:\n * - `shadow` (default): element stays visible but visually grayed out, cursor not-allowed, clicks blocked.\n * - `hide`: element removed from layout (display:none) when blocked.\n *\n * Tooltip: uses CDK Overlay to render a Material-styled tooltip.\n * - blocked + label: shows the permission label (why access is denied)\n * - allowed + tooltip: shows the action tooltip (what the element does)\n * - otherwise: no tooltip\n */\n@Directive({\n selector: '[apipassGate]'\n})\nexport class ApipassGateDirective implements OnChanges, OnDestroy {\n\n @Input('apipassGate') isAllowed = true\n @Input('apipassGateLabel') label = ''\n @Input('apipassGateTooltip') tooltip = ''\n @Input('apipassGateMode') mode: 'shadow' | 'hide' = 'shadow'\n\n private overlayRef: OverlayRef | null = null\n private tooltipRef: ComponentRef<ApipassGateTooltipComponent> | null = null\n\n constructor(\n private readonly el: ElementRef,\n private readonly renderer: Renderer2,\n private readonly overlay: Overlay,\n private readonly vcr: ViewContainerRef\n ) {}\n\n private get blocked(): boolean {\n return !this.isAllowed\n }\n\n private get tooltipMessage(): string {\n return this.blocked ? this.label : this.tooltip\n }\n\n ngOnChanges(): void {\n this.applyVisualState()\n if (this.tooltipRef) {\n this.tooltipRef.instance.message = this.tooltipMessage\n }\n }\n\n ngOnDestroy(): void {\n this.destroyOverlay()\n }\n\n @HostListener('mouseenter')\n onMouseEnter(): void {\n if (!this.tooltipMessage || this.overlayRef) return\n this.showTooltip()\n }\n\n @HostListener('mouseleave')\n onMouseLeave(): void {\n this.destroyOverlay()\n }\n\n @HostBinding('attr.aria-disabled')\n get _ariaDisabled(): true | null {\n return this.blocked ? true : null\n }\n\n @HostBinding('class.apipass-gate-blocked')\n get _blockedClass(): boolean {\n return this.blocked\n }\n\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 private applyVisualState(): void {\n const el = this.el.nativeElement\n if (this.blocked && this.mode === 'shadow') {\n this.renderer.setStyle(el, 'filter', 'grayscale(1) opacity(0.5)')\n this.renderer.setStyle(el, 'cursor', 'not-allowed')\n this.renderer.setStyle(el, 'pointer-events', 'auto')\n this.renderer.removeStyle(el, 'display')\n } else if (this.blocked && this.mode === 'hide') {\n this.renderer.setStyle(el, 'display', 'none')\n this.renderer.removeStyle(el, 'filter')\n this.renderer.removeStyle(el, 'cursor')\n this.renderer.removeStyle(el, 'pointer-events')\n } else {\n this.renderer.removeStyle(el, 'filter')\n this.renderer.removeStyle(el, 'cursor')\n this.renderer.removeStyle(el, 'pointer-events')\n this.renderer.removeStyle(el, 'display')\n }\n }\n\n private showTooltip(): void {\n const positionStrategy = this.overlay\n .position()\n .flexibleConnectedTo(this.el)\n .withPositions([\n { originX: 'center', originY: 'top', overlayX: 'center', overlayY: 'bottom', offsetY: -8 },\n { originX: 'center', originY: 'bottom', overlayX: 'center', overlayY: 'top', offsetY: 8 }\n ])\n\n this.overlayRef = this.overlay.create({\n positionStrategy,\n scrollStrategy: this.overlay.scrollStrategies.close()\n })\n\n const portal = new ComponentPortal(ApipassGateTooltipComponent, this.vcr)\n this.tooltipRef = this.overlayRef.attach(portal)\n this.tooltipRef.instance.message = this.tooltipMessage\n this.tooltipRef.changeDetectorRef.detectChanges()\n }\n\n private destroyOverlay(): void {\n this.overlayRef?.dispose()\n this.overlayRef = null\n this.tooltipRef = null\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 /**\n * Whether the principal has access to **at least one** resource of `kind` for `action`,\n * without naming a specific resource id. Use this instead of {@link checkResource} for\n * kinds whose Cerbos policies can scope access by resource id (e.g. `R.id in [...]`) —\n * {@link checkResource} would evaluate a synthetic id that never matches such a condition\n * and incorrectly deny principals that do have access to real resources.\n */\n async hasAnyAccess(kind: string, action: string): Promise<boolean> {\n const cerbosEnabled = await this.config.cerbosEnabled()\n return cerbosEnabled\n ? await this.resolveAnyAccessWithCerbos(kind, action)\n : await this.config.legacyResolver({ kind, action })\n }\n\n private async resolveAnyAccessWithCerbos(kind: string, action: string): Promise<boolean> {\n // Fail-closed: no PDP wired in means no grants.\n if (!this.cerbos) {\n console.error(`${PermissionsService.name} - CERBOS_SERVICE not provided; denying access (fail-closed)`)\n return false\n }\n try {\n const userInfo = await this.config.userInfo()\n const result = await this.cerbos.planResources(userInfo, { kind, action })\n return result.filter.kind !== 'KIND_ALWAYS_DENIED'\n } catch (e) {\n console.error(`${PermissionsService.name} - Cerbos planResources failed; denying access (fail-closed)`, e)\n return false\n }\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 { OverlayModule } from '@angular/cdk/overlay'\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 OverlayModule\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":["i1"],"mappings":"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"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@apipass/permissions",
3
- "version": "1.0.196",
3
+ "version": "1.0.198",
4
4
  "peerDependencies": {
5
5
  "@angular/common": "16.0.2",
6
6
  "@angular/core": "16.0.2",
@@ -0,0 +1,42 @@
1
+ import { ElementRef, OnChanges, OnDestroy, Renderer2, ViewContainerRef } from '@angular/core';
2
+ import { Overlay } from '@angular/cdk/overlay';
3
+ import * as i0 from "@angular/core";
4
+ /**
5
+ * Gates an action element by user permission — single, unified API.
6
+ *
7
+ * Modes:
8
+ * - `shadow` (default): element stays visible but visually grayed out, cursor not-allowed, clicks blocked.
9
+ * - `hide`: element removed from layout (display:none) when blocked.
10
+ *
11
+ * Tooltip: uses CDK Overlay to render a Material-styled tooltip.
12
+ * - blocked + label: shows the permission label (why access is denied)
13
+ * - allowed + tooltip: shows the action tooltip (what the element does)
14
+ * - otherwise: no tooltip
15
+ */
16
+ export declare class ApipassGateDirective implements OnChanges, OnDestroy {
17
+ private readonly el;
18
+ private readonly renderer;
19
+ private readonly overlay;
20
+ private readonly vcr;
21
+ isAllowed: boolean;
22
+ label: string;
23
+ tooltip: string;
24
+ mode: 'shadow' | 'hide';
25
+ private overlayRef;
26
+ private tooltipRef;
27
+ constructor(el: ElementRef, renderer: Renderer2, overlay: Overlay, vcr: ViewContainerRef);
28
+ private get blocked();
29
+ private get tooltipMessage();
30
+ ngOnChanges(): void;
31
+ ngOnDestroy(): void;
32
+ onMouseEnter(): void;
33
+ onMouseLeave(): void;
34
+ get _ariaDisabled(): true | null;
35
+ get _blockedClass(): boolean;
36
+ block(e: Event): void;
37
+ private applyVisualState;
38
+ private showTooltip;
39
+ private destroyOverlay;
40
+ static ɵfac: i0.ɵɵFactoryDeclaration<ApipassGateDirective, never>;
41
+ static ɵdir: i0.ɵɵDirectiveDeclaration<ApipassGateDirective, "[apipassGate]", never, { "isAllowed": { "alias": "apipassGate"; "required": false; }; "label": { "alias": "apipassGateLabel"; "required": false; }; "tooltip": { "alias": "apipassGateTooltip"; "required": false; }; "mode": { "alias": "apipassGateMode"; "required": false; }; }, {}, never, never, false, never>;
42
+ }
@@ -0,0 +1,10 @@
1
+ import * as i0 from "@angular/core";
2
+ /**
3
+ * Internal tooltip overlay component used by ApipassGateDirective.
4
+ * Styled to match Angular Material's tooltip appearance.
5
+ */
6
+ export declare class ApipassGateTooltipComponent {
7
+ message: string;
8
+ static ɵfac: i0.ɵɵFactoryDeclaration<ApipassGateTooltipComponent, never>;
9
+ static ɵcmp: i0.ɵɵComponentDeclaration<ApipassGateTooltipComponent, "apipass-gate-tooltip", never, {}, {}, never, never, true, never>;
10
+ }
@@ -5,11 +5,13 @@ export interface PermissionGateContext {
5
5
  $implicit: boolean;
6
6
  }
7
7
  /**
8
- * Angular equivalent of the React `PermissionGate`.
8
+ * Wraps projected content with a permission gate.
9
9
  *
10
- * It **never hides** the protected action: when blocked it still renders the
11
- * content, disabled, wrapped in a tooltip that explains why. The decision
12
- * (`isAllowed`) is supplied by the caller — this component only presents it.
10
+ * The decision (`isAllowed`) is supplied by the caller this component only presents it.
11
+ *
12
+ * Modes:
13
+ * - `shadow` (default): renders content grayed out with a tooltip explaining why access is denied.
14
+ * - `hide`: removes the content entirely from the layout when blocked.
13
15
  *
14
16
  * @example
15
17
  * ```html
@@ -25,7 +27,9 @@ export declare class PermissionGateComponent {
25
27
  isAllowed: boolean;
26
28
  /** Tooltip text shown when the action is blocked. */
27
29
  noAccessLabel: string;
30
+ /** Display mode when permission is denied. Default: `shadow`. */
31
+ mode: 'shadow' | 'hide';
28
32
  template?: TemplateRef<PermissionGateContext>;
29
33
  static ɵfac: i0.ɵɵFactoryDeclaration<PermissionGateComponent, never>;
30
- static ɵcmp: i0.ɵɵComponentDeclaration<PermissionGateComponent, "apipass-permission-gate", never, { "isAllowed": { "alias": "isAllowed"; "required": false; }; "noAccessLabel": { "alias": "noAccessLabel"; "required": false; }; }, {}, ["template"], never, false, never>;
34
+ static ɵcmp: i0.ɵɵComponentDeclaration<PermissionGateComponent, "apipass-permission-gate", never, { "isAllowed": { "alias": "isAllowed"; "required": false; }; "noAccessLabel": { "alias": "noAccessLabel"; "required": false; }; "mode": { "alias": "mode"; "required": false; }; }, {}, ["template"], never, false, never>;
31
35
  }
@@ -1,10 +1,12 @@
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";
7
+ import * as i6 from "@angular/cdk/overlay";
6
8
  export declare class PermissionsModule {
7
9
  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]>;
10
+ static ɵmod: i0.ɵɵNgModuleDeclaration<PermissionsModule, [typeof i1.PermissionGateComponent, typeof i2.PermissionGateDirective, typeof i3.ApipassGateDirective], [typeof i4.CommonModule, typeof i5.MatTooltipModule, typeof i6.OverlayModule], [typeof i1.PermissionGateComponent, typeof i2.PermissionGateDirective, typeof i3.ApipassGateDirective]>;
9
11
  static ɵinj: i0.ɵɵInjectorDeclaration<PermissionsModule>;
10
12
  }
package/public-api.d.ts CHANGED
@@ -1,6 +1,7 @@
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';
6
7
  export * from './service/permissions.providers';
@@ -19,6 +19,15 @@ export declare class PermissionsService {
19
19
  checkResources(checks: PermissionCheck[]): Promise<PermissionResultMap>;
20
20
  /** Convenience for the common single-kind case. */
21
21
  checkResource(check: PermissionCheck): Promise<PermissionResult>;
22
+ /**
23
+ * Whether the principal has access to **at least one** resource of `kind` for `action`,
24
+ * without naming a specific resource id. Use this instead of {@link checkResource} for
25
+ * kinds whose Cerbos policies can scope access by resource id (e.g. `R.id in [...]`) —
26
+ * {@link checkResource} would evaluate a synthetic id that never matches such a condition
27
+ * and incorrectly deny principals that do have access to real resources.
28
+ */
29
+ hasAnyAccess(kind: string, action: string): Promise<boolean>;
30
+ private resolveAnyAccessWithCerbos;
22
31
  private resolveWithCerbos;
23
32
  private resolveWithLegacy;
24
33
  private denyAll;
package/styles.css ADDED
@@ -0,0 +1,15 @@
1
+ /*
2
+ * @apipass/permissions — global styles
3
+ *
4
+ * Add to angular.json styles array:
5
+ * "node_modules/@apipass/permissions/styles.css"
6
+ *
7
+ * Suppresses pointer-events on all descendants of permission gate wrappers so
8
+ * cursor and tooltip always come from the gate host element, not inner elements.
9
+ * This fixes: (1) cursor showing as pointer when inner elements have cursor:pointer,
10
+ * (2) inner [matTooltip] directives firing alongside the gate's own tooltip.
11
+ */
12
+ .permission-gate-blocked *,
13
+ .apipass-gate-blocked * {
14
+ pointer-events: none;
15
+ }