rfellows commented on code in PR #8225:
URL: https://github.com/apache/nifi/pull/8225#discussion_r1449063397


##########
nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-frontend/src/main/nifi/src/app/pages/access-policies/ui/component-access-policies/component-access-policies.component.ts:
##########
@@ -0,0 +1,427 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one or more
+ *  contributor license agreements.  See the NOTICE file distributed with
+ *  this work for additional information regarding copyright ownership.
+ *  The ASF licenses this file to You under the Apache License, Version 2.0
+ *  (the "License"); you may not use this file except in compliance with
+ *  the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing, software
+ *  distributed under the License is distributed on an "AS IS" BASIS,
+ *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ *  See the License for the specific language governing permissions and
+ *  limitations under the License.
+ */
+
+import { Component, OnDestroy, OnInit, TemplateRef, ViewChild } from 
'@angular/core';
+import { Store } from '@ngrx/store';
+import { selectCurrentUser } from 
'../../../../state/current-user/current-user.selectors';
+import {
+    createAccessPolicy,
+    openAddTenantToPolicyDialog,
+    promptDeleteAccessPolicy,
+    promptRemoveTenantFromPolicy,
+    reloadAccessPolicy,
+    resetAccessPolicyState,
+    selectComponentAccessPolicy,
+    setAccessPolicy
+} from '../../state/access-policy/access-policy.actions';
+import { AccessPolicyState, RemoveTenantFromPolicyRequest } from 
'../../state/access-policy';
+import { initialState } from '../../state/access-policy/access-policy.reducer';
+import {
+    selectAccessPolicyState,
+    selectComponentResourceActionFromRoute
+} from '../../state/access-policy/access-policy.selectors';
+import { filter } from 'rxjs';
+import { takeUntilDestroyed } from '@angular/core/rxjs-interop';
+import { FormBuilder, FormControl, FormGroup, Validators } from 
'@angular/forms';
+import { NiFiCommon } from '../../../../service/nifi-common.service';
+import { ComponentType, SelectOption, TextTipInput } from 
'../../../../state/shared';
+import { TextTip } from 
'../../../../ui/common/tooltips/text-tip/text-tip.component';
+import { AccessPolicyEntity, Action, PolicyStatus, ResourceAction } from 
'../../state/shared';
+import { loadFlowConfiguration } from 
'../../../../state/flow-configuration/flow-configuration.actions';
+import { selectFlowConfiguration } from 
'../../../../state/flow-configuration/flow-configuration.selectors';
+import { loadTenants, resetTenantsState } from 
'../../state/tenants/tenants.actions';
+import { loadPolicyComponent, resetPolicyComponentState } from 
'../../state/policy-component/policy-component.actions';
+import { selectPolicyComponentState } from 
'../../state/policy-component/policy-component.selectors';
+import { PolicyComponentState } from '../../state/policy-component';
+
+@Component({
+    selector: 'global-access-policies',
+    templateUrl: './component-access-policies.component.html',
+    styleUrls: ['./component-access-policies.component.scss']
+})
+export class ComponentAccessPolicies implements OnInit, OnDestroy {
+    flowConfiguration$ = this.store.select(selectFlowConfiguration);
+    accessPolicyState$ = this.store.select(selectAccessPolicyState);
+    policyComponentState$ = this.store.select(selectPolicyComponentState);
+    currentUser$ = this.store.select(selectCurrentUser);
+
+    protected readonly TextTip = TextTip;
+    protected readonly Action = Action;
+    protected readonly PolicyStatus = PolicyStatus;
+    protected readonly ComponentType = ComponentType;
+
+    policyForm: FormGroup;
+    policyActionOptions: SelectOption[] = [
+        {
+            text: 'view the component',
+            value: 'read-component',
+            description: 'Allows users to view component configuration details'
+        },
+        {
+            text: 'modify the component',
+            value: 'write-component',
+            description: 'Allows users to modify component configuration 
details'
+        },
+        {
+            text: 'operate the component',
+            value: 'write-operation',
+            description:
+                'Allows users to operate components by changing component run 
status (start/stop/enable/disable), remote port transmission status, or 
terminating processor threads'
+        },
+        {
+            text: 'view provenance',
+            value: 'read-provenance-data',
+            description: 'Allows users to view provenance events generated by 
this component'
+        },
+        {
+            text: 'view the data',
+            value: 'read-data',
+            description:
+                'Allows users to view metadata and content for this component 
in flowfile queues in outbound connections and through provenance events'
+        },
+        {
+            text: 'modify the data',
+            value: 'write-data',
+            description:
+                'Allows users to empty flowfile queues in outbound connections 
and submit replays through provenance events'
+        },
+        {
+            text: 'receive data via site-to-site',
+            value: 'write-receive-data',
+            description: 'Allows this port to receive data from these NiFi 
instances',
+            disabled: true
+        },
+        {
+            text: 'send data via site-to-site',
+            value: 'write-send-data',
+            description: 'Allows this port to send data to these NiFi 
instances',
+            disabled: true
+        },
+        {
+            text: 'view the policies',
+            value: 'read-policies',
+            description: 'Allows users to view the list of users who can 
view/modify this component'
+        },
+        {
+            text: 'modify the policies',
+            value: 'write-policies',
+            description: 'Allows users to modify the list of users who can 
view/modify this component'
+        }
+    ];
+
+    action!: Action;
+    resource!: string;
+    policy!: string;
+    resourceIdentifier!: string;
+
+    @ViewChild('inheritedFromPolicies') inheritedFromPolicies!: 
TemplateRef<any>;
+    @ViewChild('inheritedFromController') inheritedFromController!: 
TemplateRef<any>;
+    @ViewChild('inheritedFromGlobalParameterContexts') 
inheritedFromGlobalParameterContexts!: TemplateRef<any>;
+    @ViewChild('inheritedFromProcessGroup') inheritedFromProcessGroup!: 
TemplateRef<any>;
+
+    constructor(
+        private store: Store<AccessPolicyState>,
+        private formBuilder: FormBuilder,
+        private nifiCommon: NiFiCommon
+    ) {
+        this.policyForm = this.formBuilder.group({
+            policyAction: new FormControl(this.policyActionOptions[0].value, 
Validators.required)
+        });
+
+        this.store
+            .select(selectComponentResourceActionFromRoute)
+            .pipe(
+                filter((resourceAction) => resourceAction != null),
+                takeUntilDestroyed()
+            )
+            .subscribe((componentResourceAction) => {
+                if (componentResourceAction) {
+                    this.action = componentResourceAction.action;
+                    this.policy = componentResourceAction.policy;
+                    this.resource = componentResourceAction.resource;
+                    this.resourceIdentifier = 
componentResourceAction.resourceIdentifier;
+
+                    // data transfer policies for site to site are presented 
different in the form so
+                    // we need to distinguish by type
+                    let policyForResource: string = this.policy;
+                    if (this.policy === 'data-transfer') {
+                        if (this.resource === 'input-ports') {
+                            policyForResource = 'receive-data';
+                        } else {
+                            policyForResource = 'send-data';
+                        }
+                    }
+
+                    
this.policyForm.get('policyAction')?.setValue(`${this.action}-${policyForResource}`);
+
+                    // component policies are presented simply as 
'/processors/1234' while non-component policies
+                    // like viewing provenance for a specific component is 
presented as `/provenance-data/processors/1234`
+                    let resourceToLoad: string = this.resource;
+                    if (componentResourceAction.policy !== 'component') {
+                        resourceToLoad = `${this.policy}/${this.resource}`;
+                    }
+
+                    const resourceAction: ResourceAction = {
+                        action: this.action,
+                        resource: resourceToLoad,
+                        resourceIdentifier: this.resourceIdentifier
+                    };
+
+                    this.store.dispatch(
+                        loadPolicyComponent({
+                            request: {
+                                componentResourceAction
+                            }
+                        })
+                    );
+                    this.store.dispatch(
+                        setAccessPolicy({
+                            request: {
+                                resourceAction
+                            }
+                        })
+                    );
+                }
+            });
+    }
+
+    ngOnInit(): void {
+        this.store.dispatch(loadFlowConfiguration());
+        this.store.dispatch(loadTenants());
+    }
+
+    isInitialLoading(state: AccessPolicyState): boolean {
+        return state.loadedTimestamp == initialState.loadedTimestamp;
+    }
+
+    isComponentPolicy(option: SelectOption, policyComponentState: 
PolicyComponentState): boolean {
+        // consider the type of component to override which policies shouldn't 
be supported
+
+        if (policyComponentState.resource === 'process-groups') {
+            switch (option.value) {
+                case 'write-send-data':
+                case 'write-receive-data':
+                    return false;
+            }
+        } else if (
+            policyComponentState.resource === 'controller-services' ||
+            policyComponentState.resource === 'reporting-tasks'
+        ) {
+            switch (option.value) {
+                case 'read-data':
+                case 'write-data':
+                case 'write-send-data':
+                case 'write-receive-data':
+                case 'read-provenance-data':
+                    return false;
+            }
+        } else if (
+            policyComponentState.resource === 'parameter-contexts' ||
+            policyComponentState.resource === 'parameter-providers'
+        ) {
+            switch (option.value) {
+                case 'read-data':
+                case 'write-data':
+                case 'write-send-data':
+                case 'write-receive-data':
+                case 'read-provenance-data':
+                case 'write-operation':
+                    return false;
+            }
+        } else if (policyComponentState.resource === 'labels') {
+            switch (option.value) {
+                case 'write-operation':
+                case 'read-data':
+                case 'write-data':
+                case 'write-send-data':
+                case 'write-receive-data':
+                    return false;
+            }
+        } else if (policyComponentState.resource === 'input-ports' && 
policyComponentState.allowRemoteAccess) {
+            // if input ports allow remote access, disable send data. if input 
ports do not allow remote
+            // access it will fall through to the else block where both send 
and receive data will be disabled
+            switch (option.value) {
+                case 'write-send-data':
+                    return false;
+            }
+        } else if (policyComponentState.resource === 'output-ports' && 
policyComponentState.allowRemoteAccess) {
+            // if output ports allow remote access, disable receive data. if 
output ports do not allow remote
+            // access it will fall through to the else block where both send 
and receive data will be disabled
+            switch (option.value) {
+                case 'write-receive-data':
+                    return false;
+            }
+        } else {
+            switch (option.value) {
+                case 'write-send-data':
+                case 'write-receive-data':
+                    return false;
+            }
+        }
+
+        // enable all other options
+        return true;
+    }
+
+    getSelectOptionTipData(option: SelectOption): TextTipInput {
+        return {
+            // @ts-ignore
+            text: option.description
+        };
+    }
+
+    getContextIcon(): string {
+        switch (this.resource) {
+            case 'processors':
+                return 'icon-processor';
+            case 'input-ports':
+                return 'icon-port-in';
+            case 'output-ports':
+                return 'icon-port-out';
+            case 'funnels':
+                return 'icon-funnel';
+            case 'labels':
+                return 'icon-label';
+            case 'remote-process-groups':
+                return 'icon-group-remote';
+        }
+
+        return 'icon-group';
+    }
+
+    getContextType(): string {
+        switch (this.resource) {
+            case 'processors':
+                return 'Processor';
+            case 'input-ports':
+                return 'Input Ports';
+            case 'output-ports':
+                return 'Output Ports';
+            case 'funnels':
+                return 'Funnel';
+            case 'labels':
+                return 'Label';
+            case 'remote-process-groups':
+                return 'Remote Process Group';
+        }
+
+        return 'Process Group';

Review Comment:
   No case for `parameter-contexts`?



##########
nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-frontend/src/main/nifi/src/app/pages/access-policies/ui/global-access-policies/global-access-policies.component.html:
##########
@@ -0,0 +1,155 @@
+<!--
+  ~  Licensed to the Apache Software Foundation (ASF) under one or more
+  ~  contributor license agreements.  See the NOTICE file distributed with
+  ~  this work for additional information regarding copyright ownership.
+  ~  The ASF licenses this file to You under the Apache License, Version 2.0
+  ~  (the "License"); you may not use this file except in compliance with
+  ~  the License.  You may obtain a copy of the License at
+  ~
+  ~     http://www.apache.org/licenses/LICENSE-2.0
+  ~
+  ~  Unless required by applicable law or agreed to in writing, software
+  ~  distributed under the License is distributed on an "AS IS" BASIS,
+  ~  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+  ~  See the License for the specific language governing permissions and
+  ~  limitations under the License.
+  -->
+
+<ng-container *ngIf="accessPolicyState$ | async; let accessPolicyState">
+    <div *ngIf="isInitialLoading(accessPolicyState); else loaded">
+        <ngx-skeleton-loader count="3"></ngx-skeleton-loader>
+    </div>
+
+    <ng-template #loaded>
+        <div
+            class="global-access-policies flex flex-col h-full gap-y-2"
+            *ngIf="flowConfiguration$ | async; let flowConfiguration">
+            <div class="value">
+                <div class="mb-2" [ngSwitch]="accessPolicyState.policyStatus">
+                    <ng-container *ngSwitchCase="PolicyStatus.NotFound">
+                        No policy for the specified resource.
+                        <ng-container 
*ngIf="flowConfiguration.supportsConfigurableAuthorizer">
+                            <a (click)="createNewPolicy()">Create</a> a new 
policy.
+                        </ng-container>
+                    </ng-container>
+                    <ng-container *ngSwitchCase="PolicyStatus.Inherited">
+                        <ng-container *ngIf="accessPolicyState.accessPolicy">
+                            <ng-container
+                                *ngTemplateOutlet="
+                                    
getTemplateForInheritedPolicy(accessPolicyState.accessPolicy);
+                                    context: {
+                                        $implicit: 
accessPolicyState.accessPolicy,
+                                        supportsConfigurableAuthorizer: 
flowConfiguration.supportsConfigurableAuthorizer
+                                    }
+                                "></ng-container>
+                        </ng-container>
+                    </ng-container>
+                    <ng-container *ngSwitchCase="PolicyStatus.Forbidden">
+                        Not authorized to access the policy for the specified 
resource.
+                    </ng-container>
+                </div>
+            </div>
+            <div class="flex justify-between items-center">
+                <form [formGroup]="policyForm">
+                    <div class="flex gap-x-2">
+                        <div class="resource-select">
+                            <mat-form-field>
+                                <mat-label>Policy</mat-label>
+                                <mat-select
+                                    formControlName="resource"
+                                    
(selectionChange)="resourceChanged($event.value)">
+                                    <mat-option
+                                        *ngFor="let option of resourceOptions"
+                                        [value]="option.value"
+                                        nifiTooltip
+                                        [tooltipComponentType]="TextTip"
+                                        
[tooltipInputData]="getSelectOptionTipData(option)"
+                                        [delayClose]="false"
+                                        >{{ option.text }}
+                                    </mat-option>
+                                </mat-select>
+                            </mat-form-field>
+                        </div>
+                        <div class="resource-identifier-select" 
*ngIf="supportsResourceIdentifier">
+                            <mat-form-field>
+                                <mat-label>Option</mat-label>
+                                <mat-select
+                                    formControlName="resourceIdentifier"
+                                    
(selectionChange)="resourceIdentifierChanged()">
+                                    <mat-option
+                                        *ngFor="let option of 
requiredPermissionOptions"
+                                        [value]="option.value"
+                                        nifiTooltip
+                                        [tooltipComponentType]="TextTip"
+                                        
[tooltipInputData]="getSelectOptionTipData(option)"
+                                        [delayClose]="false"
+                                        >{{ option.text }}
+                                    </mat-option>
+                                </mat-select>
+                            </mat-form-field>
+                        </div>
+                        <div class="action-select" 
[class.hidden]="!supportsReadWriteAction">
+                            <mat-form-field>
+                                <mat-label>Action</mat-label>
+                                <mat-select formControlName="action" 
(selectionChange)="actionChanged()">
+                                    <mat-option 
[value]="Action.Read">view</mat-option>
+                                    <mat-option 
[value]="Action.Write">modify</mat-option>
+                                </mat-select>
+                            </mat-form-field>
+                        </div>
+                    </div>
+                </form>
+                <div *ngIf="flowConfiguration.supportsConfigurableAuthorizer" 
class="flex gap-x-2 items-center">
+                    <button
+                        class="nifi-button"
+                        [disabled]="accessPolicyState.policyStatus !== 
PolicyStatus.Found"
+                        (click)="addTenantToPolicy()">
+                        <i class="fa fa-user-plus"></i>
+                    </button>
+                    <button
+                        class="nifi-button"
+                        [disabled]="accessPolicyState.policyStatus !== 
PolicyStatus.Found"
+                        (click)="deletePolicy()">
+                        <i class="fa fa-trash"></i>
+                    </button>
+                </div>
+            </div>
+            <div class="flex-1 -mt-2" *ngIf="currentUser$ | async as user">
+                <policy-table
+                    [policy]="accessPolicyState.accessPolicy"
+                    [supportsPolicyModification]="
+                        flowConfiguration.supportsConfigurableAuthorizer &&
+                        accessPolicyState.policyStatus === PolicyStatus.Found
+                    "
+                    
(removeTenantFromPolicy)="removeTenantFromPolicy($event)"></policy-table>
+            </div>
+            <div class="flex justify-between">
+                <div class="refresh-container flex items-center gap-x-2">
+                    <button class="nifi-button" 
(click)="refreshGlobalAccessPolicy()">
+                        <i class="fa fa-refresh" 
[class.fa-spin]="accessPolicyState.status === 'loading'"></i>
+                    </button>
+                    <div>Last updated:</div>
+                    <div class="refresh-timestamp">{{ 
accessPolicyState.loadedTimestamp }}</div>
+                </div>
+            </div>
+        </div>
+    </ng-template>
+</ng-container>
+<ng-template #inheritedFromPolicies let-policy 
let-supportsConfigurableAuthorizer>

Review Comment:
   only a single implicit parameter is allowed.
   ```suggestion
   <ng-template #inheritedFromPolicies let-policy 
let-supportsConfigurableAuthorizer=supportsConfigurableAuthorizer>
   ```



##########
nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-frontend/src/main/nifi/src/app/pages/access-policies/ui/component-access-policies/component-access-policies.component.html:
##########
@@ -0,0 +1,147 @@
+<!--
+  ~  Licensed to the Apache Software Foundation (ASF) under one or more
+  ~  contributor license agreements.  See the NOTICE file distributed with
+  ~  this work for additional information regarding copyright ownership.
+  ~  The ASF licenses this file to You under the Apache License, Version 2.0
+  ~  (the "License"); you may not use this file except in compliance with
+  ~  the License.  You may obtain a copy of the License at
+  ~
+  ~     http://www.apache.org/licenses/LICENSE-2.0
+  ~
+  ~  Unless required by applicable law or agreed to in writing, software
+  ~  distributed under the License is distributed on an "AS IS" BASIS,
+  ~  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+  ~  See the License for the specific language governing permissions and
+  ~  limitations under the License.
+  -->
+
+<ng-container *ngIf="accessPolicyState$ | async; let accessPolicyState">
+    <div *ngIf="isInitialLoading(accessPolicyState); else loaded">
+        <ngx-skeleton-loader count="3"></ngx-skeleton-loader>
+    </div>
+
+    <ng-template #loaded>
+        <ng-container *ngIf="policyComponentState$ | async; let 
policyComponentState">
+            <div
+                class="component-access-policies flex flex-col h-full gap-y-2"
+                *ngIf="flowConfiguration$ | async; let flowConfiguration">
+                <div class="value">
+                    <div class="mb-2" 
[ngSwitch]="accessPolicyState.policyStatus">
+                        <ng-container *ngSwitchCase="PolicyStatus.NotFound">
+                            No policy for the specified resource.
+                            <ng-container 
*ngIf="flowConfiguration.supportsConfigurableAuthorizer">
+                                <a (click)="createNewPolicy()">Create</a> a 
new policy.
+                            </ng-container>
+                        </ng-container>
+                        <ng-container *ngSwitchCase="PolicyStatus.Inherited">
+                            <ng-container 
*ngIf="accessPolicyState.accessPolicy">
+                                <ng-container
+                                    *ngTemplateOutlet="
+                                        
getTemplateForInheritedPolicy(accessPolicyState.accessPolicy);
+                                        context: {
+                                            $implicit: 
accessPolicyState.accessPolicy,
+                                            supportsConfigurableAuthorizer:
+                                                
flowConfiguration.supportsConfigurableAuthorizer
+                                        }
+                                    "></ng-container>
+                            </ng-container>
+                        </ng-container>
+                        <ng-container *ngSwitchCase="PolicyStatus.Forbidden">
+                            Not authorized to access the policy for the 
specified resource.
+                        </ng-container>
+                    </div>
+                </div>
+                <div class="flex justify-between items-center">
+                    <form [formGroup]="policyForm">
+                        <div class="flex gap-x-2">
+                            <div class="flex gap-x-1 -mt-2">
+                                <div class="operation-context-logo flex 
flex-col">
+                                    <i class="icon" 
[class]="getContextIcon()"></i>
+                                </div>
+                                <div class="flex flex-col">
+                                    <div class="operation-context-name">{{ 
policyComponentState.label }}</div>
+                                    <div class="operation-context-type">{{ 
getContextType() }}</div>
+                                </div>
+                            </div>
+                            <div class="policy-select">
+                                <mat-form-field>
+                                    <mat-label>Policy</mat-label>
+                                    <mat-select
+                                        formControlName="policyAction"
+                                        
(selectionChange)="policyActionChanged($event.value)">
+                                        <mat-option
+                                            *ngFor="let option of 
policyActionOptions"
+                                            
[disabled]="!isComponentPolicy(option, policyComponentState)"
+                                            [value]="option.value"
+                                            nifiTooltip
+                                            [tooltipComponentType]="TextTip"
+                                            
[tooltipInputData]="getSelectOptionTipData(option)"
+                                            [delayClose]="false"
+                                            >{{ option.text }}
+                                        </mat-option>
+                                    </mat-select>
+                                </mat-form-field>
+                            </div>
+                        </div>
+                    </form>
+                    <div 
*ngIf="flowConfiguration.supportsConfigurableAuthorizer" class="flex gap-x-2">
+                        <button
+                            class="nifi-button"
+                            [disabled]="accessPolicyState.policyStatus !== 
PolicyStatus.Found"
+                            (click)="addTenantToPolicy()">
+                            <i class="fa fa-user-plus"></i>
+                        </button>
+                        <button
+                            class="nifi-button"
+                            [disabled]="accessPolicyState.policyStatus !== 
PolicyStatus.Found"
+                            (click)="deletePolicy()">
+                            <i class="fa fa-trash"></i>
+                        </button>
+                    </div>
+                </div>
+                <div class="flex-1 -mt-2" *ngIf="currentUser$ | async as user">
+                    <policy-table
+                        [policy]="accessPolicyState.accessPolicy"
+                        [supportsPolicyModification]="
+                            flowConfiguration.supportsConfigurableAuthorizer &&
+                            accessPolicyState.policyStatus === 
PolicyStatus.Found
+                        "
+                        
(removeTenantFromPolicy)="removeTenantFromPolicy($event)"></policy-table>
+                </div>
+                <div class="flex justify-between">
+                    <div class="refresh-container flex items-center gap-x-2">
+                        <button class="nifi-button" 
(click)="refreshGlobalAccessPolicy()">
+                            <i class="fa fa-refresh" 
[class.fa-spin]="accessPolicyState.status === 'loading'"></i>
+                        </button>
+                        <div>Last updated:</div>
+                        <div class="refresh-timestamp">{{ 
accessPolicyState.loadedTimestamp }}</div>
+                    </div>
+                </div>
+            </div>
+        </ng-container>
+    </ng-template>
+</ng-container>
+<ng-template #inheritedFromPolicies let-policy 
let-supportsConfigurableAuthorizer>
+    No component specific administrators.
+    <ng-container *ngIf="supportsConfigurableAuthorizer">
+        <a (click)="createNewPolicy()">Add</a> policy for additional 
administrators.
+    </ng-container>
+</ng-template>
+<ng-template #inheritedFromController let-policy 
let-supportsConfigurableAuthorizer>
+    Showing effective policy inherited from the controller.
+    <ng-container *ngIf="supportsConfigurableAuthorizer">
+        <a (click)="createNewPolicy()">Override</a> this policy.
+    </ng-container>
+</ng-template>
+<ng-template #inheritedFromGlobalParameterContexts let-policy 
let-supportsConfigurableAuthorizer>

Review Comment:
   only 1 implicit parameter supported



##########
nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-frontend/src/main/nifi/src/app/pages/access-policies/ui/component-access-policies/component-access-policies.component.html:
##########
@@ -0,0 +1,147 @@
+<!--
+  ~  Licensed to the Apache Software Foundation (ASF) under one or more
+  ~  contributor license agreements.  See the NOTICE file distributed with
+  ~  this work for additional information regarding copyright ownership.
+  ~  The ASF licenses this file to You under the Apache License, Version 2.0
+  ~  (the "License"); you may not use this file except in compliance with
+  ~  the License.  You may obtain a copy of the License at
+  ~
+  ~     http://www.apache.org/licenses/LICENSE-2.0
+  ~
+  ~  Unless required by applicable law or agreed to in writing, software
+  ~  distributed under the License is distributed on an "AS IS" BASIS,
+  ~  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+  ~  See the License for the specific language governing permissions and
+  ~  limitations under the License.
+  -->
+
+<ng-container *ngIf="accessPolicyState$ | async; let accessPolicyState">
+    <div *ngIf="isInitialLoading(accessPolicyState); else loaded">
+        <ngx-skeleton-loader count="3"></ngx-skeleton-loader>
+    </div>
+
+    <ng-template #loaded>
+        <ng-container *ngIf="policyComponentState$ | async; let 
policyComponentState">
+            <div
+                class="component-access-policies flex flex-col h-full gap-y-2"
+                *ngIf="flowConfiguration$ | async; let flowConfiguration">
+                <div class="value">
+                    <div class="mb-2" 
[ngSwitch]="accessPolicyState.policyStatus">
+                        <ng-container *ngSwitchCase="PolicyStatus.NotFound">
+                            No policy for the specified resource.
+                            <ng-container 
*ngIf="flowConfiguration.supportsConfigurableAuthorizer">
+                                <a (click)="createNewPolicy()">Create</a> a 
new policy.
+                            </ng-container>
+                        </ng-container>
+                        <ng-container *ngSwitchCase="PolicyStatus.Inherited">
+                            <ng-container 
*ngIf="accessPolicyState.accessPolicy">
+                                <ng-container
+                                    *ngTemplateOutlet="
+                                        
getTemplateForInheritedPolicy(accessPolicyState.accessPolicy);
+                                        context: {
+                                            $implicit: 
accessPolicyState.accessPolicy,
+                                            supportsConfigurableAuthorizer:
+                                                
flowConfiguration.supportsConfigurableAuthorizer
+                                        }
+                                    "></ng-container>
+                            </ng-container>
+                        </ng-container>
+                        <ng-container *ngSwitchCase="PolicyStatus.Forbidden">
+                            Not authorized to access the policy for the 
specified resource.
+                        </ng-container>
+                    </div>
+                </div>
+                <div class="flex justify-between items-center">
+                    <form [formGroup]="policyForm">
+                        <div class="flex gap-x-2">
+                            <div class="flex gap-x-1 -mt-2">
+                                <div class="operation-context-logo flex 
flex-col">
+                                    <i class="icon" 
[class]="getContextIcon()"></i>
+                                </div>
+                                <div class="flex flex-col">
+                                    <div class="operation-context-name">{{ 
policyComponentState.label }}</div>
+                                    <div class="operation-context-type">{{ 
getContextType() }}</div>
+                                </div>
+                            </div>
+                            <div class="policy-select">
+                                <mat-form-field>
+                                    <mat-label>Policy</mat-label>
+                                    <mat-select
+                                        formControlName="policyAction"
+                                        
(selectionChange)="policyActionChanged($event.value)">
+                                        <mat-option
+                                            *ngFor="let option of 
policyActionOptions"
+                                            
[disabled]="!isComponentPolicy(option, policyComponentState)"
+                                            [value]="option.value"
+                                            nifiTooltip
+                                            [tooltipComponentType]="TextTip"
+                                            
[tooltipInputData]="getSelectOptionTipData(option)"
+                                            [delayClose]="false"
+                                            >{{ option.text }}
+                                        </mat-option>
+                                    </mat-select>
+                                </mat-form-field>
+                            </div>
+                        </div>
+                    </form>
+                    <div 
*ngIf="flowConfiguration.supportsConfigurableAuthorizer" class="flex gap-x-2">
+                        <button
+                            class="nifi-button"
+                            [disabled]="accessPolicyState.policyStatus !== 
PolicyStatus.Found"
+                            (click)="addTenantToPolicy()">
+                            <i class="fa fa-user-plus"></i>
+                        </button>
+                        <button
+                            class="nifi-button"
+                            [disabled]="accessPolicyState.policyStatus !== 
PolicyStatus.Found"

Review Comment:
   add a title. it is unclear initially that this trashcan will delete the 
entire policy and not the selected user/group in the list.



##########
nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-frontend/src/main/nifi/src/app/pages/access-policies/state/access-policy/access-policy.selectors.ts:
##########
@@ -0,0 +1,66 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one or more
+ *  contributor license agreements.  See the NOTICE file distributed with
+ *  this work for additional information regarding copyright ownership.
+ *  The ASF licenses this file to You under the Apache License, Version 2.0
+ *  (the "License"); you may not use this file except in compliance with
+ *  the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing, software
+ *  distributed under the License is distributed on an "AS IS" BASIS,
+ *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ *  See the License for the specific language governing permissions and
+ *  limitations under the License.
+ */
+
+import { createSelector } from '@ngrx/store';
+import { AccessPoliciesState, selectAccessPoliciesState } from '../index';
+import { accessPolicyFeatureKey, AccessPolicyState } from './index';
+import { selectCurrentRoute } from '../../../../state/router/router.selectors';
+import { ComponentResourceAction, ResourceAction } from '../shared';
+
+export const selectAccessPolicyState = createSelector(
+    selectAccessPoliciesState,
+    (state: AccessPoliciesState) => state[accessPolicyFeatureKey]
+);
+
+export const selectResourceAction = createSelector(
+    selectAccessPolicyState,
+    (state: AccessPolicyState) => state.resourceAction
+);
+
+export const selectAccessPolicy = createSelector(
+    selectAccessPolicyState,
+    (state: AccessPolicyState) => state.accessPolicy
+);
+
+export const selectSaving = createSelector(selectAccessPolicyState, (state: 
AccessPolicyState) => state.saving);
+
+export const selectGlobalResourceActionFromRoute = 
createSelector(selectCurrentRoute, (route) => {
+    let selectedResourceAction: ResourceAction | null = null;
+    if (route?.params.action && route?.params.resource) {
+        // always select the action and resource from the route
+        selectedResourceAction = {
+            action: route.params.action,
+            resource: route.params.resource,
+            resourceIdentifier: route.params.resourceIdentifier
+        };
+    }
+    return selectedResourceAction;
+});
+
+export const selectComponentResourceActionFromRoute = 
createSelector(selectCurrentRoute, (route) => {
+    let selectedResourceAction: ComponentResourceAction | null = null;
+    if (route?.params.action && route?.params.policy && route?.params.resource 
&& route?.params.resourceIdentifier) {
+        // always select the action and resource from the route
+        selectedResourceAction = {
+            action: route.params.action,
+            policy: route.params.policy,
+            resource: route.params.resource,
+            resourceIdentifier: route.params.resourceIdentifier
+        };
+    }

Review Comment:
   This is getting triggered after (or during) the component policy screen was 
requested to be closed. and after the state was reset. this results in the 
`accessPolicies/policyComponent` section of the store to get re-populated. 
Doesn't seem that should be happening.



##########
nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-frontend/src/main/nifi/src/app/pages/access-policies/feature/access-policies.component.ts:
##########
@@ -0,0 +1,38 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one or more
+ *  contributor license agreements.  See the NOTICE file distributed with
+ *  this work for additional information regarding copyright ownership.
+ *  The ASF licenses this file to You under the Apache License, Version 2.0
+ *  (the "License"); you may not use this file except in compliance with
+ *  the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing, software
+ *  distributed under the License is distributed on an "AS IS" BASIS,
+ *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ *  See the License for the specific language governing permissions and
+ *  limitations under the License.
+ */
+
+import { Component, OnDestroy, OnInit } from '@angular/core';
+import { Store } from '@ngrx/store';
+import { NiFiState } from '../../../state';
+import { startCurrentUserPolling, stopCurrentUserPolling } from 
'../../../state/current-user/current-user.actions';
+
+@Component({
+    selector: 'access-policies',
+    templateUrl: './access-policies.component.html',
+    styleUrls: ['./access-policies.component.scss']
+})
+export class AccessPolicies implements OnInit, OnDestroy {
+    constructor(private store: Store<NiFiState>) {}
+
+    ngOnInit(): void {
+        this.store.dispatch(startCurrentUserPolling());
+    }
+
+    ngOnDestroy(): void {
+        this.store.dispatch(stopCurrentUserPolling());

Review Comment:
   We should consider forcing a refresh of the currentUser on destroy. The way 
it works now, if the user makes a change to their own user policies and closes 
the policies page, they must wait for the flow canvas to refresh the current 
user which will take 30 seconds or so. This can lead the user to think their 
policy change didn't take effect immediately.



##########
nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-frontend/src/main/nifi/src/app/pages/access-policies/ui/global-access-policies/global-access-policies.component.html:
##########
@@ -0,0 +1,155 @@
+<!--
+  ~  Licensed to the Apache Software Foundation (ASF) under one or more
+  ~  contributor license agreements.  See the NOTICE file distributed with
+  ~  this work for additional information regarding copyright ownership.
+  ~  The ASF licenses this file to You under the Apache License, Version 2.0
+  ~  (the "License"); you may not use this file except in compliance with
+  ~  the License.  You may obtain a copy of the License at
+  ~
+  ~     http://www.apache.org/licenses/LICENSE-2.0
+  ~
+  ~  Unless required by applicable law or agreed to in writing, software
+  ~  distributed under the License is distributed on an "AS IS" BASIS,
+  ~  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+  ~  See the License for the specific language governing permissions and
+  ~  limitations under the License.
+  -->
+
+<ng-container *ngIf="accessPolicyState$ | async; let accessPolicyState">
+    <div *ngIf="isInitialLoading(accessPolicyState); else loaded">
+        <ngx-skeleton-loader count="3"></ngx-skeleton-loader>
+    </div>
+
+    <ng-template #loaded>
+        <div
+            class="global-access-policies flex flex-col h-full gap-y-2"
+            *ngIf="flowConfiguration$ | async; let flowConfiguration">
+            <div class="value">
+                <div class="mb-2" [ngSwitch]="accessPolicyState.policyStatus">
+                    <ng-container *ngSwitchCase="PolicyStatus.NotFound">
+                        No policy for the specified resource.
+                        <ng-container 
*ngIf="flowConfiguration.supportsConfigurableAuthorizer">
+                            <a (click)="createNewPolicy()">Create</a> a new 
policy.
+                        </ng-container>
+                    </ng-container>
+                    <ng-container *ngSwitchCase="PolicyStatus.Inherited">
+                        <ng-container *ngIf="accessPolicyState.accessPolicy">
+                            <ng-container
+                                *ngTemplateOutlet="
+                                    
getTemplateForInheritedPolicy(accessPolicyState.accessPolicy);
+                                    context: {
+                                        $implicit: 
accessPolicyState.accessPolicy,
+                                        supportsConfigurableAuthorizer: 
flowConfiguration.supportsConfigurableAuthorizer
+                                    }
+                                "></ng-container>
+                        </ng-container>
+                    </ng-container>
+                    <ng-container *ngSwitchCase="PolicyStatus.Forbidden">
+                        Not authorized to access the policy for the specified 
resource.
+                    </ng-container>
+                </div>
+            </div>
+            <div class="flex justify-between items-center">
+                <form [formGroup]="policyForm">
+                    <div class="flex gap-x-2">
+                        <div class="resource-select">
+                            <mat-form-field>
+                                <mat-label>Policy</mat-label>
+                                <mat-select
+                                    formControlName="resource"
+                                    
(selectionChange)="resourceChanged($event.value)">
+                                    <mat-option
+                                        *ngFor="let option of resourceOptions"
+                                        [value]="option.value"
+                                        nifiTooltip
+                                        [tooltipComponentType]="TextTip"
+                                        
[tooltipInputData]="getSelectOptionTipData(option)"
+                                        [delayClose]="false"
+                                        >{{ option.text }}
+                                    </mat-option>
+                                </mat-select>
+                            </mat-form-field>
+                        </div>
+                        <div class="resource-identifier-select" 
*ngIf="supportsResourceIdentifier">
+                            <mat-form-field>
+                                <mat-label>Option</mat-label>
+                                <mat-select
+                                    formControlName="resourceIdentifier"
+                                    
(selectionChange)="resourceIdentifierChanged()">
+                                    <mat-option
+                                        *ngFor="let option of 
requiredPermissionOptions"
+                                        [value]="option.value"
+                                        nifiTooltip
+                                        [tooltipComponentType]="TextTip"
+                                        
[tooltipInputData]="getSelectOptionTipData(option)"
+                                        [delayClose]="false"
+                                        >{{ option.text }}
+                                    </mat-option>
+                                </mat-select>
+                            </mat-form-field>
+                        </div>
+                        <div class="action-select" 
[class.hidden]="!supportsReadWriteAction">
+                            <mat-form-field>
+                                <mat-label>Action</mat-label>
+                                <mat-select formControlName="action" 
(selectionChange)="actionChanged()">
+                                    <mat-option 
[value]="Action.Read">view</mat-option>
+                                    <mat-option 
[value]="Action.Write">modify</mat-option>
+                                </mat-select>
+                            </mat-form-field>
+                        </div>
+                    </div>
+                </form>
+                <div *ngIf="flowConfiguration.supportsConfigurableAuthorizer" 
class="flex gap-x-2 items-center">
+                    <button
+                        class="nifi-button"
+                        [disabled]="accessPolicyState.policyStatus !== 
PolicyStatus.Found"
+                        (click)="addTenantToPolicy()">
+                        <i class="fa fa-user-plus"></i>
+                    </button>
+                    <button
+                        class="nifi-button"
+                        [disabled]="accessPolicyState.policyStatus !== 
PolicyStatus.Found"
+                        (click)="deletePolicy()">
+                        <i class="fa fa-trash"></i>
+                    </button>

Review Comment:
   add a title to this button to clarify that the action is deleting the policy 
and not just deleting the selected item from the table below.
   ```suggestion
                       <button
                           class="nifi-button"
                           title="Delete this policy"
                           [disabled]="accessPolicyState.policyStatus !== 
PolicyStatus.Found"
                           (click)="deletePolicy()">
                           <i class="fa fa-trash"></i>
                       </button>
   ```



##########
nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-frontend/src/main/nifi/src/app/pages/access-policies/ui/global-access-policies/global-access-policies.component.html:
##########
@@ -0,0 +1,155 @@
+<!--
+  ~  Licensed to the Apache Software Foundation (ASF) under one or more
+  ~  contributor license agreements.  See the NOTICE file distributed with
+  ~  this work for additional information regarding copyright ownership.
+  ~  The ASF licenses this file to You under the Apache License, Version 2.0
+  ~  (the "License"); you may not use this file except in compliance with
+  ~  the License.  You may obtain a copy of the License at
+  ~
+  ~     http://www.apache.org/licenses/LICENSE-2.0
+  ~
+  ~  Unless required by applicable law or agreed to in writing, software
+  ~  distributed under the License is distributed on an "AS IS" BASIS,
+  ~  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+  ~  See the License for the specific language governing permissions and
+  ~  limitations under the License.
+  -->
+
+<ng-container *ngIf="accessPolicyState$ | async; let accessPolicyState">
+    <div *ngIf="isInitialLoading(accessPolicyState); else loaded">
+        <ngx-skeleton-loader count="3"></ngx-skeleton-loader>
+    </div>
+
+    <ng-template #loaded>
+        <div
+            class="global-access-policies flex flex-col h-full gap-y-2"
+            *ngIf="flowConfiguration$ | async; let flowConfiguration">
+            <div class="value">
+                <div class="mb-2" [ngSwitch]="accessPolicyState.policyStatus">
+                    <ng-container *ngSwitchCase="PolicyStatus.NotFound">
+                        No policy for the specified resource.
+                        <ng-container 
*ngIf="flowConfiguration.supportsConfigurableAuthorizer">
+                            <a (click)="createNewPolicy()">Create</a> a new 
policy.
+                        </ng-container>
+                    </ng-container>
+                    <ng-container *ngSwitchCase="PolicyStatus.Inherited">
+                        <ng-container *ngIf="accessPolicyState.accessPolicy">
+                            <ng-container
+                                *ngTemplateOutlet="
+                                    
getTemplateForInheritedPolicy(accessPolicyState.accessPolicy);
+                                    context: {
+                                        $implicit: 
accessPolicyState.accessPolicy,
+                                        supportsConfigurableAuthorizer: 
flowConfiguration.supportsConfigurableAuthorizer
+                                    }
+                                "></ng-container>
+                        </ng-container>
+                    </ng-container>
+                    <ng-container *ngSwitchCase="PolicyStatus.Forbidden">
+                        Not authorized to access the policy for the specified 
resource.
+                    </ng-container>
+                </div>
+            </div>
+            <div class="flex justify-between items-center">
+                <form [formGroup]="policyForm">
+                    <div class="flex gap-x-2">
+                        <div class="resource-select">
+                            <mat-form-field>
+                                <mat-label>Policy</mat-label>
+                                <mat-select
+                                    formControlName="resource"
+                                    
(selectionChange)="resourceChanged($event.value)">
+                                    <mat-option
+                                        *ngFor="let option of resourceOptions"
+                                        [value]="option.value"
+                                        nifiTooltip
+                                        [tooltipComponentType]="TextTip"
+                                        
[tooltipInputData]="getSelectOptionTipData(option)"
+                                        [delayClose]="false"
+                                        >{{ option.text }}
+                                    </mat-option>
+                                </mat-select>
+                            </mat-form-field>
+                        </div>
+                        <div class="resource-identifier-select" 
*ngIf="supportsResourceIdentifier">
+                            <mat-form-field>
+                                <mat-label>Option</mat-label>
+                                <mat-select
+                                    formControlName="resourceIdentifier"
+                                    
(selectionChange)="resourceIdentifierChanged()">
+                                    <mat-option
+                                        *ngFor="let option of 
requiredPermissionOptions"
+                                        [value]="option.value"
+                                        nifiTooltip
+                                        [tooltipComponentType]="TextTip"
+                                        
[tooltipInputData]="getSelectOptionTipData(option)"
+                                        [delayClose]="false"
+                                        >{{ option.text }}
+                                    </mat-option>
+                                </mat-select>
+                            </mat-form-field>
+                        </div>
+                        <div class="action-select" 
[class.hidden]="!supportsReadWriteAction">
+                            <mat-form-field>
+                                <mat-label>Action</mat-label>
+                                <mat-select formControlName="action" 
(selectionChange)="actionChanged()">
+                                    <mat-option 
[value]="Action.Read">view</mat-option>
+                                    <mat-option 
[value]="Action.Write">modify</mat-option>
+                                </mat-select>
+                            </mat-form-field>
+                        </div>
+                    </div>
+                </form>
+                <div *ngIf="flowConfiguration.supportsConfigurableAuthorizer" 
class="flex gap-x-2 items-center">
+                    <button
+                        class="nifi-button"
+                        [disabled]="accessPolicyState.policyStatus !== 
PolicyStatus.Found"
+                        (click)="addTenantToPolicy()">
+                        <i class="fa fa-user-plus"></i>
+                    </button>

Review Comment:
   add a title to this button to clarify what it does.
   ```suggestion
                       <button
                           class="nifi-button"
                           title="Add users/groups to this policy"
                           [disabled]="accessPolicyState.policyStatus !== 
PolicyStatus.Found"
                           (click)="addTenantToPolicy()">
                           <i class="fa fa-user-plus"></i>
                       </button>
   ```



##########
nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-frontend/src/main/nifi/src/app/pages/access-policies/ui/component-access-policies/component-access-policies.component.ts:
##########
@@ -0,0 +1,427 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one or more
+ *  contributor license agreements.  See the NOTICE file distributed with
+ *  this work for additional information regarding copyright ownership.
+ *  The ASF licenses this file to You under the Apache License, Version 2.0
+ *  (the "License"); you may not use this file except in compliance with
+ *  the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing, software
+ *  distributed under the License is distributed on an "AS IS" BASIS,
+ *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ *  See the License for the specific language governing permissions and
+ *  limitations under the License.
+ */
+
+import { Component, OnDestroy, OnInit, TemplateRef, ViewChild } from 
'@angular/core';
+import { Store } from '@ngrx/store';
+import { selectCurrentUser } from 
'../../../../state/current-user/current-user.selectors';
+import {
+    createAccessPolicy,
+    openAddTenantToPolicyDialog,
+    promptDeleteAccessPolicy,
+    promptRemoveTenantFromPolicy,
+    reloadAccessPolicy,
+    resetAccessPolicyState,
+    selectComponentAccessPolicy,
+    setAccessPolicy
+} from '../../state/access-policy/access-policy.actions';
+import { AccessPolicyState, RemoveTenantFromPolicyRequest } from 
'../../state/access-policy';
+import { initialState } from '../../state/access-policy/access-policy.reducer';
+import {
+    selectAccessPolicyState,
+    selectComponentResourceActionFromRoute
+} from '../../state/access-policy/access-policy.selectors';
+import { filter } from 'rxjs';
+import { takeUntilDestroyed } from '@angular/core/rxjs-interop';
+import { FormBuilder, FormControl, FormGroup, Validators } from 
'@angular/forms';
+import { NiFiCommon } from '../../../../service/nifi-common.service';
+import { ComponentType, SelectOption, TextTipInput } from 
'../../../../state/shared';
+import { TextTip } from 
'../../../../ui/common/tooltips/text-tip/text-tip.component';
+import { AccessPolicyEntity, Action, PolicyStatus, ResourceAction } from 
'../../state/shared';
+import { loadFlowConfiguration } from 
'../../../../state/flow-configuration/flow-configuration.actions';
+import { selectFlowConfiguration } from 
'../../../../state/flow-configuration/flow-configuration.selectors';
+import { loadTenants, resetTenantsState } from 
'../../state/tenants/tenants.actions';
+import { loadPolicyComponent, resetPolicyComponentState } from 
'../../state/policy-component/policy-component.actions';
+import { selectPolicyComponentState } from 
'../../state/policy-component/policy-component.selectors';
+import { PolicyComponentState } from '../../state/policy-component';
+
+@Component({
+    selector: 'global-access-policies',
+    templateUrl: './component-access-policies.component.html',
+    styleUrls: ['./component-access-policies.component.scss']
+})
+export class ComponentAccessPolicies implements OnInit, OnDestroy {
+    flowConfiguration$ = this.store.select(selectFlowConfiguration);
+    accessPolicyState$ = this.store.select(selectAccessPolicyState);
+    policyComponentState$ = this.store.select(selectPolicyComponentState);
+    currentUser$ = this.store.select(selectCurrentUser);
+
+    protected readonly TextTip = TextTip;
+    protected readonly Action = Action;
+    protected readonly PolicyStatus = PolicyStatus;
+    protected readonly ComponentType = ComponentType;
+
+    policyForm: FormGroup;
+    policyActionOptions: SelectOption[] = [
+        {
+            text: 'view the component',
+            value: 'read-component',
+            description: 'Allows users to view component configuration details'
+        },
+        {
+            text: 'modify the component',
+            value: 'write-component',
+            description: 'Allows users to modify component configuration 
details'
+        },
+        {
+            text: 'operate the component',
+            value: 'write-operation',
+            description:
+                'Allows users to operate components by changing component run 
status (start/stop/enable/disable), remote port transmission status, or 
terminating processor threads'
+        },
+        {
+            text: 'view provenance',
+            value: 'read-provenance-data',
+            description: 'Allows users to view provenance events generated by 
this component'
+        },
+        {
+            text: 'view the data',
+            value: 'read-data',
+            description:
+                'Allows users to view metadata and content for this component 
in flowfile queues in outbound connections and through provenance events'
+        },
+        {
+            text: 'modify the data',
+            value: 'write-data',
+            description:
+                'Allows users to empty flowfile queues in outbound connections 
and submit replays through provenance events'
+        },
+        {
+            text: 'receive data via site-to-site',
+            value: 'write-receive-data',
+            description: 'Allows this port to receive data from these NiFi 
instances',
+            disabled: true
+        },
+        {
+            text: 'send data via site-to-site',
+            value: 'write-send-data',
+            description: 'Allows this port to send data to these NiFi 
instances',
+            disabled: true
+        },
+        {
+            text: 'view the policies',
+            value: 'read-policies',
+            description: 'Allows users to view the list of users who can 
view/modify this component'
+        },
+        {
+            text: 'modify the policies',
+            value: 'write-policies',
+            description: 'Allows users to modify the list of users who can 
view/modify this component'
+        }
+    ];
+
+    action!: Action;
+    resource!: string;
+    policy!: string;
+    resourceIdentifier!: string;
+
+    @ViewChild('inheritedFromPolicies') inheritedFromPolicies!: 
TemplateRef<any>;
+    @ViewChild('inheritedFromController') inheritedFromController!: 
TemplateRef<any>;
+    @ViewChild('inheritedFromGlobalParameterContexts') 
inheritedFromGlobalParameterContexts!: TemplateRef<any>;
+    @ViewChild('inheritedFromProcessGroup') inheritedFromProcessGroup!: 
TemplateRef<any>;
+
+    constructor(
+        private store: Store<AccessPolicyState>,
+        private formBuilder: FormBuilder,
+        private nifiCommon: NiFiCommon
+    ) {
+        this.policyForm = this.formBuilder.group({
+            policyAction: new FormControl(this.policyActionOptions[0].value, 
Validators.required)
+        });
+
+        this.store
+            .select(selectComponentResourceActionFromRoute)
+            .pipe(
+                filter((resourceAction) => resourceAction != null),
+                takeUntilDestroyed()
+            )
+            .subscribe((componentResourceAction) => {
+                if (componentResourceAction) {
+                    this.action = componentResourceAction.action;
+                    this.policy = componentResourceAction.policy;
+                    this.resource = componentResourceAction.resource;
+                    this.resourceIdentifier = 
componentResourceAction.resourceIdentifier;
+
+                    // data transfer policies for site to site are presented 
different in the form so
+                    // we need to distinguish by type
+                    let policyForResource: string = this.policy;
+                    if (this.policy === 'data-transfer') {
+                        if (this.resource === 'input-ports') {
+                            policyForResource = 'receive-data';
+                        } else {
+                            policyForResource = 'send-data';
+                        }
+                    }
+
+                    
this.policyForm.get('policyAction')?.setValue(`${this.action}-${policyForResource}`);
+
+                    // component policies are presented simply as 
'/processors/1234' while non-component policies
+                    // like viewing provenance for a specific component is 
presented as `/provenance-data/processors/1234`
+                    let resourceToLoad: string = this.resource;
+                    if (componentResourceAction.policy !== 'component') {
+                        resourceToLoad = `${this.policy}/${this.resource}`;
+                    }
+
+                    const resourceAction: ResourceAction = {
+                        action: this.action,
+                        resource: resourceToLoad,
+                        resourceIdentifier: this.resourceIdentifier
+                    };
+
+                    this.store.dispatch(
+                        loadPolicyComponent({
+                            request: {
+                                componentResourceAction
+                            }
+                        })
+                    );
+                    this.store.dispatch(
+                        setAccessPolicy({
+                            request: {
+                                resourceAction
+                            }
+                        })
+                    );
+                }
+            });
+    }
+
+    ngOnInit(): void {
+        this.store.dispatch(loadFlowConfiguration());
+        this.store.dispatch(loadTenants());
+    }
+
+    isInitialLoading(state: AccessPolicyState): boolean {
+        return state.loadedTimestamp == initialState.loadedTimestamp;
+    }
+
+    isComponentPolicy(option: SelectOption, policyComponentState: 
PolicyComponentState): boolean {
+        // consider the type of component to override which policies shouldn't 
be supported
+
+        if (policyComponentState.resource === 'process-groups') {
+            switch (option.value) {
+                case 'write-send-data':
+                case 'write-receive-data':
+                    return false;
+            }
+        } else if (
+            policyComponentState.resource === 'controller-services' ||
+            policyComponentState.resource === 'reporting-tasks'
+        ) {
+            switch (option.value) {
+                case 'read-data':
+                case 'write-data':
+                case 'write-send-data':
+                case 'write-receive-data':
+                case 'read-provenance-data':
+                    return false;
+            }
+        } else if (
+            policyComponentState.resource === 'parameter-contexts' ||
+            policyComponentState.resource === 'parameter-providers'
+        ) {
+            switch (option.value) {
+                case 'read-data':
+                case 'write-data':
+                case 'write-send-data':
+                case 'write-receive-data':
+                case 'read-provenance-data':
+                case 'write-operation':
+                    return false;
+            }
+        } else if (policyComponentState.resource === 'labels') {
+            switch (option.value) {
+                case 'write-operation':
+                case 'read-data':
+                case 'write-data':
+                case 'write-send-data':
+                case 'write-receive-data':
+                    return false;
+            }
+        } else if (policyComponentState.resource === 'input-ports' && 
policyComponentState.allowRemoteAccess) {
+            // if input ports allow remote access, disable send data. if input 
ports do not allow remote
+            // access it will fall through to the else block where both send 
and receive data will be disabled
+            switch (option.value) {
+                case 'write-send-data':
+                    return false;
+            }
+        } else if (policyComponentState.resource === 'output-ports' && 
policyComponentState.allowRemoteAccess) {
+            // if output ports allow remote access, disable receive data. if 
output ports do not allow remote
+            // access it will fall through to the else block where both send 
and receive data will be disabled
+            switch (option.value) {
+                case 'write-receive-data':
+                    return false;
+            }
+        } else {
+            switch (option.value) {
+                case 'write-send-data':
+                case 'write-receive-data':
+                    return false;
+            }
+        }
+
+        // enable all other options
+        return true;
+    }
+
+    getSelectOptionTipData(option: SelectOption): TextTipInput {
+        return {
+            // @ts-ignore
+            text: option.description
+        };
+    }
+
+    getContextIcon(): string {
+        switch (this.resource) {
+            case 'processors':
+                return 'icon-processor';
+            case 'input-ports':
+                return 'icon-port-in';
+            case 'output-ports':
+                return 'icon-port-out';
+            case 'funnels':
+                return 'icon-funnel';
+            case 'labels':
+                return 'icon-label';
+            case 'remote-process-groups':
+                return 'icon-group-remote';
+        }
+
+        return 'icon-group';

Review Comment:
   No case for `parameter-contexts`?



##########
nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-frontend/src/main/nifi/src/app/pages/access-policies/ui/global-access-policies/global-access-policies.component.html:
##########
@@ -0,0 +1,155 @@
+<!--
+  ~  Licensed to the Apache Software Foundation (ASF) under one or more
+  ~  contributor license agreements.  See the NOTICE file distributed with
+  ~  this work for additional information regarding copyright ownership.
+  ~  The ASF licenses this file to You under the Apache License, Version 2.0
+  ~  (the "License"); you may not use this file except in compliance with
+  ~  the License.  You may obtain a copy of the License at
+  ~
+  ~     http://www.apache.org/licenses/LICENSE-2.0
+  ~
+  ~  Unless required by applicable law or agreed to in writing, software
+  ~  distributed under the License is distributed on an "AS IS" BASIS,
+  ~  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+  ~  See the License for the specific language governing permissions and
+  ~  limitations under the License.
+  -->
+
+<ng-container *ngIf="accessPolicyState$ | async; let accessPolicyState">
+    <div *ngIf="isInitialLoading(accessPolicyState); else loaded">
+        <ngx-skeleton-loader count="3"></ngx-skeleton-loader>
+    </div>
+
+    <ng-template #loaded>
+        <div
+            class="global-access-policies flex flex-col h-full gap-y-2"
+            *ngIf="flowConfiguration$ | async; let flowConfiguration">
+            <div class="value">
+                <div class="mb-2" [ngSwitch]="accessPolicyState.policyStatus">
+                    <ng-container *ngSwitchCase="PolicyStatus.NotFound">
+                        No policy for the specified resource.
+                        <ng-container 
*ngIf="flowConfiguration.supportsConfigurableAuthorizer">
+                            <a (click)="createNewPolicy()">Create</a> a new 
policy.
+                        </ng-container>
+                    </ng-container>
+                    <ng-container *ngSwitchCase="PolicyStatus.Inherited">
+                        <ng-container *ngIf="accessPolicyState.accessPolicy">
+                            <ng-container
+                                *ngTemplateOutlet="
+                                    
getTemplateForInheritedPolicy(accessPolicyState.accessPolicy);
+                                    context: {
+                                        $implicit: 
accessPolicyState.accessPolicy,
+                                        supportsConfigurableAuthorizer: 
flowConfiguration.supportsConfigurableAuthorizer
+                                    }
+                                "></ng-container>
+                        </ng-container>
+                    </ng-container>
+                    <ng-container *ngSwitchCase="PolicyStatus.Forbidden">
+                        Not authorized to access the policy for the specified 
resource.
+                    </ng-container>
+                </div>
+            </div>
+            <div class="flex justify-between items-center">
+                <form [formGroup]="policyForm">
+                    <div class="flex gap-x-2">
+                        <div class="resource-select">
+                            <mat-form-field>
+                                <mat-label>Policy</mat-label>
+                                <mat-select
+                                    formControlName="resource"
+                                    
(selectionChange)="resourceChanged($event.value)">
+                                    <mat-option
+                                        *ngFor="let option of resourceOptions"
+                                        [value]="option.value"
+                                        nifiTooltip
+                                        [tooltipComponentType]="TextTip"
+                                        
[tooltipInputData]="getSelectOptionTipData(option)"
+                                        [delayClose]="false"
+                                        >{{ option.text }}
+                                    </mat-option>
+                                </mat-select>
+                            </mat-form-field>
+                        </div>
+                        <div class="resource-identifier-select" 
*ngIf="supportsResourceIdentifier">
+                            <mat-form-field>
+                                <mat-label>Option</mat-label>
+                                <mat-select
+                                    formControlName="resourceIdentifier"
+                                    
(selectionChange)="resourceIdentifierChanged()">
+                                    <mat-option
+                                        *ngFor="let option of 
requiredPermissionOptions"
+                                        [value]="option.value"
+                                        nifiTooltip
+                                        [tooltipComponentType]="TextTip"
+                                        
[tooltipInputData]="getSelectOptionTipData(option)"
+                                        [delayClose]="false"
+                                        >{{ option.text }}
+                                    </mat-option>
+                                </mat-select>
+                            </mat-form-field>
+                        </div>
+                        <div class="action-select" 
[class.hidden]="!supportsReadWriteAction">
+                            <mat-form-field>
+                                <mat-label>Action</mat-label>
+                                <mat-select formControlName="action" 
(selectionChange)="actionChanged()">
+                                    <mat-option 
[value]="Action.Read">view</mat-option>
+                                    <mat-option 
[value]="Action.Write">modify</mat-option>
+                                </mat-select>
+                            </mat-form-field>
+                        </div>
+                    </div>
+                </form>
+                <div *ngIf="flowConfiguration.supportsConfigurableAuthorizer" 
class="flex gap-x-2 items-center">
+                    <button
+                        class="nifi-button"
+                        [disabled]="accessPolicyState.policyStatus !== 
PolicyStatus.Found"
+                        (click)="addTenantToPolicy()">
+                        <i class="fa fa-user-plus"></i>
+                    </button>
+                    <button
+                        class="nifi-button"
+                        [disabled]="accessPolicyState.policyStatus !== 
PolicyStatus.Found"
+                        (click)="deletePolicy()">
+                        <i class="fa fa-trash"></i>
+                    </button>
+                </div>
+            </div>
+            <div class="flex-1 -mt-2" *ngIf="currentUser$ | async as user">
+                <policy-table
+                    [policy]="accessPolicyState.accessPolicy"
+                    [supportsPolicyModification]="
+                        flowConfiguration.supportsConfigurableAuthorizer &&
+                        accessPolicyState.policyStatus === PolicyStatus.Found
+                    "
+                    
(removeTenantFromPolicy)="removeTenantFromPolicy($event)"></policy-table>
+            </div>
+            <div class="flex justify-between">
+                <div class="refresh-container flex items-center gap-x-2">
+                    <button class="nifi-button" 
(click)="refreshGlobalAccessPolicy()">
+                        <i class="fa fa-refresh" 
[class.fa-spin]="accessPolicyState.status === 'loading'"></i>
+                    </button>
+                    <div>Last updated:</div>
+                    <div class="refresh-timestamp">{{ 
accessPolicyState.loadedTimestamp }}</div>
+                </div>
+            </div>
+        </div>
+    </ng-template>
+</ng-container>
+<ng-template #inheritedFromPolicies let-policy 
let-supportsConfigurableAuthorizer>
+    Showing effective policy inherited from all policies.
+    <ng-container *ngIf="supportsConfigurableAuthorizer">
+        <a (click)="createNewPolicy()">Override</a> this policy.
+    </ng-container>
+</ng-template>
+<ng-template #inheritedFromController let-policy 
let-supportsConfigurableAuthorizer>

Review Comment:
   only a single implicit parameter is allowed.
   ```suggestion
   <ng-template #inheritedFromController let-policy 
let-supportsConfigurableAuthorizer=supportsConfigurableAuthorizer>
   ```



##########
nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-frontend/src/main/nifi/src/app/pages/access-policies/ui/component-access-policies/component-access-policies.component.html:
##########
@@ -0,0 +1,147 @@
+<!--
+  ~  Licensed to the Apache Software Foundation (ASF) under one or more
+  ~  contributor license agreements.  See the NOTICE file distributed with
+  ~  this work for additional information regarding copyright ownership.
+  ~  The ASF licenses this file to You under the Apache License, Version 2.0
+  ~  (the "License"); you may not use this file except in compliance with
+  ~  the License.  You may obtain a copy of the License at
+  ~
+  ~     http://www.apache.org/licenses/LICENSE-2.0
+  ~
+  ~  Unless required by applicable law or agreed to in writing, software
+  ~  distributed under the License is distributed on an "AS IS" BASIS,
+  ~  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+  ~  See the License for the specific language governing permissions and
+  ~  limitations under the License.
+  -->
+
+<ng-container *ngIf="accessPolicyState$ | async; let accessPolicyState">
+    <div *ngIf="isInitialLoading(accessPolicyState); else loaded">
+        <ngx-skeleton-loader count="3"></ngx-skeleton-loader>
+    </div>
+
+    <ng-template #loaded>
+        <ng-container *ngIf="policyComponentState$ | async; let 
policyComponentState">
+            <div
+                class="component-access-policies flex flex-col h-full gap-y-2"
+                *ngIf="flowConfiguration$ | async; let flowConfiguration">
+                <div class="value">
+                    <div class="mb-2" 
[ngSwitch]="accessPolicyState.policyStatus">
+                        <ng-container *ngSwitchCase="PolicyStatus.NotFound">
+                            No policy for the specified resource.
+                            <ng-container 
*ngIf="flowConfiguration.supportsConfigurableAuthorizer">
+                                <a (click)="createNewPolicy()">Create</a> a 
new policy.
+                            </ng-container>
+                        </ng-container>
+                        <ng-container *ngSwitchCase="PolicyStatus.Inherited">
+                            <ng-container 
*ngIf="accessPolicyState.accessPolicy">
+                                <ng-container
+                                    *ngTemplateOutlet="
+                                        
getTemplateForInheritedPolicy(accessPolicyState.accessPolicy);
+                                        context: {
+                                            $implicit: 
accessPolicyState.accessPolicy,
+                                            supportsConfigurableAuthorizer:
+                                                
flowConfiguration.supportsConfigurableAuthorizer
+                                        }
+                                    "></ng-container>
+                            </ng-container>
+                        </ng-container>
+                        <ng-container *ngSwitchCase="PolicyStatus.Forbidden">
+                            Not authorized to access the policy for the 
specified resource.
+                        </ng-container>
+                    </div>
+                </div>
+                <div class="flex justify-between items-center">
+                    <form [formGroup]="policyForm">
+                        <div class="flex gap-x-2">
+                            <div class="flex gap-x-1 -mt-2">
+                                <div class="operation-context-logo flex 
flex-col">
+                                    <i class="icon" 
[class]="getContextIcon()"></i>
+                                </div>
+                                <div class="flex flex-col">
+                                    <div class="operation-context-name">{{ 
policyComponentState.label }}</div>
+                                    <div class="operation-context-type">{{ 
getContextType() }}</div>
+                                </div>
+                            </div>
+                            <div class="policy-select">
+                                <mat-form-field>
+                                    <mat-label>Policy</mat-label>
+                                    <mat-select
+                                        formControlName="policyAction"
+                                        
(selectionChange)="policyActionChanged($event.value)">
+                                        <mat-option
+                                            *ngFor="let option of 
policyActionOptions"
+                                            
[disabled]="!isComponentPolicy(option, policyComponentState)"
+                                            [value]="option.value"
+                                            nifiTooltip
+                                            [tooltipComponentType]="TextTip"
+                                            
[tooltipInputData]="getSelectOptionTipData(option)"
+                                            [delayClose]="false"
+                                            >{{ option.text }}
+                                        </mat-option>
+                                    </mat-select>
+                                </mat-form-field>
+                            </div>
+                        </div>
+                    </form>
+                    <div 
*ngIf="flowConfiguration.supportsConfigurableAuthorizer" class="flex gap-x-2">
+                        <button
+                            class="nifi-button"
+                            [disabled]="accessPolicyState.policyStatus !== 
PolicyStatus.Found"
+                            (click)="addTenantToPolicy()">
+                            <i class="fa fa-user-plus"></i>
+                        </button>
+                        <button
+                            class="nifi-button"
+                            [disabled]="accessPolicyState.policyStatus !== 
PolicyStatus.Found"
+                            (click)="deletePolicy()">
+                            <i class="fa fa-trash"></i>
+                        </button>
+                    </div>
+                </div>
+                <div class="flex-1 -mt-2" *ngIf="currentUser$ | async as user">
+                    <policy-table
+                        [policy]="accessPolicyState.accessPolicy"
+                        [supportsPolicyModification]="
+                            flowConfiguration.supportsConfigurableAuthorizer &&
+                            accessPolicyState.policyStatus === 
PolicyStatus.Found
+                        "
+                        
(removeTenantFromPolicy)="removeTenantFromPolicy($event)"></policy-table>
+                </div>
+                <div class="flex justify-between">
+                    <div class="refresh-container flex items-center gap-x-2">
+                        <button class="nifi-button" 
(click)="refreshGlobalAccessPolicy()">
+                            <i class="fa fa-refresh" 
[class.fa-spin]="accessPolicyState.status === 'loading'"></i>
+                        </button>
+                        <div>Last updated:</div>
+                        <div class="refresh-timestamp">{{ 
accessPolicyState.loadedTimestamp }}</div>
+                    </div>
+                </div>
+            </div>
+        </ng-container>
+    </ng-template>
+</ng-container>
+<ng-template #inheritedFromPolicies let-policy 
let-supportsConfigurableAuthorizer>

Review Comment:
   only 1 implicit parameter supported



##########
nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-frontend/src/main/nifi/src/app/pages/access-policies/ui/global-access-policies/global-access-policies.component.html:
##########
@@ -0,0 +1,155 @@
+<!--
+  ~  Licensed to the Apache Software Foundation (ASF) under one or more
+  ~  contributor license agreements.  See the NOTICE file distributed with
+  ~  this work for additional information regarding copyright ownership.
+  ~  The ASF licenses this file to You under the Apache License, Version 2.0
+  ~  (the "License"); you may not use this file except in compliance with
+  ~  the License.  You may obtain a copy of the License at
+  ~
+  ~     http://www.apache.org/licenses/LICENSE-2.0
+  ~
+  ~  Unless required by applicable law or agreed to in writing, software
+  ~  distributed under the License is distributed on an "AS IS" BASIS,
+  ~  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+  ~  See the License for the specific language governing permissions and
+  ~  limitations under the License.
+  -->
+
+<ng-container *ngIf="accessPolicyState$ | async; let accessPolicyState">
+    <div *ngIf="isInitialLoading(accessPolicyState); else loaded">
+        <ngx-skeleton-loader count="3"></ngx-skeleton-loader>
+    </div>
+
+    <ng-template #loaded>
+        <div
+            class="global-access-policies flex flex-col h-full gap-y-2"
+            *ngIf="flowConfiguration$ | async; let flowConfiguration">
+            <div class="value">
+                <div class="mb-2" [ngSwitch]="accessPolicyState.policyStatus">
+                    <ng-container *ngSwitchCase="PolicyStatus.NotFound">
+                        No policy for the specified resource.
+                        <ng-container 
*ngIf="flowConfiguration.supportsConfigurableAuthorizer">
+                            <a (click)="createNewPolicy()">Create</a> a new 
policy.
+                        </ng-container>
+                    </ng-container>
+                    <ng-container *ngSwitchCase="PolicyStatus.Inherited">
+                        <ng-container *ngIf="accessPolicyState.accessPolicy">
+                            <ng-container
+                                *ngTemplateOutlet="
+                                    
getTemplateForInheritedPolicy(accessPolicyState.accessPolicy);
+                                    context: {
+                                        $implicit: 
accessPolicyState.accessPolicy,
+                                        supportsConfigurableAuthorizer: 
flowConfiguration.supportsConfigurableAuthorizer
+                                    }
+                                "></ng-container>
+                        </ng-container>
+                    </ng-container>
+                    <ng-container *ngSwitchCase="PolicyStatus.Forbidden">
+                        Not authorized to access the policy for the specified 
resource.
+                    </ng-container>
+                </div>
+            </div>
+            <div class="flex justify-between items-center">
+                <form [formGroup]="policyForm">
+                    <div class="flex gap-x-2">
+                        <div class="resource-select">
+                            <mat-form-field>
+                                <mat-label>Policy</mat-label>
+                                <mat-select
+                                    formControlName="resource"
+                                    
(selectionChange)="resourceChanged($event.value)">
+                                    <mat-option
+                                        *ngFor="let option of resourceOptions"
+                                        [value]="option.value"
+                                        nifiTooltip
+                                        [tooltipComponentType]="TextTip"
+                                        
[tooltipInputData]="getSelectOptionTipData(option)"
+                                        [delayClose]="false"
+                                        >{{ option.text }}
+                                    </mat-option>
+                                </mat-select>
+                            </mat-form-field>
+                        </div>
+                        <div class="resource-identifier-select" 
*ngIf="supportsResourceIdentifier">
+                            <mat-form-field>
+                                <mat-label>Option</mat-label>
+                                <mat-select
+                                    formControlName="resourceIdentifier"
+                                    
(selectionChange)="resourceIdentifierChanged()">
+                                    <mat-option
+                                        *ngFor="let option of 
requiredPermissionOptions"
+                                        [value]="option.value"
+                                        nifiTooltip
+                                        [tooltipComponentType]="TextTip"
+                                        
[tooltipInputData]="getSelectOptionTipData(option)"
+                                        [delayClose]="false"
+                                        >{{ option.text }}
+                                    </mat-option>
+                                </mat-select>
+                            </mat-form-field>
+                        </div>
+                        <div class="action-select" 
[class.hidden]="!supportsReadWriteAction">
+                            <mat-form-field>
+                                <mat-label>Action</mat-label>
+                                <mat-select formControlName="action" 
(selectionChange)="actionChanged()">
+                                    <mat-option 
[value]="Action.Read">view</mat-option>
+                                    <mat-option 
[value]="Action.Write">modify</mat-option>
+                                </mat-select>
+                            </mat-form-field>
+                        </div>
+                    </div>
+                </form>
+                <div *ngIf="flowConfiguration.supportsConfigurableAuthorizer" 
class="flex gap-x-2 items-center">
+                    <button
+                        class="nifi-button"
+                        [disabled]="accessPolicyState.policyStatus !== 
PolicyStatus.Found"
+                        (click)="addTenantToPolicy()">
+                        <i class="fa fa-user-plus"></i>
+                    </button>
+                    <button
+                        class="nifi-button"
+                        [disabled]="accessPolicyState.policyStatus !== 
PolicyStatus.Found"
+                        (click)="deletePolicy()">
+                        <i class="fa fa-trash"></i>
+                    </button>
+                </div>
+            </div>
+            <div class="flex-1 -mt-2" *ngIf="currentUser$ | async as user">
+                <policy-table
+                    [policy]="accessPolicyState.accessPolicy"
+                    [supportsPolicyModification]="
+                        flowConfiguration.supportsConfigurableAuthorizer &&
+                        accessPolicyState.policyStatus === PolicyStatus.Found
+                    "
+                    
(removeTenantFromPolicy)="removeTenantFromPolicy($event)"></policy-table>
+            </div>
+            <div class="flex justify-between">
+                <div class="refresh-container flex items-center gap-x-2">
+                    <button class="nifi-button" 
(click)="refreshGlobalAccessPolicy()">
+                        <i class="fa fa-refresh" 
[class.fa-spin]="accessPolicyState.status === 'loading'"></i>
+                    </button>
+                    <div>Last updated:</div>
+                    <div class="refresh-timestamp">{{ 
accessPolicyState.loadedTimestamp }}</div>
+                </div>
+            </div>
+        </div>
+    </ng-template>
+</ng-container>
+<ng-template #inheritedFromPolicies let-policy 
let-supportsConfigurableAuthorizer>
+    Showing effective policy inherited from all policies.
+    <ng-container *ngIf="supportsConfigurableAuthorizer">
+        <a (click)="createNewPolicy()">Override</a> this policy.
+    </ng-container>
+</ng-template>
+<ng-template #inheritedFromController let-policy 
let-supportsConfigurableAuthorizer>
+    Showing effective policy inherited from the controller.
+    <ng-container *ngIf="supportsConfigurableAuthorizer">
+        <a (click)="createNewPolicy()">Override</a> this policy.
+    </ng-container>
+</ng-template>
+<ng-template #inheritedFromNoRestrictions let-policy 
let-supportsConfigurableAuthorizer>

Review Comment:
   Only a single implicit parameter is allowed. Currently, this results in 
`supportsConfigurableAuthorizer` evaluating to true. Thus, allowing creation of 
a policy even it `supportsConfigurableAuthorizer` is actually false.
   ```suggestion
   <ng-template #inheritedFromNoRestrictions let-policy 
let-supportsConfigurableAuthorizer=supportsConfigurableAuthorizer>
   ```



##########
nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-frontend/src/main/nifi/src/app/pages/flow-designer/ui/canvas/header/header.component.html:
##########
@@ -131,7 +131,15 @@
                         <i class="fa fa-fw fa-users mr-2"></i>
                         Users
                     </button>
-                    <button mat-menu-item class="global-menu-item">
+                    <button
+                        mat-menu-item
+                        class="global-menu-item"
+                        [routerLink]="['/access-policies', 'global']"
+                        [disabled]="
+                            !user.tenantsPermissions.canRead ||
+                            !user.policiesPermissions.canRead ||
+                            !user.policiesPermissions.canWrite
+                        ">

Review Comment:
   Question because I'm unclear TBH... If the FlowConfiguration indicates that 
`supportsManagedAuthorizer` is false, should we allow the user to navigate to 
the Policies screen? Seems like there will be nothing to show or configure in 
this case. We don't evaluate that condition here.
   Or is it not an issue as the permissions in question in such a scenario 
would evaluate to false?
   
   Same question regarding Users above.



##########
nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-frontend/src/main/nifi/src/app/pages/access-policies/ui/common/policy-table/policy-table.component.ts:
##########
@@ -0,0 +1,145 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one or more
+ *  contributor license agreements.  See the NOTICE file distributed with
+ *  this work for additional information regarding copyright ownership.
+ *  The ASF licenses this file to You under the Apache License, Version 2.0
+ *  (the "License"); you may not use this file except in compliance with
+ *  the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing, software
+ *  distributed under the License is distributed on an "AS IS" BASIS,
+ *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ *  See the License for the specific language governing permissions and
+ *  limitations under the License.
+ */
+
+import { AfterViewInit, Component, EventEmitter, Input, Output } from 
'@angular/core';
+import { MatTableDataSource, MatTableModule } from '@angular/material/table';
+import { MatSortModule, Sort } from '@angular/material/sort';
+import { NiFiCommon } from '../../../../../service/nifi-common.service';
+import { CurrentUser } from '../../../../../state/current-user';
+import { TenantEntity, UserEntity } from '../../../../../state/shared';
+import { NgIf } from '@angular/common';
+import { AccessPolicyEntity } from '../../../state/shared';
+import { RemoveTenantFromPolicyRequest } from '../../../state/access-policy';
+
+export interface TenantItem {
+    id: string;
+    user: string;
+    tenantType: 'user' | 'userGroup';
+    configurable: boolean;
+}
+
+@Component({
+    selector: 'policy-table',
+    standalone: true,
+    templateUrl: './policy-table.component.html',
+    imports: [MatTableModule, MatSortModule, NgIf],
+    styleUrls: ['./policy-table.component.scss', 
'../../../../../../assets/styles/listing-table.scss']
+})
+export class PolicyTable implements AfterViewInit {
+    displayedColumns: string[] = ['user', 'actions'];
+    dataSource: MatTableDataSource<TenantItem> = new 
MatTableDataSource<TenantItem>();
+
+    tenantLookup: Map<string, TenantEntity> = new Map<string, TenantEntity>();
+
+    @Input() set policy(policy: AccessPolicyEntity | undefined) {
+        const tenantItems: TenantItem[] = [];
+
+        if (policy) {
+            policy.component.users.forEach((user) => {
+                this.tenantLookup.set(user.id, user);
+                tenantItems.push({
+                    id: user.id,
+                    tenantType: 'user',
+                    user: user.component.identity,
+                    configurable: user.component.configurable
+                });
+            });
+            policy.component.userGroups.forEach((userGroup) => {
+                this.tenantLookup.set(userGroup.id, userGroup);
+                tenantItems.push({
+                    id: userGroup.id,
+                    tenantType: 'userGroup',
+                    user: userGroup.component.identity,
+                    configurable: userGroup.component.configurable
+                });
+            });
+        }
+
+        this.dataSource.data = this.sortUsers(tenantItems, this.sort);
+        this._policy = policy;
+    }
+
+    @Input() supportsPolicyModification!: boolean;
+
+    @Output() removeTenantFromPolicy: 
EventEmitter<RemoveTenantFromPolicyRequest> =
+        new EventEmitter<RemoveTenantFromPolicyRequest>();
+
+    _policy: AccessPolicyEntity | undefined;
+    selectedTenantId: string | null = null;
+
+    sort: Sort = {
+        active: 'user',
+        direction: 'asc'
+    };
+
+    constructor(private nifiCommon: NiFiCommon) {}
+
+    ngAfterViewInit(): void {}

Review Comment:
   can we just remove this?



##########
nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-frontend/src/main/nifi/src/app/pages/access-policies/state/access-policy/access-policy.selectors.ts:
##########
@@ -0,0 +1,66 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one or more
+ *  contributor license agreements.  See the NOTICE file distributed with
+ *  this work for additional information regarding copyright ownership.
+ *  The ASF licenses this file to You under the Apache License, Version 2.0
+ *  (the "License"); you may not use this file except in compliance with
+ *  the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing, software
+ *  distributed under the License is distributed on an "AS IS" BASIS,
+ *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ *  See the License for the specific language governing permissions and
+ *  limitations under the License.
+ */
+
+import { createSelector } from '@ngrx/store';
+import { AccessPoliciesState, selectAccessPoliciesState } from '../index';
+import { accessPolicyFeatureKey, AccessPolicyState } from './index';
+import { selectCurrentRoute } from '../../../../state/router/router.selectors';
+import { ComponentResourceAction, ResourceAction } from '../shared';
+
+export const selectAccessPolicyState = createSelector(
+    selectAccessPoliciesState,
+    (state: AccessPoliciesState) => state[accessPolicyFeatureKey]
+);
+
+export const selectResourceAction = createSelector(
+    selectAccessPolicyState,
+    (state: AccessPolicyState) => state.resourceAction
+);
+
+export const selectAccessPolicy = createSelector(
+    selectAccessPolicyState,
+    (state: AccessPolicyState) => state.accessPolicy
+);
+
+export const selectSaving = createSelector(selectAccessPolicyState, (state: 
AccessPolicyState) => state.saving);
+
+export const selectGlobalResourceActionFromRoute = 
createSelector(selectCurrentRoute, (route) => {
+    let selectedResourceAction: ResourceAction | null = null;
+    if (route?.params.action && route?.params.resource) {
+        // always select the action and resource from the route
+        selectedResourceAction = {
+            action: route.params.action,
+            resource: route.params.resource,
+            resourceIdentifier: route.params.resourceIdentifier
+        };

Review Comment:
   This is getting triggered after (or during) the global policy screen was 
requested to be closed. and after the state was reset. this results in the 
`accessPolicies/accessPolicy` section of the store to get re-populated. Doesn't 
seem that should be happening.



##########
nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-frontend/src/main/nifi/src/app/pages/access-policies/ui/component-access-policies/component-access-policies.component.html:
##########
@@ -0,0 +1,147 @@
+<!--
+  ~  Licensed to the Apache Software Foundation (ASF) under one or more
+  ~  contributor license agreements.  See the NOTICE file distributed with
+  ~  this work for additional information regarding copyright ownership.
+  ~  The ASF licenses this file to You under the Apache License, Version 2.0
+  ~  (the "License"); you may not use this file except in compliance with
+  ~  the License.  You may obtain a copy of the License at
+  ~
+  ~     http://www.apache.org/licenses/LICENSE-2.0
+  ~
+  ~  Unless required by applicable law or agreed to in writing, software
+  ~  distributed under the License is distributed on an "AS IS" BASIS,
+  ~  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+  ~  See the License for the specific language governing permissions and
+  ~  limitations under the License.
+  -->
+
+<ng-container *ngIf="accessPolicyState$ | async; let accessPolicyState">
+    <div *ngIf="isInitialLoading(accessPolicyState); else loaded">
+        <ngx-skeleton-loader count="3"></ngx-skeleton-loader>
+    </div>
+
+    <ng-template #loaded>
+        <ng-container *ngIf="policyComponentState$ | async; let 
policyComponentState">
+            <div
+                class="component-access-policies flex flex-col h-full gap-y-2"
+                *ngIf="flowConfiguration$ | async; let flowConfiguration">
+                <div class="value">
+                    <div class="mb-2" 
[ngSwitch]="accessPolicyState.policyStatus">
+                        <ng-container *ngSwitchCase="PolicyStatus.NotFound">
+                            No policy for the specified resource.
+                            <ng-container 
*ngIf="flowConfiguration.supportsConfigurableAuthorizer">
+                                <a (click)="createNewPolicy()">Create</a> a 
new policy.
+                            </ng-container>
+                        </ng-container>
+                        <ng-container *ngSwitchCase="PolicyStatus.Inherited">
+                            <ng-container 
*ngIf="accessPolicyState.accessPolicy">
+                                <ng-container
+                                    *ngTemplateOutlet="
+                                        
getTemplateForInheritedPolicy(accessPolicyState.accessPolicy);
+                                        context: {
+                                            $implicit: 
accessPolicyState.accessPolicy,
+                                            supportsConfigurableAuthorizer:
+                                                
flowConfiguration.supportsConfigurableAuthorizer
+                                        }
+                                    "></ng-container>
+                            </ng-container>
+                        </ng-container>
+                        <ng-container *ngSwitchCase="PolicyStatus.Forbidden">
+                            Not authorized to access the policy for the 
specified resource.
+                        </ng-container>
+                    </div>
+                </div>
+                <div class="flex justify-between items-center">
+                    <form [formGroup]="policyForm">
+                        <div class="flex gap-x-2">
+                            <div class="flex gap-x-1 -mt-2">
+                                <div class="operation-context-logo flex 
flex-col">
+                                    <i class="icon" 
[class]="getContextIcon()"></i>
+                                </div>
+                                <div class="flex flex-col">
+                                    <div class="operation-context-name">{{ 
policyComponentState.label }}</div>
+                                    <div class="operation-context-type">{{ 
getContextType() }}</div>
+                                </div>
+                            </div>
+                            <div class="policy-select">
+                                <mat-form-field>
+                                    <mat-label>Policy</mat-label>
+                                    <mat-select
+                                        formControlName="policyAction"
+                                        
(selectionChange)="policyActionChanged($event.value)">
+                                        <mat-option
+                                            *ngFor="let option of 
policyActionOptions"
+                                            
[disabled]="!isComponentPolicy(option, policyComponentState)"
+                                            [value]="option.value"
+                                            nifiTooltip
+                                            [tooltipComponentType]="TextTip"
+                                            
[tooltipInputData]="getSelectOptionTipData(option)"
+                                            [delayClose]="false"
+                                            >{{ option.text }}
+                                        </mat-option>
+                                    </mat-select>
+                                </mat-form-field>
+                            </div>
+                        </div>
+                    </form>
+                    <div 
*ngIf="flowConfiguration.supportsConfigurableAuthorizer" class="flex gap-x-2">
+                        <button
+                            class="nifi-button"
+                            [disabled]="accessPolicyState.policyStatus !== 
PolicyStatus.Found"
+                            (click)="addTenantToPolicy()">
+                            <i class="fa fa-user-plus"></i>
+                        </button>
+                        <button
+                            class="nifi-button"
+                            [disabled]="accessPolicyState.policyStatus !== 
PolicyStatus.Found"
+                            (click)="deletePolicy()">
+                            <i class="fa fa-trash"></i>
+                        </button>
+                    </div>
+                </div>
+                <div class="flex-1 -mt-2" *ngIf="currentUser$ | async as user">
+                    <policy-table
+                        [policy]="accessPolicyState.accessPolicy"
+                        [supportsPolicyModification]="
+                            flowConfiguration.supportsConfigurableAuthorizer &&
+                            accessPolicyState.policyStatus === 
PolicyStatus.Found
+                        "
+                        
(removeTenantFromPolicy)="removeTenantFromPolicy($event)"></policy-table>
+                </div>
+                <div class="flex justify-between">
+                    <div class="refresh-container flex items-center gap-x-2">
+                        <button class="nifi-button" 
(click)="refreshGlobalAccessPolicy()">
+                            <i class="fa fa-refresh" 
[class.fa-spin]="accessPolicyState.status === 'loading'"></i>
+                        </button>
+                        <div>Last updated:</div>
+                        <div class="refresh-timestamp">{{ 
accessPolicyState.loadedTimestamp }}</div>
+                    </div>
+                </div>
+            </div>
+        </ng-container>
+    </ng-template>
+</ng-container>
+<ng-template #inheritedFromPolicies let-policy 
let-supportsConfigurableAuthorizer>
+    No component specific administrators.
+    <ng-container *ngIf="supportsConfigurableAuthorizer">
+        <a (click)="createNewPolicy()">Add</a> policy for additional 
administrators.
+    </ng-container>
+</ng-template>
+<ng-template #inheritedFromController let-policy 
let-supportsConfigurableAuthorizer>

Review Comment:
   only 1 implicit parameter supported



##########
nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-frontend/src/main/nifi/src/app/pages/access-policies/ui/component-access-policies/component-access-policies.component.html:
##########
@@ -0,0 +1,147 @@
+<!--
+  ~  Licensed to the Apache Software Foundation (ASF) under one or more
+  ~  contributor license agreements.  See the NOTICE file distributed with
+  ~  this work for additional information regarding copyright ownership.
+  ~  The ASF licenses this file to You under the Apache License, Version 2.0
+  ~  (the "License"); you may not use this file except in compliance with
+  ~  the License.  You may obtain a copy of the License at
+  ~
+  ~     http://www.apache.org/licenses/LICENSE-2.0
+  ~
+  ~  Unless required by applicable law or agreed to in writing, software
+  ~  distributed under the License is distributed on an "AS IS" BASIS,
+  ~  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+  ~  See the License for the specific language governing permissions and
+  ~  limitations under the License.
+  -->
+
+<ng-container *ngIf="accessPolicyState$ | async; let accessPolicyState">
+    <div *ngIf="isInitialLoading(accessPolicyState); else loaded">
+        <ngx-skeleton-loader count="3"></ngx-skeleton-loader>
+    </div>
+
+    <ng-template #loaded>
+        <ng-container *ngIf="policyComponentState$ | async; let 
policyComponentState">
+            <div
+                class="component-access-policies flex flex-col h-full gap-y-2"
+                *ngIf="flowConfiguration$ | async; let flowConfiguration">
+                <div class="value">
+                    <div class="mb-2" 
[ngSwitch]="accessPolicyState.policyStatus">
+                        <ng-container *ngSwitchCase="PolicyStatus.NotFound">
+                            No policy for the specified resource.
+                            <ng-container 
*ngIf="flowConfiguration.supportsConfigurableAuthorizer">
+                                <a (click)="createNewPolicy()">Create</a> a 
new policy.
+                            </ng-container>
+                        </ng-container>
+                        <ng-container *ngSwitchCase="PolicyStatus.Inherited">
+                            <ng-container 
*ngIf="accessPolicyState.accessPolicy">
+                                <ng-container
+                                    *ngTemplateOutlet="
+                                        
getTemplateForInheritedPolicy(accessPolicyState.accessPolicy);
+                                        context: {
+                                            $implicit: 
accessPolicyState.accessPolicy,
+                                            supportsConfigurableAuthorizer:
+                                                
flowConfiguration.supportsConfigurableAuthorizer
+                                        }
+                                    "></ng-container>
+                            </ng-container>
+                        </ng-container>
+                        <ng-container *ngSwitchCase="PolicyStatus.Forbidden">
+                            Not authorized to access the policy for the 
specified resource.
+                        </ng-container>
+                    </div>
+                </div>
+                <div class="flex justify-between items-center">
+                    <form [formGroup]="policyForm">
+                        <div class="flex gap-x-2">
+                            <div class="flex gap-x-1 -mt-2">
+                                <div class="operation-context-logo flex 
flex-col">
+                                    <i class="icon" 
[class]="getContextIcon()"></i>
+                                </div>
+                                <div class="flex flex-col">
+                                    <div class="operation-context-name">{{ 
policyComponentState.label }}</div>
+                                    <div class="operation-context-type">{{ 
getContextType() }}</div>
+                                </div>
+                            </div>
+                            <div class="policy-select">
+                                <mat-form-field>
+                                    <mat-label>Policy</mat-label>
+                                    <mat-select
+                                        formControlName="policyAction"
+                                        
(selectionChange)="policyActionChanged($event.value)">
+                                        <mat-option
+                                            *ngFor="let option of 
policyActionOptions"
+                                            
[disabled]="!isComponentPolicy(option, policyComponentState)"
+                                            [value]="option.value"
+                                            nifiTooltip
+                                            [tooltipComponentType]="TextTip"
+                                            
[tooltipInputData]="getSelectOptionTipData(option)"
+                                            [delayClose]="false"
+                                            >{{ option.text }}
+                                        </mat-option>
+                                    </mat-select>
+                                </mat-form-field>
+                            </div>
+                        </div>
+                    </form>
+                    <div 
*ngIf="flowConfiguration.supportsConfigurableAuthorizer" class="flex gap-x-2">
+                        <button
+                            class="nifi-button"
+                            [disabled]="accessPolicyState.policyStatus !== 
PolicyStatus.Found"
+                            (click)="addTenantToPolicy()">
+                            <i class="fa fa-user-plus"></i>
+                        </button>
+                        <button
+                            class="nifi-button"
+                            [disabled]="accessPolicyState.policyStatus !== 
PolicyStatus.Found"
+                            (click)="deletePolicy()">
+                            <i class="fa fa-trash"></i>
+                        </button>
+                    </div>
+                </div>
+                <div class="flex-1 -mt-2" *ngIf="currentUser$ | async as user">
+                    <policy-table
+                        [policy]="accessPolicyState.accessPolicy"
+                        [supportsPolicyModification]="
+                            flowConfiguration.supportsConfigurableAuthorizer &&
+                            accessPolicyState.policyStatus === 
PolicyStatus.Found
+                        "
+                        
(removeTenantFromPolicy)="removeTenantFromPolicy($event)"></policy-table>
+                </div>
+                <div class="flex justify-between">
+                    <div class="refresh-container flex items-center gap-x-2">
+                        <button class="nifi-button" 
(click)="refreshGlobalAccessPolicy()">
+                            <i class="fa fa-refresh" 
[class.fa-spin]="accessPolicyState.status === 'loading'"></i>
+                        </button>
+                        <div>Last updated:</div>
+                        <div class="refresh-timestamp">{{ 
accessPolicyState.loadedTimestamp }}</div>
+                    </div>
+                </div>
+            </div>
+        </ng-container>
+    </ng-template>
+</ng-container>
+<ng-template #inheritedFromPolicies let-policy 
let-supportsConfigurableAuthorizer>
+    No component specific administrators.
+    <ng-container *ngIf="supportsConfigurableAuthorizer">
+        <a (click)="createNewPolicy()">Add</a> policy for additional 
administrators.
+    </ng-container>
+</ng-template>
+<ng-template #inheritedFromController let-policy 
let-supportsConfigurableAuthorizer>
+    Showing effective policy inherited from the controller.
+    <ng-container *ngIf="supportsConfigurableAuthorizer">
+        <a (click)="createNewPolicy()">Override</a> this policy.
+    </ng-container>
+</ng-template>
+<ng-template #inheritedFromGlobalParameterContexts let-policy 
let-supportsConfigurableAuthorizer>
+    Showing effective policy inherited from global parameter context policy.
+    <ng-container *ngIf="supportsConfigurableAuthorizer">
+        <a (click)="createNewPolicy()">Override</a> this policy.
+    </ng-container>
+</ng-template>
+<ng-template #inheritedFromProcessGroup let-policy 
let-supportsConfigurableAuthorizer>

Review Comment:
   only 1 implicit parameter supported



##########
nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-frontend/src/main/nifi/src/app/pages/access-policies/ui/common/policy-table/policy-table.component.ts:
##########
@@ -0,0 +1,145 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one or more
+ *  contributor license agreements.  See the NOTICE file distributed with
+ *  this work for additional information regarding copyright ownership.
+ *  The ASF licenses this file to You under the Apache License, Version 2.0
+ *  (the "License"); you may not use this file except in compliance with
+ *  the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing, software
+ *  distributed under the License is distributed on an "AS IS" BASIS,
+ *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ *  See the License for the specific language governing permissions and
+ *  limitations under the License.
+ */
+
+import { AfterViewInit, Component, EventEmitter, Input, Output } from 
'@angular/core';
+import { MatTableDataSource, MatTableModule } from '@angular/material/table';
+import { MatSortModule, Sort } from '@angular/material/sort';
+import { NiFiCommon } from '../../../../../service/nifi-common.service';
+import { CurrentUser } from '../../../../../state/current-user';
+import { TenantEntity, UserEntity } from '../../../../../state/shared';
+import { NgIf } from '@angular/common';
+import { AccessPolicyEntity } from '../../../state/shared';
+import { RemoveTenantFromPolicyRequest } from '../../../state/access-policy';
+
+export interface TenantItem {
+    id: string;
+    user: string;
+    tenantType: 'user' | 'userGroup';
+    configurable: boolean;
+}
+
+@Component({
+    selector: 'policy-table',
+    standalone: true,
+    templateUrl: './policy-table.component.html',
+    imports: [MatTableModule, MatSortModule, NgIf],
+    styleUrls: ['./policy-table.component.scss', 
'../../../../../../assets/styles/listing-table.scss']
+})
+export class PolicyTable implements AfterViewInit {
+    displayedColumns: string[] = ['user', 'actions'];
+    dataSource: MatTableDataSource<TenantItem> = new 
MatTableDataSource<TenantItem>();
+
+    tenantLookup: Map<string, TenantEntity> = new Map<string, TenantEntity>();
+
+    @Input() set policy(policy: AccessPolicyEntity | undefined) {
+        const tenantItems: TenantItem[] = [];
+
+        if (policy) {
+            policy.component.users.forEach((user) => {
+                this.tenantLookup.set(user.id, user);
+                tenantItems.push({
+                    id: user.id,
+                    tenantType: 'user',
+                    user: user.component.identity,
+                    configurable: user.component.configurable
+                });
+            });
+            policy.component.userGroups.forEach((userGroup) => {
+                this.tenantLookup.set(userGroup.id, userGroup);
+                tenantItems.push({
+                    id: userGroup.id,
+                    tenantType: 'userGroup',
+                    user: userGroup.component.identity,
+                    configurable: userGroup.component.configurable
+                });
+            });
+        }
+
+        this.dataSource.data = this.sortUsers(tenantItems, this.sort);
+        this._policy = policy;
+    }
+
+    @Input() supportsPolicyModification!: boolean;
+
+    @Output() removeTenantFromPolicy: 
EventEmitter<RemoveTenantFromPolicyRequest> =
+        new EventEmitter<RemoveTenantFromPolicyRequest>();
+
+    _policy: AccessPolicyEntity | undefined;

Review Comment:
   The appears to be intended to be private based on naming convention. I don't 
see any usage in the html referencing it, can it be private?



##########
nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-frontend/src/main/nifi/src/app/pages/access-policies/ui/component-access-policies/component-access-policies.component.html:
##########
@@ -0,0 +1,147 @@
+<!--
+  ~  Licensed to the Apache Software Foundation (ASF) under one or more
+  ~  contributor license agreements.  See the NOTICE file distributed with
+  ~  this work for additional information regarding copyright ownership.
+  ~  The ASF licenses this file to You under the Apache License, Version 2.0
+  ~  (the "License"); you may not use this file except in compliance with
+  ~  the License.  You may obtain a copy of the License at
+  ~
+  ~     http://www.apache.org/licenses/LICENSE-2.0
+  ~
+  ~  Unless required by applicable law or agreed to in writing, software
+  ~  distributed under the License is distributed on an "AS IS" BASIS,
+  ~  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+  ~  See the License for the specific language governing permissions and
+  ~  limitations under the License.
+  -->
+
+<ng-container *ngIf="accessPolicyState$ | async; let accessPolicyState">
+    <div *ngIf="isInitialLoading(accessPolicyState); else loaded">
+        <ngx-skeleton-loader count="3"></ngx-skeleton-loader>
+    </div>
+
+    <ng-template #loaded>
+        <ng-container *ngIf="policyComponentState$ | async; let 
policyComponentState">
+            <div
+                class="component-access-policies flex flex-col h-full gap-y-2"
+                *ngIf="flowConfiguration$ | async; let flowConfiguration">
+                <div class="value">
+                    <div class="mb-2" 
[ngSwitch]="accessPolicyState.policyStatus">
+                        <ng-container *ngSwitchCase="PolicyStatus.NotFound">
+                            No policy for the specified resource.
+                            <ng-container 
*ngIf="flowConfiguration.supportsConfigurableAuthorizer">
+                                <a (click)="createNewPolicy()">Create</a> a 
new policy.
+                            </ng-container>
+                        </ng-container>
+                        <ng-container *ngSwitchCase="PolicyStatus.Inherited">
+                            <ng-container 
*ngIf="accessPolicyState.accessPolicy">
+                                <ng-container
+                                    *ngTemplateOutlet="
+                                        
getTemplateForInheritedPolicy(accessPolicyState.accessPolicy);
+                                        context: {
+                                            $implicit: 
accessPolicyState.accessPolicy,
+                                            supportsConfigurableAuthorizer:
+                                                
flowConfiguration.supportsConfigurableAuthorizer
+                                        }
+                                    "></ng-container>
+                            </ng-container>
+                        </ng-container>
+                        <ng-container *ngSwitchCase="PolicyStatus.Forbidden">
+                            Not authorized to access the policy for the 
specified resource.
+                        </ng-container>
+                    </div>
+                </div>
+                <div class="flex justify-between items-center">
+                    <form [formGroup]="policyForm">
+                        <div class="flex gap-x-2">
+                            <div class="flex gap-x-1 -mt-2">
+                                <div class="operation-context-logo flex 
flex-col">
+                                    <i class="icon" 
[class]="getContextIcon()"></i>
+                                </div>
+                                <div class="flex flex-col">
+                                    <div class="operation-context-name">{{ 
policyComponentState.label }}</div>
+                                    <div class="operation-context-type">{{ 
getContextType() }}</div>
+                                </div>
+                            </div>
+                            <div class="policy-select">
+                                <mat-form-field>
+                                    <mat-label>Policy</mat-label>
+                                    <mat-select
+                                        formControlName="policyAction"
+                                        
(selectionChange)="policyActionChanged($event.value)">
+                                        <mat-option
+                                            *ngFor="let option of 
policyActionOptions"
+                                            
[disabled]="!isComponentPolicy(option, policyComponentState)"
+                                            [value]="option.value"
+                                            nifiTooltip
+                                            [tooltipComponentType]="TextTip"
+                                            
[tooltipInputData]="getSelectOptionTipData(option)"
+                                            [delayClose]="false"
+                                            >{{ option.text }}
+                                        </mat-option>
+                                    </mat-select>
+                                </mat-form-field>
+                            </div>
+                        </div>
+                    </form>
+                    <div 
*ngIf="flowConfiguration.supportsConfigurableAuthorizer" class="flex gap-x-2">
+                        <button
+                            class="nifi-button"
+                            [disabled]="accessPolicyState.policyStatus !== 
PolicyStatus.Found"

Review Comment:
   add a title



-- 
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.

To unsubscribe, e-mail: [email protected]

For queries about this service, please contact Infrastructure at:
[email protected]

Reply via email to