File
Index
Properties
|
|
Methods
|
|
Inputs
|
|
HostListeners
|
|
Accessors
|
|
persistPolicyUrl
|
Type : string
|
|
show
|
Type : boolean
|
Default value : false
|
|
HostListeners
document:keypress
|
Arguments : '$event'
|
document:keypress(event: KeyboardEvent)
|
|
Methods
handleContextChanged
|
handleContextChanged(value: string)
|
|
Parameters :
Name |
Type |
Optional |
value |
string
|
No
|
|
onKeyPress
|
onKeyPress(event: KeyboardEvent)
|
Decorators :
@HostListener('document:keypress', ['$event'])
|
|
Parameters :
Name |
Type |
Optional |
event |
KeyboardEvent
|
No
|
|
open
|
open(value?: boolean)
|
|
Parameters :
Name |
Type |
Optional |
value |
boolean
|
Yes
|
|
aclHintCheckboxControl
|
Default value : new UntypedFormControl(false)
|
|
aclIconName
|
Default value : AclIconName
|
|
selectedTab
|
Default value : this.tabs[0]
|
|
tabs
|
Type : []
|
Default value : ['Rules', 'Reports', 'Settings', 'Environment', 'Help']
|
|
template
|
Type : TemplateRef<>
|
Decorators :
@ViewChild(TemplateRef)
|
|
Accessors
aclDecisionLog
|
getaclDecisionLog()
|
|
aclService
|
getaclService()
|
|
import { AclDecision, AclRule } from '@allianz/taly-acl';
import {
Component,
DestroyRef,
HostListener,
Inject,
Input,
TemplateRef,
ViewChild,
inject
} from '@angular/core';
import { takeUntilDestroyed } from '@angular/core/rxjs-interop';
import { UntypedFormControl } from '@angular/forms';
import { startWith, tap } from 'rxjs/operators';
import { AclIconName } from '../acl-icon/acl-icon.interface';
import { AclService } from '../services/acl.service';
import { AclInspectorService } from './inspector.service';
// TODO: proper inspector resize could be achieved with https://stackoverflow.com/questions/56776046/how-to-make-angular-material-dialog-re-sizable-in-angular-7
@Component({
selector: 'acl-inspector',
templateUrl: './inspector.component.html',
styleUrls: ['./inspector.component.scss'],
standalone: false
})
export class AclInspectorComponent {
aclIconName = AclIconName;
aclHintCheckboxControl = new UntypedFormControl(false);
tabs = ['Rules', 'Reports', 'Settings', 'Environment', 'Help'];
selectedTab = this.tabs[0];
@Input() show = false;
@Input() persistPolicyUrl!: string;
@ViewChild(TemplateRef) template!: TemplateRef<unknown>;
private inspectorService: AclInspectorService;
private destroyRef = inject(DestroyRef);
constructor(@Inject(AclInspectorService) inspectorService: AclInspectorService) {
this.inspectorService = inspectorService;
this.inspectorService.openSignal.subscribe(() => this.open(true));
this.aclHintCheckboxControl.valueChanges
.pipe(
takeUntilDestroyed(this.destroyRef),
startWith(this.aclHintCheckboxControl.value),
tap((showHints) => {
this.inspectorService.showAclHints$.next(showHints);
})
)
.subscribe();
this.destroyRef.onDestroy(() => {
this.inspectorService.showAclHints$.next(false);
});
}
handleContextChanged(value: string) {
this.aclService.setEnvironmentValue('context', value);
}
updateRules(rules: AclRule[]) {
this.aclService._setGlobalRules(rules);
}
open(value?: boolean) {
this.show = value ?? !this.show;
}
get aclDecisionLog(): AclDecision[] {
return this.inspectorService.aclDecisionLog;
}
get aclService(): AclService {
return this.inspectorService.aclService;
}
@HostListener('document:keypress', ['$event'])
onKeyPress(event: KeyboardEvent) {
// bail if input element is focused
if (
document.activeElement?.tagName === 'INPUT' ||
document.activeElement?.tagName === 'TEXTAREA'
) {
return;
}
if (event.key === 'h') {
this.aclHintCheckboxControl.setValue(!this.aclHintCheckboxControl.value);
}
}
}
<span class="anchor" cdkOverlayOrigin #trigger="cdkOverlayOrigin"></span>
<button class="debug-opener" (click)="open()">
<acl-icon [name]="aclIconName.Acl" size="28"></acl-icon> ACL Inspector
<span class="triangle"><span class="triangle-down"></span></span>
</button>
<ng-template
cdkConnectedOverlay
(detach)="show = false"
[cdkConnectedOverlayOrigin]="trigger"
[cdkConnectedOverlayOpen]="show"
>
<div cdkDrag class="debug-panel" cdkDragRootElement=".cdk-overlay-pane">
<div class="debug-content">
<debug-header (closeWindow)="open(false)" cdkDragHandle></debug-header>
<div class="tab-group-nav padding-left-16">
<button
class="tab-btn"
[ngClass]="{ 'tab-btn--active': selectedTab === tab }"
*ngFor="let tab of tabs; let i = index"
(click)="selectedTab = tab"
>
{{ tab }}
</button>
</div>
<div class="tab-group">
<div class="tab" *ngIf="selectedTab === 'Rules'">
<acl-policy-editor
class="editor"
(changed)="updateRules($event)"
[policy]="(this.aclService.currentPolicy$ | async)!"
[persistPolicyUrl]="this.persistPolicyUrl"
>
</acl-policy-editor>
</div>
<div class="tab" *ngIf="selectedTab === 'Reports'">
<report-viewer
(ruleAdded)="aclService._addGlobalRule($event)"
[log]="aclDecisionLog"
></report-viewer>
</div>
<div class="tab" *ngIf="selectedTab === 'Settings'" class="tab-content tab-content-padding">
<label>
<input type="checkbox" [formControl]="aclHintCheckboxControl" />
Show Acl Hints
</label>
</div>
<div
class="tab"
*ngIf="selectedTab === 'Environment'"
class="tab-content tab-content-padding"
>
<acl-environment context="creation" (contextChanged)="handleContextChanged($event)">
</acl-environment>
</div>
<div class="tab" *ngIf="selectedTab === 'Help'" class="tab-content tab-content-padding">
<p>
Rules are evaluated with the <strong>"first-applicable"</strong> strategy.<br />
This means a given resource is checked from top to bottom against all policy rules.<br />
The first one matching determines the state of the resource. If no rule is matching, a
resource by default is visible and neither disabled nor readonly.<br />
<br />
- Try to double click to edit a rule part
<br />
- Rearrange rules with the handle on the left to test the order against the algorithm
</p>
</div>
</div>
</div>
</div>
</ng-template>
@use '../inspector.shared' as *;
@use '../inspector.colors' as *;
:host {
display: block;
position: fixed;
right: 25px;
top: 60px;
z-index: 10;
}
.anchor {
position: absolute;
right: 30px; /**button width**/
top: 5px;
z-index: -1;
}
.debug-panel {
background-color: $debug-color-dark;
color: #fff;
padding: 2px 0;
line-height: 16px;
font-size: 13px;
border-radius: 8px;
box-shadow: 0 2px 2px 0 rgba(0, 0, 0, 0.14), 0 1px 5px 0 rgba(0, 0, 0, 0.12),
0 3px 1px -2px rgba(0, 0, 0, 0.2);
overflow: hidden;
}
.debug-opener {
cursor: pointer;
font-size: 20px;
line-height: 24px;
padding: 8px 16px 8px 6px;
margin: 0 -20px 0 10px;
background-color: $debug-color-dark;
border: 1px solid $debug-color-dark;
color: #fff;
border-radius: 8px;
font-weight: 700;
vertical-align: middle;
display: inline-block;
transition: background-color 0.3s ease;
transform: rotate(90deg) translate(102%);
transform-origin: top right;
&:hover,
&:focus {
background-color: $debug-color-dark-hover;
}
svg {
margin-bottom: -6px;
}
.triangle {
margin-bottom: -1px;
}
}
.tab-group-nav {
background-color: $debug-color-dark;
padding-top: 8px;
}
.tab-btn {
@include base-button-styles;
display: inline-block;
padding: 8px 16px;
font-size: 16px;
line-height: 18px;
font-weight: 700;
border-top-left-radius: 4px;
border-top-right-radius: 4px;
margin-right: 1px;
color: #fff;
&:hover,
&--active {
background-color: $debug-color-light;
}
}
.tab-group {
background-color: $debug-color-light;
}
.tab-content {
font-size: 14px;
}
.tab-content-padding {
padding: 16px;
}
.editor {
height: 50vh;
width: 50vw;
resize: both;
overflow-y: auto;
}
Legend
Html element with directive