Browse Source

feat(admin-ui): Add support for permissions on custom fields

Relates to #2671
Michael Bromley 2 years ago
parent
commit
94e0c42d2c

+ 3 - 1
packages/admin-ui/src/lib/catalog/src/components/product-options-editor/product-options-editor.component.ts

@@ -12,6 +12,7 @@ import {
     LanguageCode,
     NotificationService,
     Permission,
+    PermissionsService,
     ProductOptionFragment,
     ProductOptionGroupFragment,
     ServerConfigService,
@@ -48,12 +49,13 @@ export class ProductOptionsEditorComponent extends BaseDetailComponent<ProductWi
         protected router: Router,
         protected serverConfigService: ServerConfigService,
         protected dataService: DataService,
+        protected permissionsService: PermissionsService,
         private productDetailService: ProductDetailService,
         private formBuilder: UntypedFormBuilder,
         private changeDetector: ChangeDetectorRef,
         private notificationService: NotificationService,
     ) {
-        super(route, router, serverConfigService, dataService);
+        super(route, router, serverConfigService, dataService, permissionsService);
         this.optionGroupCustomFields = this.getCustomFieldConfig('ProductOptionGroup');
         this.optionCustomFields = this.getCustomFieldConfig('ProductOption');
     }

+ 15 - 2
packages/admin-ui/src/lib/core/src/common/base-detail.component.ts

@@ -9,6 +9,7 @@ import { distinctUntilChanged, filter, map, shareReplay, switchMap, takeUntil, t
 import { DataService } from '../data/providers/data.service';
 import { ServerConfigService } from '../data/server-config';
 import { BreadcrumbValue } from '../providers/breadcrumb/breadcrumb.service';
+import { PermissionsService } from '../providers/permissions/permissions.service';
 
 import { DeactivateAware } from './deactivate-aware';
 import { CustomFieldConfig, CustomFields, LanguageCode } from './generated-types';
@@ -70,6 +71,7 @@ export abstract class BaseDetailComponent<Entity extends { id: string; updatedAt
         protected router: Router,
         protected serverConfigService: ServerConfigService,
         protected dataService: DataService,
+        protected permissionsService: PermissionsService,
     ) {}
 
     init() {
@@ -149,7 +151,12 @@ export abstract class BaseDetailComponent<Entity extends { id: string; updatedAt
     }
 
     protected getCustomFieldConfig(key: Exclude<keyof CustomFields, '__typename'>): CustomFieldConfig[] {
-        return this.serverConfigService.getCustomFieldsFor(key);
+        return this.serverConfigService.getCustomFieldsFor(key).filter(f => {
+            if (f.requiresPermission?.length) {
+                return this.permissionsService.userHasPermissions(f.requiresPermission);
+            }
+            return true;
+        });
     }
 
     protected setQueryParam(key: string, value: any) {
@@ -184,7 +191,13 @@ export abstract class TypedBaseDetailComponent<
     protected entity: ResultOf<T>[Field];
 
     protected constructor() {
-        super(inject(ActivatedRoute), inject(Router), inject(ServerConfigService), inject(DataService));
+        super(
+            inject(ActivatedRoute),
+            inject(Router),
+            inject(ServerConfigService),
+            inject(DataService),
+            inject(PermissionsService),
+        );
     }
 
     override init() {

+ 8 - 2
packages/admin-ui/src/lib/core/src/common/base-list.component.ts

@@ -10,6 +10,7 @@ import { QueryResult } from '../data/query-result';
 import { ServerConfigService } from '../data/server-config';
 import { DataTableFilterCollection } from '../providers/data-table/data-table-filter-collection';
 import { DataTableSortCollection } from '../providers/data-table/data-table-sort-collection';
+import { PermissionsService } from '../providers/permissions/permissions.service';
 import { CustomFieldConfig, CustomFields, LanguageCode } from './generated-types';
 import { SelectionManager } from './utilities/selection-manager';
 
@@ -178,7 +179,6 @@ export class BaseListComponent<ResultType, ItemType, VariableType extends Record
         valueOrOptions?: any,
         maybeOptions?: { replaceUrl?: boolean; queryParamsHandling?: QueryParamsHandling },
     ) {
-        const paramsObject = typeof keyOrHash === 'string' ? { [keyOrHash]: valueOrOptions } : keyOrHash;
         const options = (typeof keyOrHash === 'string' ? maybeOptions : valueOrOptions) ?? {};
         this.router.navigate(['./'], {
             queryParams: typeof keyOrHash === 'string' ? { [keyOrHash]: valueOrOptions } : keyOrHash,
@@ -211,6 +211,7 @@ export class TypedBaseListComponent<
     protected dataService = inject(DataService);
     protected router = inject(Router);
     protected serverConfigService = inject(ServerConfigService);
+    protected permissionsService = inject(PermissionsService);
     private refreshStreams: Array<Observable<any>> = [];
     private collections: Array<DataTableFilterCollection | DataTableSortCollection<any>> = [];
     constructor() {
@@ -263,6 +264,11 @@ export class TypedBaseListComponent<
     }
 
     getCustomFieldConfig(key: Exclude<keyof CustomFields, '__typename'> | string): CustomFieldConfig[] {
-        return this.serverConfigService.getCustomFieldsFor(key);
+        return this.serverConfigService.getCustomFieldsFor(key).filter(f => {
+            if (f.requiresPermission?.length) {
+                return this.permissionsService.userHasPermissions(f.requiresPermission);
+            }
+            return true;
+        });
     }
 }

+ 10 - 0
packages/admin-ui/src/lib/core/src/common/generated-types.ts

@@ -321,6 +321,7 @@ export type BooleanCustomFieldConfig = CustomField & {
   name: Scalars['String']['output'];
   nullable?: Maybe<Scalars['Boolean']['output']>;
   readonly?: Maybe<Scalars['Boolean']['output']>;
+  requiresPermission?: Maybe<Array<Permission>>;
   type: Scalars['String']['output'];
   ui?: Maybe<Scalars['JSON']['output']>;
 };
@@ -1317,6 +1318,7 @@ export type CustomField = {
   name: Scalars['String']['output'];
   nullable?: Maybe<Scalars['Boolean']['output']>;
   readonly?: Maybe<Scalars['Boolean']['output']>;
+  requiresPermission?: Maybe<Array<Permission>>;
   type: Scalars['String']['output'];
   ui?: Maybe<Scalars['JSON']['output']>;
 };
@@ -1515,6 +1517,7 @@ export type DateTimeCustomFieldConfig = CustomField & {
   name: Scalars['String']['output'];
   nullable?: Maybe<Scalars['Boolean']['output']>;
   readonly?: Maybe<Scalars['Boolean']['output']>;
+  requiresPermission?: Maybe<Array<Permission>>;
   step?: Maybe<Scalars['Int']['output']>;
   type: Scalars['String']['output'];
   ui?: Maybe<Scalars['JSON']['output']>;
@@ -1820,6 +1823,7 @@ export type FloatCustomFieldConfig = CustomField & {
   name: Scalars['String']['output'];
   nullable?: Maybe<Scalars['Boolean']['output']>;
   readonly?: Maybe<Scalars['Boolean']['output']>;
+  requiresPermission?: Maybe<Array<Permission>>;
   step?: Maybe<Scalars['Float']['output']>;
   type: Scalars['String']['output'];
   ui?: Maybe<Scalars['JSON']['output']>;
@@ -2025,6 +2029,7 @@ export type IntCustomFieldConfig = CustomField & {
   name: Scalars['String']['output'];
   nullable?: Maybe<Scalars['Boolean']['output']>;
   readonly?: Maybe<Scalars['Boolean']['output']>;
+  requiresPermission?: Maybe<Array<Permission>>;
   step?: Maybe<Scalars['Int']['output']>;
   type: Scalars['String']['output'];
   ui?: Maybe<Scalars['JSON']['output']>;
@@ -2489,6 +2494,7 @@ export type LocaleStringCustomFieldConfig = CustomField & {
   nullable?: Maybe<Scalars['Boolean']['output']>;
   pattern?: Maybe<Scalars['String']['output']>;
   readonly?: Maybe<Scalars['Boolean']['output']>;
+  requiresPermission?: Maybe<Array<Permission>>;
   type: Scalars['String']['output'];
   ui?: Maybe<Scalars['JSON']['output']>;
 };
@@ -2502,6 +2508,7 @@ export type LocaleTextCustomFieldConfig = CustomField & {
   name: Scalars['String']['output'];
   nullable?: Maybe<Scalars['Boolean']['output']>;
   readonly?: Maybe<Scalars['Boolean']['output']>;
+  requiresPermission?: Maybe<Array<Permission>>;
   type: Scalars['String']['output'];
   ui?: Maybe<Scalars['JSON']['output']>;
 };
@@ -5489,6 +5496,7 @@ export type RelationCustomFieldConfig = CustomField & {
   name: Scalars['String']['output'];
   nullable?: Maybe<Scalars['Boolean']['output']>;
   readonly?: Maybe<Scalars['Boolean']['output']>;
+  requiresPermission?: Maybe<Array<Permission>>;
   scalarFields: Array<Scalars['String']['output']>;
   type: Scalars['String']['output'];
   ui?: Maybe<Scalars['JSON']['output']>;
@@ -6000,6 +6008,7 @@ export type StringCustomFieldConfig = CustomField & {
   options?: Maybe<Array<StringFieldOption>>;
   pattern?: Maybe<Scalars['String']['output']>;
   readonly?: Maybe<Scalars['Boolean']['output']>;
+  requiresPermission?: Maybe<Array<Permission>>;
   type: Scalars['String']['output'];
   ui?: Maybe<Scalars['JSON']['output']>;
 };
@@ -6241,6 +6250,7 @@ export type TextCustomFieldConfig = CustomField & {
   name: Scalars['String']['output'];
   nullable?: Maybe<Scalars['Boolean']['output']>;
   readonly?: Maybe<Scalars['Boolean']['output']>;
+  requiresPermission?: Maybe<Array<Permission>>;
   type: Scalars['String']['output'];
   ui?: Maybe<Scalars['JSON']['output']>;
 };

+ 1 - 0
packages/admin-ui/src/lib/core/src/data/definitions/settings-definitions.ts

@@ -560,6 +560,7 @@ export const CUSTOM_FIELD_CONFIG_FRAGMENT = gql`
         }
         readonly
         nullable
+        requiresPermission
         ui
     }
 `;

+ 0 - 1
packages/admin-ui/src/lib/core/src/data/server-config.ts

@@ -8,7 +8,6 @@ import {
     GetServerConfigQuery,
     OrderProcessState,
     PermissionDefinition,
-    ServerConfig,
 } from '../common/generated-types';
 
 import { GET_GLOBAL_SETTINGS, GET_SERVER_CONFIG } from './definitions/settings-definitions';

+ 11 - 29
packages/admin-ui/src/lib/core/src/providers/alerts/alerts.service.ts

@@ -1,18 +1,9 @@
 import { Injectable } from '@angular/core';
 import { notNullOrUndefined } from '@vendure/common/lib/shared-utils';
-import {
-    BehaviorSubject,
-    combineLatest,
-    interval,
-    isObservable,
-    Observable,
-    of,
-    Subject,
-    switchMap,
-} from 'rxjs';
-import { filter, first, map, mapTo, startWith, take } from 'rxjs/operators';
+import { BehaviorSubject, combineLatest, interval, isObservable, Observable, Subject, switchMap } from 'rxjs';
+import { map, startWith, take } from 'rxjs/operators';
 import { Permission } from '../../common/generated-types';
-import { DataService } from '../../data/providers/data.service';
+import { PermissionsService } from '../permissions/permissions.service';
 
 export interface AlertConfig<T = any> {
     id: string;
@@ -86,9 +77,9 @@ export class AlertsService {
     private alertsMap = new Map<string, Alert<any>>();
     private configUpdated = new Subject<void>();
 
-    constructor(private dataService: DataService) {
+    constructor(private permissionsService: PermissionsService) {
         const alerts$ = this.configUpdated.pipe(
-            mapTo([...this.alertsMap.values()]),
+            map(() => [...this.alertsMap.values()]),
             startWith([...this.alertsMap.values()]),
         );
 
@@ -103,26 +94,17 @@ export class AlertsService {
     }
 
     configureAlert<T>(config: AlertConfig<T>) {
-        this.hasSufficientPermissions(config.requiredPermissions)
-            .pipe(first())
-            .subscribe(hasSufficientPermissions => {
-                if (hasSufficientPermissions) {
-                    this.alertsMap.set(config.id, new Alert(config));
-                    this.configUpdated.next();
-                }
-            });
+        if (this.hasSufficientPermissions(config.requiredPermissions)) {
+            this.alertsMap.set(config.id, new Alert(config));
+            this.configUpdated.next();
+        }
     }
 
     hasSufficientPermissions(permissions?: Permission[]) {
         if (!permissions || permissions.length === 0) {
-            return of(true);
+            return true;
         }
-        return this.dataService.client.userStatus().stream$.pipe(
-            filter(({ userStatus }) => userStatus.isLoggedIn),
-            map(({ userStatus }) =>
-                permissions.some(permission => userStatus.permissions.includes(permission)),
-            ),
-        );
+        return this.permissionsService.userHasPermissions(permissions);
     }
 
     refresh(id?: string) {

+ 8 - 5
packages/admin-ui/src/lib/core/src/providers/auth/auth.service.ts

@@ -7,6 +7,7 @@ import { AttemptLoginMutation, CurrentUserFragment } from '../../common/generate
 import { DataService } from '../../data/providers/data.service';
 import { ServerConfigService } from '../../data/server-config';
 import { LocalStorageService } from '../local-storage/local-storage.service';
+import { PermissionsService } from '../permissions/permissions.service';
 
 /**
  * This service handles logic relating to authentication of the current user.
@@ -19,6 +20,7 @@ export class AuthService {
         private localStorageService: LocalStorageService,
         private dataService: DataService,
         private serverConfigService: ServerConfigService,
+        private permissionsService: PermissionsService,
     ) {}
 
     /**
@@ -39,7 +41,8 @@ export class AuthService {
             }),
             switchMap(login => {
                 if (login.__typename === 'CurrentUser') {
-                    const { id } = this.getActiveChannel(login.channels);
+                    const activeChannel = this.getActiveChannel(login.channels);
+                    this.permissionsService.setCurrentUserPermissions(activeChannel.permissions);
                     return this.dataService.administrator.getActiveAdministrator().single$.pipe(
                         switchMap(({ activeAdministrator }) => {
                             if (activeAdministrator) {
@@ -47,7 +50,7 @@ export class AuthService {
                                     .loginSuccess(
                                         activeAdministrator.id,
                                         `${activeAdministrator.firstName} ${activeAdministrator.lastName}`,
-                                        id,
+                                        activeChannel.id,
                                         login.channels,
                                     )
                                     .pipe(map(() => login));
@@ -107,8 +110,8 @@ export class AuthService {
                     return of(false) as any;
                 }
                 this.setChannelToken(me.channels);
-
-                const { id } = this.getActiveChannel(me.channels);
+                const activeChannel = this.getActiveChannel(me.channels);
+                this.permissionsService.setCurrentUserPermissions(activeChannel.permissions);
                 return this.dataService.administrator.getActiveAdministrator().single$.pipe(
                     switchMap(({ activeAdministrator }) => {
                         if (activeAdministrator) {
@@ -116,7 +119,7 @@ export class AuthService {
                                 .loginSuccess(
                                     activeAdministrator.id,
                                     `${activeAdministrator.firstName} ${activeAdministrator.lastName}`,
-                                    id,
+                                    activeChannel.id,
                                     me.channels,
                                 )
                                 .pipe(map(() => true));

+ 7 - 1
packages/admin-ui/src/lib/core/src/providers/channel/channel.service.ts

@@ -6,6 +6,7 @@ import { map, shareReplay, tap } from 'rxjs/operators';
 import { UserStatusFragment } from '../../common/generated-types';
 import { DataService } from '../../data/providers/data.service';
 import { LocalStorageService } from '../local-storage/local-storage.service';
+import { PermissionsService } from '../permissions/permissions.service';
 
 @Injectable({
     providedIn: 'root',
@@ -13,7 +14,11 @@ import { LocalStorageService } from '../local-storage/local-storage.service';
 export class ChannelService {
     defaultChannelIsActive$: Observable<boolean>;
 
-    constructor(private dataService: DataService, private localStorageService: LocalStorageService) {
+    constructor(
+        private dataService: DataService,
+        private localStorageService: LocalStorageService,
+        private permissionsService: PermissionsService,
+    ) {
         this.defaultChannelIsActive$ = this.dataService.client
             .userStatus()
             .mapStream(({ userStatus }) => {
@@ -30,6 +35,7 @@ export class ChannelService {
                 const activeChannel = userStatus.channels.find(c => c.id === channelId);
                 if (activeChannel) {
                     this.localStorageService.set('activeChannelToken', activeChannel.token);
+                    this.permissionsService.setCurrentUserPermissions(activeChannel.permissions);
                 }
             }),
         );

+ 41 - 0
packages/admin-ui/src/lib/core/src/providers/permissions/permissions.service.ts

@@ -0,0 +1,41 @@
+import { Injectable } from '@angular/core';
+import { BehaviorSubject } from 'rxjs';
+import { Permission } from '../../common/generated-types';
+
+/**
+ * @description
+ * This service is used internally to power components & logic that are dependent on knowing the
+ * current user's permissions in the currently-active channel.
+ */
+@Injectable({
+    providedIn: 'root',
+})
+export class PermissionsService {
+    private currentUserPermissions: string[] = [];
+    private _currentUserPermissions$ = new BehaviorSubject<string[]>([]);
+    currentUserPermissions$ = this._currentUserPermissions$.asObservable();
+
+    /**
+     * @description
+     * This is called whenever:
+     * - the user logs in
+     * - the active channel changes
+     *
+     * Since active user validation occurs as part of the main auth guard, we can be assured
+     * that if the user is logged in, then this method will be called with the user's permissions
+     * before any other components are rendered lower down in the component tree.
+     */
+    setCurrentUserPermissions(permissions: string[]) {
+        this.currentUserPermissions = permissions;
+        this._currentUserPermissions$.next(permissions);
+    }
+
+    userHasPermissions(requiredPermissions: Array<string | Permission>): boolean {
+        for (const perm of requiredPermissions) {
+            if (this.currentUserPermissions.includes(perm)) {
+                return true;
+            }
+        }
+        return false;
+    }
+}

+ 1 - 0
packages/admin-ui/src/lib/core/src/public_api.ts

@@ -122,6 +122,7 @@ export * from './providers/nav-builder/nav-builder.service';
 export * from './providers/notification/notification.service';
 export * from './providers/overlay-host/overlay-host.service';
 export * from './providers/page/page.service';
+export * from './providers/permissions/permissions.service';
 export * from './shared/components/action-bar/action-bar.component';
 export * from './shared/components/action-bar-items/action-bar-items.component';
 export * from './shared/components/address-form/address-form.component';

+ 1 - 1
packages/admin-ui/src/lib/core/src/shared/components/tabbed-custom-fields/tabbed-custom-fields.component.ts

@@ -1,5 +1,5 @@
 import { ChangeDetectionStrategy, Component, Input, OnInit } from '@angular/core';
-import { AbstractControl, FormGroup } from '@angular/forms';
+import { AbstractControl } from '@angular/forms';
 import { DefaultFormComponentId } from '@vendure/common/lib/shared-types';
 
 import { CustomFieldConfig } from '../../../common/generated-types';

+ 4 - 22
packages/admin-ui/src/lib/core/src/shared/directives/if-permissions.directive.spec.ts

@@ -1,8 +1,6 @@
 import { Component, Input } from '@angular/core';
-import { ComponentFixture, fakeAsync, TestBed, tick } from '@angular/core/testing';
-import { of } from 'rxjs';
-
-import { DataService } from '../../data/providers/data.service';
+import { ComponentFixture, TestBed } from '@angular/core/testing';
+import { PermissionsService } from '../../providers/permissions/permissions.service';
 
 import { IfPermissionsDirective } from './if-permissions.directive';
 
@@ -12,9 +10,10 @@ describe('vdrIfPermissions directive', () => {
     beforeEach(() => {
         fixture = TestBed.configureTestingModule({
             declarations: [TestComponent, IfPermissionsDirective],
-            providers: [{ provide: DataService, useClass: MockDataService }],
         }).createComponent(TestComponent);
         fixture.detectChanges(); // initial binding
+
+        TestBed.inject(PermissionsService).setCurrentUserPermissions(['ValidPermission']);
     });
 
     it('has permission (single)', () => {
@@ -79,20 +78,3 @@ describe('vdrIfPermissions directive', () => {
 export class TestComponent {
     @Input() permissionToTest: string | string[] | null = '';
 }
-
-class MockDataService {
-    client = {
-        userStatus() {
-            return {
-                mapStream: (mapFn: any) =>
-                    of(
-                        mapFn({
-                            userStatus: {
-                                permissions: ['ValidPermission'],
-                            },
-                        }),
-                    ),
-            };
-        },
-    };
-}

+ 8 - 22
packages/admin-ui/src/lib/core/src/shared/directives/if-permissions.directive.ts

@@ -1,16 +1,9 @@
-import {
-    ChangeDetectorRef,
-    Directive,
-    EmbeddedViewRef,
-    Input,
-    TemplateRef,
-    ViewContainerRef,
-} from '@angular/core';
+import { ChangeDetectorRef, Directive, Input, TemplateRef, ViewContainerRef } from '@angular/core';
 import { of } from 'rxjs';
-import { tap } from 'rxjs/operators';
+import { map, tap } from 'rxjs/operators';
 
 import { Permission } from '../../common/generated-types';
-import { DataService } from '../../data/providers/data.service';
+import { PermissionsService } from '../../providers/permissions/permissions.service';
 
 import { IfDirectiveBase } from './if-directive-base';
 
@@ -39,8 +32,8 @@ export class IfPermissionsDirective extends IfDirectiveBase<Array<Permission[] |
     constructor(
         _viewContainer: ViewContainerRef,
         templateRef: TemplateRef<any>,
-        private dataService: DataService,
         private changeDetectorRef: ChangeDetectorRef,
+        private permissionsService: PermissionsService,
     ) {
         super(_viewContainer, templateRef, permissions => {
             if (permissions == null) {
@@ -48,17 +41,10 @@ export class IfPermissionsDirective extends IfDirectiveBase<Array<Permission[] |
             } else if (!permissions) {
                 return of(false);
             }
-            return this.dataService.client
-                .userStatus()
-                .mapStream(({ userStatus }) => {
-                    for (const permission of permissions) {
-                        if (userStatus.permissions.includes(permission)) {
-                            return true;
-                        }
-                    }
-                    return false;
-                })
-                .pipe(tap(() => this.changeDetectorRef.markForCheck()));
+            return this.permissionsService.currentUserPermissions$.pipe(
+                map(() => this.permissionsService.userHasPermissions(permissions)),
+                tap(() => this.changeDetectorRef.markForCheck()),
+            );
         });
     }
 

+ 8 - 20
packages/admin-ui/src/lib/core/src/shared/pipes/has-permission.pipe.ts

@@ -1,7 +1,6 @@
 import { ChangeDetectorRef, OnDestroy, Pipe, PipeTransform } from '@angular/core';
-import { Observable, Subscription } from 'rxjs';
-
-import { DataService } from '../../data/providers/data.service';
+import { Subscription } from 'rxjs';
+import { PermissionsService } from '../../providers/permissions/permissions.service';
 
 /**
  * @description
@@ -20,15 +19,13 @@ import { DataService } from '../../data/providers/data.service';
 })
 export class HasPermissionPipe implements PipeTransform, OnDestroy {
     private hasPermission = false;
-    private currentPermissions$: Observable<string[]>;
     private lastPermissions: string | null = null;
     private subscription: Subscription;
 
-    constructor(private dataService: DataService, private changeDetectorRef: ChangeDetectorRef) {
-        this.currentPermissions$ = this.dataService.client
-            .userStatus()
-            .mapStream(data => data.userStatus.permissions);
-    }
+    constructor(
+        private permissionsService: PermissionsService,
+        private changeDetectorRef: ChangeDetectorRef,
+    ) {}
 
     transform(input: string | string[]): any {
         const requiredPermissions = Array.isArray(input) ? input : [input];
@@ -37,8 +34,8 @@ export class HasPermissionPipe implements PipeTransform, OnDestroy {
             this.lastPermissions = requiredPermissionsString;
             this.hasPermission = false;
             this.dispose();
-            this.subscription = this.currentPermissions$.subscribe(permissions => {
-                this.hasPermission = this.checkPermissions(permissions, requiredPermissions);
+            this.subscription = this.permissionsService.currentUserPermissions$.subscribe(() => {
+                this.hasPermission = this.permissionsService.userHasPermissions(requiredPermissions);
                 this.changeDetectorRef.markForCheck();
             });
         }
@@ -50,15 +47,6 @@ export class HasPermissionPipe implements PipeTransform, OnDestroy {
         this.dispose();
     }
 
-    private checkPermissions(userPermissions: string[], requiredPermissions: string[]): boolean {
-        for (const perm of requiredPermissions) {
-            if (userPermissions.includes(perm)) {
-                return true;
-            }
-        }
-        return false;
-    }
-
     private dispose() {
         if (this.subscription) {
             this.subscription.unsubscribe();