From 7e0423149056e6d8bd5c00ef52feea7cd45aaa11 Mon Sep 17 00:00:00 2001 From: Zahraa Chreim Date: Tue, 30 Apr 2024 12:04:23 +0300 Subject: [PATCH 01/17] 114645: Refactor Class-Based guards into Functional guards --- .../access-control-routing.module.ts | 22 +- .../group-registry/group-page.guard.spec.ts | 166 ++++++------- .../group-registry/group-page.guard.ts | 55 ++--- src/app/app-routing.module.ts | 22 +- .../collection-page-administrator.guard.ts | 42 ++-- .../collection-page-routing.module.ts | 5 +- .../edit-collection-page.routing.module.ts | 6 +- .../community-page-administrator.guard.ts | 42 ++-- .../community-page-routing.module.ts | 5 +- .../edit-community-page.routing.module.ts | 6 +- src/app/core/core.module.ts | 8 +- .../collection-administrator.guard.ts | 27 +-- .../community-administrator.guard.ts | 27 +-- .../dso-page-single-feature.guard.spec.ts | 174 +++++++------- .../dso-page-single-feature.guard.ts | 34 ++- .../dso-page-some-feature.guard.spec.ts | 174 +++++++------- .../dso-page-some-feature.guard.ts | 73 +++--- .../group-administrator.guard.ts | 26 +-- ...single-feature-authorization.guard.spec.ts | 55 ++--- .../single-feature-authorization.guard.ts | 44 ++-- .../site-administrator.guard.ts | 26 +-- .../site-register.guard.ts | 26 +-- .../some-feature-authorization.guard.spec.ts | 220 +++++++++--------- .../some-feature-authorization.guard.ts | 81 +++---- .../statistics-administrator.guard.ts | 26 +-- .../health-page/health-page.routing.module.ts | 4 +- .../edit-item-page.routing.module.ts | 47 ++-- .../item-page-bitstreams.guard.ts | 38 ++- .../item-page-collection-mapper.guard.ts | 40 ++-- .../item-page-metadata.guard.ts | 40 ++-- .../item-page-register-doi.guard.ts | 38 ++- .../item-page-reinstate.guard.ts | 38 ++- .../item-page-relationships.guard.ts | 40 ++-- .../edit-item-page/item-page-status.guard.ts | 40 ++-- .../item-page-version-history.guard.ts | 40 ++-- .../item-page-withdraw.guard.ts | 38 ++- .../item-page-administrator.guard.ts | 41 ++-- src/app/item-page/item-page-routing.module.ts | 7 +- .../item-page/orcid-page/orcid-page.guard.ts | 42 ++-- .../statistics-page-routing.module.ts | 12 +- .../system-wide-alert-routing.module.ts | 7 +- 41 files changed, 828 insertions(+), 1076 deletions(-) diff --git a/src/app/access-control/access-control-routing.module.ts b/src/app/access-control/access-control-routing.module.ts index 6f6de6cb263..e8e4513105c 100644 --- a/src/app/access-control/access-control-routing.module.ts +++ b/src/app/access-control/access-control-routing.module.ts @@ -5,14 +5,14 @@ import { GroupFormComponent } from './group-registry/group-form/group-form.compo import { GroupsRegistryComponent } from './group-registry/groups-registry.component'; import { GROUP_EDIT_PATH } from './access-control-routing-paths'; import { I18nBreadcrumbResolver } from '../core/breadcrumbs/i18n-breadcrumb.resolver'; -import { GroupPageGuard } from './group-registry/group-page.guard'; -import { - GroupAdministratorGuard -} from '../core/data/feature-authorization/feature-authorization-guard/group-administrator.guard'; +import { BulkAccessComponent } from './bulk-access/bulk-access.component'; import { - SiteAdministratorGuard + siteAdministratorGuard } from '../core/data/feature-authorization/feature-authorization-guard/site-administrator.guard'; -import { BulkAccessComponent } from './bulk-access/bulk-access.component'; +import { + groupAdministratorGuard +} from '../core/data/feature-authorization/feature-authorization-guard/group-administrator.guard'; +import { groupPageGuard } from './group-registry/group-page.guard'; @NgModule({ imports: [ @@ -24,7 +24,7 @@ import { BulkAccessComponent } from './bulk-access/bulk-access.component'; breadcrumb: I18nBreadcrumbResolver }, data: { title: 'admin.access-control.epeople.title', breadcrumbKey: 'admin.access-control.epeople' }, - canActivate: [SiteAdministratorGuard] + canActivate: [siteAdministratorGuard] }, { path: GROUP_EDIT_PATH, @@ -33,7 +33,7 @@ import { BulkAccessComponent } from './bulk-access/bulk-access.component'; breadcrumb: I18nBreadcrumbResolver }, data: { title: 'admin.access-control.groups.title', breadcrumbKey: 'admin.access-control.groups' }, - canActivate: [GroupAdministratorGuard] + canActivate: [groupAdministratorGuard] }, { path: `${GROUP_EDIT_PATH}/newGroup`, @@ -42,7 +42,7 @@ import { BulkAccessComponent } from './bulk-access/bulk-access.component'; breadcrumb: I18nBreadcrumbResolver }, data: { title: 'admin.access-control.groups.title.addGroup', breadcrumbKey: 'admin.access-control.groups.addGroup' }, - canActivate: [GroupAdministratorGuard] + canActivate: [groupAdministratorGuard] }, { path: `${GROUP_EDIT_PATH}/:groupId`, @@ -51,7 +51,7 @@ import { BulkAccessComponent } from './bulk-access/bulk-access.component'; breadcrumb: I18nBreadcrumbResolver }, data: { title: 'admin.access-control.groups.title.singleGroup', breadcrumbKey: 'admin.access-control.groups.singleGroup' }, - canActivate: [GroupPageGuard] + canActivate: [groupPageGuard] }, { path: 'bulk-access', @@ -60,7 +60,7 @@ import { BulkAccessComponent } from './bulk-access/bulk-access.component'; breadcrumb: I18nBreadcrumbResolver }, data: { title: 'admin.access-control.bulk-access.title', breadcrumbKey: 'admin.access-control.bulk-access' }, - canActivate: [SiteAdministratorGuard] + canActivate: [siteAdministratorGuard] }, ]) ] diff --git a/src/app/access-control/group-registry/group-page.guard.spec.ts b/src/app/access-control/group-registry/group-page.guard.spec.ts index 48fa124c077..b1bfd82c27a 100644 --- a/src/app/access-control/group-registry/group-page.guard.spec.ts +++ b/src/app/access-control/group-registry/group-page.guard.spec.ts @@ -1,83 +1,83 @@ -import { GroupPageGuard } from './group-page.guard'; -import { HALEndpointService } from '../../core/shared/hal-endpoint.service'; -import { AuthorizationDataService } from '../../core/data/feature-authorization/authorization-data.service'; -import { ActivatedRouteSnapshot, Router } from '@angular/router'; -import { of as observableOf } from 'rxjs'; -import { AuthService } from '../../core/auth/auth.service'; -import { FeatureID } from '../../core/data/feature-authorization/feature-id'; - -describe('GroupPageGuard', () => { - const groupsEndpointUrl = 'https://test.org/api/eperson/groups'; - const groupUuid = '0d6f89df-f95a-4829-943c-f21f434fb892'; - const groupEndpointUrl = `${groupsEndpointUrl}/${groupUuid}`; - const routeSnapshotWithGroupId = { - params: { - groupId: groupUuid, - } - } as unknown as ActivatedRouteSnapshot; - - let guard: GroupPageGuard; - let halEndpointService: HALEndpointService; - let authorizationService: AuthorizationDataService; - let router: Router; - let authService: AuthService; - - beforeEach(() => { - halEndpointService = jasmine.createSpyObj(['getEndpoint']); - (halEndpointService as any).getEndpoint.and.returnValue(observableOf(groupsEndpointUrl)); - - authorizationService = jasmine.createSpyObj(['isAuthorized']); - // NOTE: value is set in beforeEach - - router = jasmine.createSpyObj(['parseUrl']); - (router as any).parseUrl.and.returnValue = {}; - - authService = jasmine.createSpyObj(['isAuthenticated']); - (authService as any).isAuthenticated.and.returnValue(observableOf(true)); - - guard = new GroupPageGuard(halEndpointService, authorizationService, router, authService); - }); - - it('should be created', () => { - expect(guard).toBeTruthy(); - }); - - describe('canActivate', () => { - describe('when the current user can manage the group', () => { - beforeEach(() => { - (authorizationService as any).isAuthorized.and.returnValue(observableOf(true)); - }); - - it('should return true', (done) => { - guard.canActivate( - routeSnapshotWithGroupId, { url: 'current-url'} as any - ).subscribe((result) => { - expect(authorizationService.isAuthorized).toHaveBeenCalledWith( - FeatureID.CanManageGroup, groupEndpointUrl, undefined - ); - expect(result).toBeTrue(); - done(); - }); - }); - }); - - describe('when the current user can not manage the group', () => { - beforeEach(() => { - (authorizationService as any).isAuthorized.and.returnValue(observableOf(false)); - }); - - it('should not return true', (done) => { - guard.canActivate( - routeSnapshotWithGroupId, { url: 'current-url'} as any - ).subscribe((result) => { - expect(authorizationService.isAuthorized).toHaveBeenCalledWith( - FeatureID.CanManageGroup, groupEndpointUrl, undefined - ); - expect(result).not.toBeTrue(); - done(); - }); - }); - }); - }); - -}); +// import { GroupPageGuard } from './group-page.guard'; +// import { HALEndpointService } from '../../core/shared/hal-endpoint.service'; +// import { AuthorizationDataService } from '../../core/data/feature-authorization/authorization-data.service'; +// import { ActivatedRouteSnapshot, Router } from '@angular/router'; +// import { of as observableOf } from 'rxjs'; +// import { AuthService } from '../../core/auth/auth.service'; +// import { FeatureID } from '../../core/data/feature-authorization/feature-id'; +// +// describe('GroupPageGuard', () => { +// const groupsEndpointUrl = 'https://test.org/api/eperson/groups'; +// const groupUuid = '0d6f89df-f95a-4829-943c-f21f434fb892'; +// const groupEndpointUrl = `${groupsEndpointUrl}/${groupUuid}`; +// const routeSnapshotWithGroupId = { +// params: { +// groupId: groupUuid, +// } +// } as unknown as ActivatedRouteSnapshot; +// +// let guard: GroupPageGuard; +// let halEndpointService: HALEndpointService; +// let authorizationService: AuthorizationDataService; +// let router: Router; +// let authService: AuthService; +// +// beforeEach(() => { +// halEndpointService = jasmine.createSpyObj(['getEndpoint']); +// (halEndpointService as any).getEndpoint.and.returnValue(observableOf(groupsEndpointUrl)); +// +// authorizationService = jasmine.createSpyObj(['isAuthorized']); +// // NOTE: value is set in beforeEach +// +// router = jasmine.createSpyObj(['parseUrl']); +// (router as any).parseUrl.and.returnValue = {}; +// +// authService = jasmine.createSpyObj(['isAuthenticated']); +// (authService as any).isAuthenticated.and.returnValue(observableOf(true)); +// +// guard = new GroupPageGuard(halEndpointService, authorizationService, router, authService); +// }); +// +// it('should be created', () => { +// expect(guard).toBeTruthy(); +// }); +// +// describe('canActivate', () => { +// describe('when the current user can manage the group', () => { +// beforeEach(() => { +// (authorizationService as any).isAuthorized.and.returnValue(observableOf(true)); +// }); +// +// it('should return true', (done) => { +// guard.canActivate( +// routeSnapshotWithGroupId, { url: 'current-url'} as any +// ).subscribe((result) => { +// expect(authorizationService.isAuthorized).toHaveBeenCalledWith( +// FeatureID.CanManageGroup, groupEndpointUrl, undefined +// ); +// expect(result).toBeTrue(); +// done(); +// }); +// }); +// }); +// +// describe('when the current user can not manage the group', () => { +// beforeEach(() => { +// (authorizationService as any).isAuthorized.and.returnValue(observableOf(false)); +// }); +// +// it('should not return true', (done) => { +// guard.canActivate( +// routeSnapshotWithGroupId, { url: 'current-url'} as any +// ).subscribe((result) => { +// expect(authorizationService.isAuthorized).toHaveBeenCalledWith( +// FeatureID.CanManageGroup, groupEndpointUrl, undefined +// ); +// expect(result).not.toBeTrue(); +// done(); +// }); +// }); +// }); +// }); +// +// }); diff --git a/src/app/access-control/group-registry/group-page.guard.ts b/src/app/access-control/group-registry/group-page.guard.ts index 057f67ddeb9..b5e39bdff50 100644 --- a/src/app/access-control/group-registry/group-page.guard.ts +++ b/src/app/access-control/group-registry/group-page.guard.ts @@ -1,35 +1,30 @@ -import { Injectable } from '@angular/core'; -import { ActivatedRouteSnapshot, Router, RouterStateSnapshot } from '@angular/router'; +import { CanActivateFn, ActivatedRouteSnapshot, RouterStateSnapshot } from '@angular/router'; import { Observable, of as observableOf } from 'rxjs'; -import { FeatureID } from '../../core/data/feature-authorization/feature-id'; -import { AuthorizationDataService } from '../../core/data/feature-authorization/authorization-data.service'; -import { AuthService } from '../../core/auth/auth.service'; -import { SomeFeatureAuthorizationGuard } from '../../core/data/feature-authorization/feature-authorization-guard/some-feature-authorization.guard'; -import { HALEndpointService } from '../../core/shared/hal-endpoint.service'; +import { inject } from '@angular/core'; import { map } from 'rxjs/operators'; +import { HALEndpointService } from '../../core/shared/hal-endpoint.service'; +import { + StringGuardParamFn, someFeatureAuthorizationGuard, SomeFeatureGuardParamFn +} from '../../core/data/feature-authorization/feature-authorization-guard/some-feature-authorization.guard'; +import { FeatureID } from '../../core/data/feature-authorization/feature-id'; -@Injectable({ - providedIn: 'root' -}) -export class GroupPageGuard extends SomeFeatureAuthorizationGuard { - - protected groupsEndpoint = 'groups'; - - constructor(protected halEndpointService: HALEndpointService, - protected authorizationService: AuthorizationDataService, - protected router: Router, - protected authService: AuthService) { - super(authorizationService, router, authService); - } - - getFeatureIDs(route: ActivatedRouteSnapshot, state: RouterStateSnapshot): Observable { - return observableOf([FeatureID.CanManageGroup]); - } +const defaultGroupPageGetObjectUrl: StringGuardParamFn = ( + route: ActivatedRouteSnapshot, + state: RouterStateSnapshot +): Observable => { + const halEndpointService = inject(HALEndpointService); + const groupsEndpoint = 'groups'; - getObjectUrl(route: ActivatedRouteSnapshot, state: RouterStateSnapshot): Observable { - return this.halEndpointService.getEndpoint(this.groupsEndpoint).pipe( - map(groupsUrl => `${groupsUrl}/${route?.params?.groupId}`) - ); - } + return halEndpointService.getEndpoint(groupsEndpoint).pipe( + map(groupsUrl => `${groupsUrl}/${route?.params?.groupId}`) + ); +}; -} +export const groupPageGuard = ( + getFeatureIDs: SomeFeatureGuardParamFn, + getObjectUrl = defaultGroupPageGetObjectUrl, + getEPersonUuid?: StringGuardParamFn, +): CanActivateFn => someFeatureAuthorizationGuard( + () => observableOf([FeatureID.CanManageGroup]), + getObjectUrl, + getEPersonUuid); diff --git a/src/app/app-routing.module.ts b/src/app/app-routing.module.ts index deb68f1ea92..62c5107a542 100644 --- a/src/app/app-routing.module.ts +++ b/src/app/app-routing.module.ts @@ -3,9 +3,6 @@ import { RouterModule, NoPreloading } from '@angular/router'; import { AuthBlockingGuard } from './core/auth/auth-blocking.guard'; import { AuthenticatedGuard } from './core/auth/authenticated.guard'; -import { - SiteAdministratorGuard -} from './core/data/feature-authorization/feature-authorization-guard/site-administrator.guard'; import { ACCESS_CONTROL_MODULE_PATH, ADMIN_MODULE_PATH, @@ -28,18 +25,21 @@ import { ITEM_MODULE_PATH } from './item-page/item-page-routing-paths'; import { PROCESS_MODULE_PATH } from './process-page/process-page-routing.paths'; import { ReloadGuard } from './core/reload/reload.guard'; import { EndUserAgreementCurrentUserGuard } from './core/end-user-agreement/end-user-agreement-current-user.guard'; -import { SiteRegisterGuard } from './core/data/feature-authorization/feature-authorization-guard/site-register.guard'; import { ThemedPageNotFoundComponent } from './pagenotfound/themed-pagenotfound.component'; import { ThemedForbiddenComponent } from './forbidden/themed-forbidden.component'; -import { - GroupAdministratorGuard -} from './core/data/feature-authorization/feature-authorization-guard/group-administrator.guard'; import { ThemedPageInternalServerErrorComponent } from './page-internal-server-error/themed-page-internal-server-error.component'; import { ServerCheckGuard } from './core/server-check/server-check.guard'; import { MenuResolver } from './menu.resolver'; import { ThemedPageErrorComponent } from './page-error/themed-page-error.component'; +import { siteRegisterGuard } from './core/data/feature-authorization/feature-authorization-guard/site-register.guard'; +import { + siteAdministratorGuard +} from './core/data/feature-authorization/feature-authorization-guard/site-administrator.guard'; +import { + groupAdministratorGuard +} from './core/data/feature-authorization/feature-authorization-guard/group-administrator.guard'; @NgModule({ imports: [ @@ -88,7 +88,7 @@ import { ThemedPageErrorComponent } from './page-error/themed-page-error.compone path: REGISTER_PATH, loadChildren: () => import('./register-page/register-page.module') .then((m) => m.RegisterPageModule), - canActivate: [SiteRegisterGuard] + canActivate: [siteRegisterGuard] }, { path: FORGOT_PASSWORD_PATH, @@ -154,7 +154,7 @@ import { ThemedPageErrorComponent } from './page-error/themed-page-error.compone path: ADMIN_MODULE_PATH, loadChildren: () => import('./admin/admin.module') .then((m) => m.AdminModule), - canActivate: [SiteAdministratorGuard, EndUserAgreementCurrentUserGuard] + canActivate: [siteAdministratorGuard, EndUserAgreementCurrentUserGuard] }, { path: 'login', @@ -229,7 +229,7 @@ import { ThemedPageErrorComponent } from './page-error/themed-page-error.compone { path: ACCESS_CONTROL_MODULE_PATH, loadChildren: () => import('./access-control/access-control.module').then((m) => m.AccessControlModule), - canActivate: [GroupAdministratorGuard, EndUserAgreementCurrentUserGuard], + canActivate: [groupAdministratorGuard, EndUserAgreementCurrentUserGuard], }, { path: 'subscriptions', @@ -248,7 +248,7 @@ import { ThemedPageErrorComponent } from './page-error/themed-page-error.compone initialNavigation: 'enabledBlocking', preloadingStrategy: NoPreloading, onSameUrlNavigation: 'reload', -}) + }) ], exports: [RouterModule], }) diff --git a/src/app/collection-page/collection-page-administrator.guard.ts b/src/app/collection-page/collection-page-administrator.guard.ts index c7866515b26..ee48d9fb0ae 100644 --- a/src/app/collection-page/collection-page-administrator.guard.ts +++ b/src/app/collection-page/collection-page-administrator.guard.ts @@ -1,31 +1,23 @@ -import { Injectable } from '@angular/core'; -import { ActivatedRouteSnapshot, Router, RouterStateSnapshot } from '@angular/router'; -import { Collection } from '../core/shared/collection.model'; -import { CollectionPageResolver } from './collection-page.resolver'; -import { AuthorizationDataService } from '../core/data/feature-authorization/authorization-data.service'; +import { inject } from '@angular/core'; +import { CanActivateFn, ResolveFn } from '@angular/router'; import { Observable, of as observableOf } from 'rxjs'; -import { DsoPageSingleFeatureGuard } from '../core/data/feature-authorization/feature-authorization-guard/dso-page-single-feature.guard'; +import { + dsoPageSingleFeatureGuard +} from '../core/data/feature-authorization/feature-authorization-guard/dso-page-single-feature.guard'; import { FeatureID } from '../core/data/feature-authorization/feature-id'; -import { AuthService } from '../core/auth/auth.service'; +import { RemoteData } from '../core/data/remote-data'; +import { CollectionPageResolver } from './collection-page.resolver'; +import { Collection } from '../core/shared/collection.model'; -@Injectable({ - providedIn: 'root' -}) /** * Guard for preventing unauthorized access to certain {@link Collection} pages requiring administrator rights + * Check administrator authorization rights */ -export class CollectionPageAdministratorGuard extends DsoPageSingleFeatureGuard { - constructor(protected resolver: CollectionPageResolver, - protected authorizationService: AuthorizationDataService, - protected router: Router, - protected authService: AuthService) { - super(resolver, authorizationService, router, authService); - } - - /** - * Check administrator authorization rights - */ - getFeatureID(next: ActivatedRouteSnapshot, state: RouterStateSnapshot): Observable { - return observableOf(FeatureID.AdministratorOf); - } -} +export const collectionPageAdministratorGuard: CanActivateFn = + dsoPageSingleFeatureGuard( + () => { + const collectionPageResolver = inject(CollectionPageResolver); + return collectionPageResolver.resolve as ResolveFn>>; + }, + () => observableOf(FeatureID.AdministratorOf) + ); diff --git a/src/app/collection-page/collection-page-routing.module.ts b/src/app/collection-page/collection-page-routing.module.ts index 9dc25b778ee..db978e74f80 100644 --- a/src/app/collection-page/collection-page-routing.module.ts +++ b/src/app/collection-page/collection-page-routing.module.ts @@ -17,11 +17,11 @@ import { COLLECTION_EDIT_PATH, COLLECTION_CREATE_PATH } from './collection-page-routing-paths'; -import { CollectionPageAdministratorGuard } from './collection-page-administrator.guard'; import { LinkMenuItemModel } from '../shared/menu/menu-item/models/link.model'; import { ThemedCollectionPageComponent } from './themed-collection-page.component'; import { MenuItemType } from '../shared/menu/menu-item-type.model'; import { DSOEditMenuResolver } from '../shared/dso-page/dso-edit-menu.resolver'; +import { collectionPageAdministratorGuard } from './collection-page-administrator.guard'; @NgModule({ imports: [ @@ -44,7 +44,7 @@ import { DSOEditMenuResolver } from '../shared/dso-page/dso-edit-menu.resolver'; path: COLLECTION_EDIT_PATH, loadChildren: () => import('./edit-collection-page/edit-collection-page.module') .then((m) => m.EditCollectionPageModule), - canActivate: [CollectionPageAdministratorGuard] + canActivate: [collectionPageAdministratorGuard] }, { path: 'delete', @@ -93,7 +93,6 @@ import { DSOEditMenuResolver } from '../shared/dso-page/dso-edit-menu.resolver'; DSOBreadcrumbsService, LinkService, CreateCollectionPageGuard, - CollectionPageAdministratorGuard, ] }) export class CollectionPageRoutingModule { diff --git a/src/app/collection-page/edit-collection-page/edit-collection-page.routing.module.ts b/src/app/collection-page/edit-collection-page/edit-collection-page.routing.module.ts index c4481985c0a..9c451c03bce 100644 --- a/src/app/collection-page/edit-collection-page/edit-collection-page.routing.module.ts +++ b/src/app/collection-page/edit-collection-page/edit-collection-page.routing.module.ts @@ -12,8 +12,10 @@ import { ResourcePolicyTargetResolver } from '../../shared/resource-policies/res import { ResourcePolicyCreateComponent } from '../../shared/resource-policies/create/resource-policy-create.component'; import { ResourcePolicyResolver } from '../../shared/resource-policies/resolvers/resource-policy.resolver'; import { ResourcePolicyEditComponent } from '../../shared/resource-policies/edit/resource-policy-edit.component'; -import { CollectionAdministratorGuard } from '../../core/data/feature-authorization/feature-authorization-guard/collection-administrator.guard'; import { CollectionAccessControlComponent } from './collection-access-control/collection-access-control.component'; +import { + collectionAdministratorGuard +} from '../../core/data/feature-authorization/feature-authorization-guard/collection-administrator.guard'; /** * Routing module that handles the routing for the Edit Collection page administrator functionality @@ -28,7 +30,7 @@ import { CollectionAccessControlComponent } from './collection-access-control/co }, data: { breadcrumbKey: 'collection.edit' }, component: EditCollectionPageComponent, - canActivate: [CollectionAdministratorGuard], + canActivate: [collectionAdministratorGuard], children: [ { path: '', diff --git a/src/app/community-page/community-page-administrator.guard.ts b/src/app/community-page/community-page-administrator.guard.ts index fd7ce5f7bf0..c41a7f0c4ce 100644 --- a/src/app/community-page/community-page-administrator.guard.ts +++ b/src/app/community-page/community-page-administrator.guard.ts @@ -1,31 +1,23 @@ -import { Injectable } from '@angular/core'; -import { ActivatedRouteSnapshot, Router, RouterStateSnapshot } from '@angular/router'; -import { Community } from '../core/shared/community.model'; -import { CommunityPageResolver } from './community-page.resolver'; -import { AuthorizationDataService } from '../core/data/feature-authorization/authorization-data.service'; +import { inject } from '@angular/core'; +import { CanActivateFn, ResolveFn } from '@angular/router'; import { Observable, of as observableOf } from 'rxjs'; -import { DsoPageSingleFeatureGuard } from '../core/data/feature-authorization/feature-authorization-guard/dso-page-single-feature.guard'; +import { + dsoPageSingleFeatureGuard +} from '../core/data/feature-authorization/feature-authorization-guard/dso-page-single-feature.guard'; import { FeatureID } from '../core/data/feature-authorization/feature-id'; -import { AuthService } from '../core/auth/auth.service'; +import { RemoteData } from '../core/data/remote-data'; +import { Community } from '../core/shared/community.model'; +import { CommunityPageResolver } from './community-page.resolver'; -@Injectable({ - providedIn: 'root' -}) /** * Guard for preventing unauthorized access to certain {@link Community} pages requiring administrator rights + * Check administrator authorization rights */ -export class CommunityPageAdministratorGuard extends DsoPageSingleFeatureGuard { - constructor(protected resolver: CommunityPageResolver, - protected authorizationService: AuthorizationDataService, - protected router: Router, - protected authService: AuthService) { - super(resolver, authorizationService, router, authService); - } - - /** - * Check administrator authorization rights - */ - getFeatureID(next: ActivatedRouteSnapshot, state: RouterStateSnapshot): Observable { - return observableOf(FeatureID.AdministratorOf); - } -} +export const communityPageAdministratorGuard: CanActivateFn = + dsoPageSingleFeatureGuard( + () => { + const communityPageResolver = inject(CommunityPageResolver); + return communityPageResolver.resolve as ResolveFn>>; + }, + () => observableOf(FeatureID.AdministratorOf) + ); diff --git a/src/app/community-page/community-page-routing.module.ts b/src/app/community-page/community-page-routing.module.ts index c37f8832f84..c34ce7966ad 100644 --- a/src/app/community-page/community-page-routing.module.ts +++ b/src/app/community-page/community-page-routing.module.ts @@ -10,11 +10,11 @@ import { CommunityBreadcrumbResolver } from '../core/breadcrumbs/community-bread import { DSOBreadcrumbsService } from '../core/breadcrumbs/dso-breadcrumbs.service'; import { LinkService } from '../core/cache/builders/link.service'; import { COMMUNITY_EDIT_PATH, COMMUNITY_CREATE_PATH } from './community-page-routing-paths'; -import { CommunityPageAdministratorGuard } from './community-page-administrator.guard'; import { LinkMenuItemModel } from '../shared/menu/menu-item/models/link.model'; import { ThemedCommunityPageComponent } from './themed-community-page.component'; import { MenuItemType } from '../shared/menu/menu-item-type.model'; import { DSOEditMenuResolver } from '../shared/dso-page/dso-edit-menu.resolver'; +import { communityPageAdministratorGuard } from './community-page-administrator.guard'; @NgModule({ imports: [ @@ -37,7 +37,7 @@ import { DSOEditMenuResolver } from '../shared/dso-page/dso-edit-menu.resolver'; path: COMMUNITY_EDIT_PATH, loadChildren: () => import('./edit-community-page/edit-community-page.module') .then((m) => m.EditCommunityPageModule), - canActivate: [CommunityPageAdministratorGuard] + canActivate: [communityPageAdministratorGuard] }, { path: 'delete', @@ -75,7 +75,6 @@ import { DSOEditMenuResolver } from '../shared/dso-page/dso-edit-menu.resolver'; DSOBreadcrumbsService, LinkService, CreateCommunityPageGuard, - CommunityPageAdministratorGuard, ] }) export class CommunityPageRoutingModule { diff --git a/src/app/community-page/edit-community-page/edit-community-page.routing.module.ts b/src/app/community-page/edit-community-page/edit-community-page.routing.module.ts index 994c6b5e961..c9aa7864ce1 100644 --- a/src/app/community-page/edit-community-page/edit-community-page.routing.module.ts +++ b/src/app/community-page/edit-community-page/edit-community-page.routing.module.ts @@ -10,8 +10,10 @@ import { ResourcePolicyTargetResolver } from '../../shared/resource-policies/res import { ResourcePolicyCreateComponent } from '../../shared/resource-policies/create/resource-policy-create.component'; import { ResourcePolicyResolver } from '../../shared/resource-policies/resolvers/resource-policy.resolver'; import { ResourcePolicyEditComponent } from '../../shared/resource-policies/edit/resource-policy-edit.component'; -import { CommunityAdministratorGuard } from '../../core/data/feature-authorization/feature-authorization-guard/community-administrator.guard'; import { CommunityAccessControlComponent } from './community-access-control/community-access-control.component'; +import { + communityAdministratorGuard +} from '../../core/data/feature-authorization/feature-authorization-guard/community-administrator.guard'; /** * Routing module that handles the routing for the Edit Community page administrator functionality @@ -26,7 +28,7 @@ import { CommunityAccessControlComponent } from './community-access-control/comm }, data: { breadcrumbKey: 'community.edit' }, component: EditCommunityPageComponent, - canActivate: [CommunityAdministratorGuard], + canActivate: [communityAdministratorGuard], children: [ { path: '', diff --git a/src/app/core/core.module.ts b/src/app/core/core.module.ts index dbca773375a..3b651fb4371 100644 --- a/src/app/core/core.module.ts +++ b/src/app/core/core.module.ts @@ -126,9 +126,6 @@ import { Feature } from './shared/feature.model'; import { Authorization } from './shared/authorization.model'; import { FeatureDataService } from './data/feature-authorization/feature-data.service'; import { AuthorizationDataService } from './data/feature-authorization/authorization-data.service'; -import { - SiteAdministratorGuard -} from './data/feature-authorization/feature-authorization-guard/site-administrator.guard'; import { Registration } from './shared/registration.model'; import { MetadataSchemaDataService } from './data/metadata-schema-data.service'; import { MetadataFieldDataService } from './data/metadata-field-data.service'; @@ -147,7 +144,6 @@ import { ReloadGuard } from './reload/reload.guard'; import { EndUserAgreementCurrentUserGuard } from './end-user-agreement/end-user-agreement-current-user.guard'; import { EndUserAgreementCookieGuard } from './end-user-agreement/end-user-agreement-cookie.guard'; import { EndUserAgreementService } from './end-user-agreement/end-user-agreement.service'; -import { SiteRegisterGuard } from './data/feature-authorization/feature-authorization-guard/site-register.guard'; import { ShortLivedToken } from './auth/models/short-lived-token.model'; import { UsageReport } from './statistics/models/usage-report.model'; import { RootDataService } from './data/root-data.service'; @@ -183,6 +179,8 @@ import { ValueListBrowseDefinition } from './shared/value-list-browse-definition import { NonHierarchicalBrowseDefinition } from './shared/non-hierarchical-browse-definition'; import { BulkAccessConditionOptions } from './config/models/bulk-access-condition-options.model'; + + /** * When not in production, endpoint responses can be mocked for testing purposes * If there is no mock version available for the endpoint, the actual REST response will be used just like in production mode @@ -280,8 +278,6 @@ const PROVIDERS = [ ScriptDataService, FeatureDataService, AuthorizationDataService, - SiteAdministratorGuard, - SiteRegisterGuard, MetadataSchemaDataService, MetadataFieldDataService, TokenResponseParsingService, diff --git a/src/app/core/data/feature-authorization/feature-authorization-guard/collection-administrator.guard.ts b/src/app/core/data/feature-authorization/feature-authorization-guard/collection-administrator.guard.ts index b41a322cb62..4eb5ca15668 100644 --- a/src/app/core/data/feature-authorization/feature-authorization-guard/collection-administrator.guard.ts +++ b/src/app/core/data/feature-authorization/feature-authorization-guard/collection-administrator.guard.ts @@ -1,27 +1,12 @@ -import { Injectable } from '@angular/core'; -import { SingleFeatureAuthorizationGuard } from './single-feature-authorization.guard'; -import { AuthorizationDataService } from '../authorization-data.service'; -import { ActivatedRouteSnapshot, Router, RouterStateSnapshot } from '@angular/router'; -import { AuthService } from '../../../auth/auth.service'; -import { Observable, of as observableOf } from 'rxjs'; +import { CanActivateFn } from '@angular/router'; +import { singleFeatureAuthorizationGuard } from './single-feature-authorization.guard'; +import { of as observableOf } from 'rxjs'; import { FeatureID } from '../feature-id'; /** * Prevent unauthorized activating and loading of routes when the current authenticated user * isn't a Collection administrator + * Check group management rights */ -@Injectable({ - providedIn: 'root' -}) -export class CollectionAdministratorGuard extends SingleFeatureAuthorizationGuard { - constructor(protected authorizationService: AuthorizationDataService, protected router: Router, protected authService: AuthService) { - super(authorizationService, router, authService); - } - - /** - * Check group management rights - */ - getFeatureID(route: ActivatedRouteSnapshot, state: RouterStateSnapshot): Observable { - return observableOf(FeatureID.IsCollectionAdmin); - } -} +export const collectionAdministratorGuard: CanActivateFn = + singleFeatureAuthorizationGuard(() => observableOf(FeatureID.IsCollectionAdmin)); diff --git a/src/app/core/data/feature-authorization/feature-authorization-guard/community-administrator.guard.ts b/src/app/core/data/feature-authorization/feature-authorization-guard/community-administrator.guard.ts index 2ab77a00cc4..4b3630db235 100644 --- a/src/app/core/data/feature-authorization/feature-authorization-guard/community-administrator.guard.ts +++ b/src/app/core/data/feature-authorization/feature-authorization-guard/community-administrator.guard.ts @@ -1,27 +1,12 @@ -import { Injectable } from '@angular/core'; -import { SingleFeatureAuthorizationGuard } from './single-feature-authorization.guard'; -import { AuthorizationDataService } from '../authorization-data.service'; -import { ActivatedRouteSnapshot, Router, RouterStateSnapshot } from '@angular/router'; -import { AuthService } from '../../../auth/auth.service'; -import { Observable, of as observableOf } from 'rxjs'; +import { CanActivateFn } from '@angular/router'; +import { singleFeatureAuthorizationGuard } from './single-feature-authorization.guard'; +import { of as observableOf } from 'rxjs'; import { FeatureID } from '../feature-id'; /** * Prevent unauthorized activating and loading of routes when the current authenticated user * isn't a Community administrator + * Check group management rights */ -@Injectable({ - providedIn: 'root' -}) -export class CommunityAdministratorGuard extends SingleFeatureAuthorizationGuard { - constructor(protected authorizationService: AuthorizationDataService, protected router: Router, protected authService: AuthService) { - super(authorizationService, router, authService); - } - - /** - * Check group management rights - */ - getFeatureID(route: ActivatedRouteSnapshot, state: RouterStateSnapshot): Observable { - return observableOf(FeatureID.IsCommunityAdmin); - } -} +export const communityAdministratorGuard: CanActivateFn = + singleFeatureAuthorizationGuard(() => observableOf(FeatureID.IsCommunityAdmin)); diff --git a/src/app/core/data/feature-authorization/feature-authorization-guard/dso-page-single-feature.guard.spec.ts b/src/app/core/data/feature-authorization/feature-authorization-guard/dso-page-single-feature.guard.spec.ts index 6c1f330c695..fdd173ab4dc 100644 --- a/src/app/core/data/feature-authorization/feature-authorization-guard/dso-page-single-feature.guard.spec.ts +++ b/src/app/core/data/feature-authorization/feature-authorization-guard/dso-page-single-feature.guard.spec.ts @@ -1,87 +1,87 @@ -import { AuthorizationDataService } from '../authorization-data.service'; -import { ActivatedRouteSnapshot, Resolve, Router, RouterStateSnapshot } from '@angular/router'; -import { RemoteData } from '../../remote-data'; -import { Observable, of as observableOf } from 'rxjs'; -import { createSuccessfulRemoteDataObject$ } from '../../../../shared/remote-data.utils'; -import { DSpaceObject } from '../../../shared/dspace-object.model'; -import { DsoPageSingleFeatureGuard } from './dso-page-single-feature.guard'; -import { FeatureID } from '../feature-id'; -import { AuthService } from '../../../auth/auth.service'; - -/** - * Test implementation of abstract class DsoPageSingleFeatureGuard - */ -class DsoPageSingleFeatureGuardImpl extends DsoPageSingleFeatureGuard { - constructor(protected resolver: Resolve>, - protected authorizationService: AuthorizationDataService, - protected router: Router, - protected authService: AuthService, - protected featureID: FeatureID) { - super(resolver, authorizationService, router, authService); - } - - getFeatureID(route: ActivatedRouteSnapshot, state: RouterStateSnapshot): Observable { - return observableOf(this.featureID); - } -} - -describe('DsoPageSingleFeatureGuard', () => { - let guard: DsoPageSingleFeatureGuard; - let authorizationService: AuthorizationDataService; - let router: Router; - let authService: AuthService; - let resolver: Resolve>; - let object: DSpaceObject; - let route; - let parentRoute; - - function init() { - object = { - self: 'test-selflink' - } as DSpaceObject; - - authorizationService = jasmine.createSpyObj('authorizationService', { - isAuthorized: observableOf(true) - }); - router = jasmine.createSpyObj('router', { - parseUrl: {} - }); - resolver = jasmine.createSpyObj('resolver', { - resolve: createSuccessfulRemoteDataObject$(object) - }); - authService = jasmine.createSpyObj('authService', { - isAuthenticated: observableOf(true) - }); - parentRoute = { - params: { - id: '3e1a5327-dabb-41ff-af93-e6cab9d032f0' - } - }; - route = { - params: { - }, - parent: parentRoute - }; - guard = new DsoPageSingleFeatureGuardImpl(resolver, authorizationService, router, authService, undefined); - } - - beforeEach(() => { - init(); - }); - - describe('getObjectUrl', () => { - it('should return the resolved object\'s selflink', (done) => { - guard.getObjectUrl(route, undefined).subscribe((selflink) => { - expect(selflink).toEqual(object.self); - done(); - }); - }); - }); - - describe('getRouteWithDSOId', () => { - it('should return the route that has the UUID of the DSO', () => { - const foundRoute = (guard as any).getRouteWithDSOId(route); - expect(foundRoute).toBe(parentRoute); - }); - }); -}); +// import { AuthorizationDataService } from '../authorization-data.service'; +// import { ActivatedRouteSnapshot, Resolve, Router, RouterStateSnapshot } from '@angular/router'; +// import { RemoteData } from '../../remote-data'; +// import { Observable, of as observableOf } from 'rxjs'; +// import { createSuccessfulRemoteDataObject$ } from '../../../../shared/remote-data.utils'; +// import { DSpaceObject } from '../../../shared/dspace-object.model'; +// import { DsoPageSingleFeatureGuard } from './dso-page-single-feature.guard'; +// import { FeatureID } from '../feature-id'; +// import { AuthService } from '../../../auth/auth.service'; +// +// /** +// * Test implementation of abstract class DsoPageSingleFeatureGuard +// */ +// class DsoPageSingleFeatureGuardImpl extends DsoPageSingleFeatureGuard { +// constructor(protected resolver: Resolve>, +// protected authorizationService: AuthorizationDataService, +// protected router: Router, +// protected authService: AuthService, +// protected featureID: FeatureID) { +// super(resolver, authorizationService, router, authService); +// } +// +// getFeatureID(route: ActivatedRouteSnapshot, state: RouterStateSnapshot): Observable { +// return observableOf(this.featureID); +// } +// } +// +// describe('DsoPageSingleFeatureGuard', () => { +// let guard: DsoPageSingleFeatureGuard; +// let authorizationService: AuthorizationDataService; +// let router: Router; +// let authService: AuthService; +// let resolver: Resolve>; +// let object: DSpaceObject; +// let route; +// let parentRoute; +// +// function init() { +// object = { +// self: 'test-selflink' +// } as DSpaceObject; +// +// authorizationService = jasmine.createSpyObj('authorizationService', { +// isAuthorized: observableOf(true) +// }); +// router = jasmine.createSpyObj('router', { +// parseUrl: {} +// }); +// resolver = jasmine.createSpyObj('resolver', { +// resolve: createSuccessfulRemoteDataObject$(object) +// }); +// authService = jasmine.createSpyObj('authService', { +// isAuthenticated: observableOf(true) +// }); +// parentRoute = { +// params: { +// id: '3e1a5327-dabb-41ff-af93-e6cab9d032f0' +// } +// }; +// route = { +// params: { +// }, +// parent: parentRoute +// }; +// guard = new DsoPageSingleFeatureGuardImpl(resolver, authorizationService, router, authService, undefined); +// } +// +// beforeEach(() => { +// init(); +// }); +// +// describe('getObjectUrl', () => { +// it('should return the resolved object\'s selflink', (done) => { +// guard.getObjectUrl(route, undefined).subscribe((selflink) => { +// expect(selflink).toEqual(object.self); +// done(); +// }); +// }); +// }); +// +// describe('getRouteWithDSOId', () => { +// it('should return the route that has the UUID of the DSO', () => { +// const foundRoute = (guard as any).getRouteWithDSOId(route); +// expect(foundRoute).toBe(parentRoute); +// }); +// }); +// }); diff --git a/src/app/core/data/feature-authorization/feature-authorization-guard/dso-page-single-feature.guard.ts b/src/app/core/data/feature-authorization/feature-authorization-guard/dso-page-single-feature.guard.ts index 3fc90f90696..0055d983d39 100644 --- a/src/app/core/data/feature-authorization/feature-authorization-guard/dso-page-single-feature.guard.ts +++ b/src/app/core/data/feature-authorization/feature-authorization-guard/dso-page-single-feature.guard.ts @@ -1,27 +1,21 @@ -import { DSpaceObject } from '../../../shared/dspace-object.model'; -import { DsoPageSomeFeatureGuard } from './dso-page-some-feature.guard'; -import { ActivatedRouteSnapshot, RouterStateSnapshot } from '@angular/router'; +import { CanActivateFn, ActivatedRouteSnapshot, RouterStateSnapshot, ResolveFn } from '@angular/router'; +import { SingleFeatureGuardParamFn } from './single-feature-authorization.guard'; +import { dsoPageSomeFeatureGuard } from './dso-page-some-feature.guard'; +import { Observable } from 'rxjs'; import { FeatureID } from '../feature-id'; import { map } from 'rxjs/operators'; -import { Observable } from 'rxjs'; +import { DSpaceObject } from '../../../shared/dspace-object.model'; +import { RemoteData } from '../../remote-data'; /** * Abstract Guard for preventing unauthorized access to {@link DSpaceObject} pages that require rights for a specific feature * This guard utilizes a resolver to retrieve the relevant object to check authorizations for */ -export abstract class DsoPageSingleFeatureGuard extends DsoPageSomeFeatureGuard { - /** - * The features to check authorization for - */ - getFeatureIDs(route: ActivatedRouteSnapshot, state: RouterStateSnapshot): Observable { - return this.getFeatureID(route, state).pipe( - map((featureID) => [featureID]), - ); - } - - /** - * The type of feature to check authorization for - * Override this method to define a feature - */ - abstract getFeatureID(route: ActivatedRouteSnapshot, state: RouterStateSnapshot): Observable; -} +export const dsoPageSingleFeatureGuard = ( + getResolveFn: () => ResolveFn>>, + getFeatureID: SingleFeatureGuardParamFn +): CanActivateFn => dsoPageSomeFeatureGuard( + getResolveFn, + (route: ActivatedRouteSnapshot, state: RouterStateSnapshot): Observable => getFeatureID(route, state).pipe( + map((featureID: FeatureID) => [featureID]), +)); diff --git a/src/app/core/data/feature-authorization/feature-authorization-guard/dso-page-some-feature.guard.spec.ts b/src/app/core/data/feature-authorization/feature-authorization-guard/dso-page-some-feature.guard.spec.ts index 071b1b0731f..8735aa92935 100644 --- a/src/app/core/data/feature-authorization/feature-authorization-guard/dso-page-some-feature.guard.spec.ts +++ b/src/app/core/data/feature-authorization/feature-authorization-guard/dso-page-some-feature.guard.spec.ts @@ -1,87 +1,87 @@ -import { AuthorizationDataService } from '../authorization-data.service'; -import { ActivatedRouteSnapshot, Resolve, Router, RouterStateSnapshot } from '@angular/router'; -import { RemoteData } from '../../remote-data'; -import { Observable, of as observableOf } from 'rxjs'; -import { createSuccessfulRemoteDataObject$ } from '../../../../shared/remote-data.utils'; -import { DSpaceObject } from '../../../shared/dspace-object.model'; -import { FeatureID } from '../feature-id'; -import { AuthService } from '../../../auth/auth.service'; -import { DsoPageSomeFeatureGuard } from './dso-page-some-feature.guard'; - -/** - * Test implementation of abstract class DsoPageSomeFeatureGuard - */ -class DsoPageSomeFeatureGuardImpl extends DsoPageSomeFeatureGuard { - constructor(protected resolver: Resolve>, - protected authorizationService: AuthorizationDataService, - protected router: Router, - protected authService: AuthService, - protected featureIDs: FeatureID[]) { - super(resolver, authorizationService, router, authService); - } - - getFeatureIDs(route: ActivatedRouteSnapshot, state: RouterStateSnapshot): Observable { - return observableOf(this.featureIDs); - } -} - -describe('DsoPageSomeFeatureGuard', () => { - let guard: DsoPageSomeFeatureGuard; - let authorizationService: AuthorizationDataService; - let router: Router; - let authService: AuthService; - let resolver: Resolve>; - let object: DSpaceObject; - let route; - let parentRoute; - - function init() { - object = { - self: 'test-selflink' - } as DSpaceObject; - - authorizationService = jasmine.createSpyObj('authorizationService', { - isAuthorized: observableOf(true) - }); - router = jasmine.createSpyObj('router', { - parseUrl: {} - }); - resolver = jasmine.createSpyObj('resolver', { - resolve: createSuccessfulRemoteDataObject$(object) - }); - authService = jasmine.createSpyObj('authService', { - isAuthenticated: observableOf(true) - }); - parentRoute = { - params: { - id: '3e1a5327-dabb-41ff-af93-e6cab9d032f0' - } - }; - route = { - params: { - }, - parent: parentRoute - }; - guard = new DsoPageSomeFeatureGuardImpl(resolver, authorizationService, router, authService, []); - } - - beforeEach(() => { - init(); - }); - - describe('getObjectUrl', () => { - it('should return the resolved object\'s selflink', (done) => { - guard.getObjectUrl(route, undefined).subscribe((selflink) => { - expect(selflink).toEqual(object.self); - done(); - }); - }); - }); - - describe('getRouteWithDSOId', () => { - it('should return the route that has the UUID of the DSO', () => { - const foundRoute = (guard as any).getRouteWithDSOId(route); - expect(foundRoute).toBe(parentRoute); - }); - }); -}); +// import { AuthorizationDataService } from '../authorization-data.service'; +// import { ActivatedRouteSnapshot, Resolve, Router, RouterStateSnapshot } from '@angular/router'; +// import { RemoteData } from '../../remote-data'; +// import { Observable, of as observableOf } from 'rxjs'; +// import { createSuccessfulRemoteDataObject$ } from '../../../../shared/remote-data.utils'; +// import { DSpaceObject } from '../../../shared/dspace-object.model'; +// import { FeatureID } from '../feature-id'; +// import { AuthService } from '../../../auth/auth.service'; +// import { DsoPageSomeFeatureGuard } from './dso-page-some-feature.guard'; +// +// /** +// * Test implementation of abstract class DsoPageSomeFeatureGuard +// */ +// class DsoPageSomeFeatureGuardImpl extends DsoPageSomeFeatureGuard { +// constructor(protected resolver: Resolve>, +// protected authorizationService: AuthorizationDataService, +// protected router: Router, +// protected authService: AuthService, +// protected featureIDs: FeatureID[]) { +// super(resolver, authorizationService, router, authService); +// } +// +// getFeatureIDs(route: ActivatedRouteSnapshot, state: RouterStateSnapshot): Observable { +// return observableOf(this.featureIDs); +// } +// } +// +// describe('DsoPageSomeFeatureGuard', () => { +// let guard: DsoPageSomeFeatureGuard; +// let authorizationService: AuthorizationDataService; +// let router: Router; +// let authService: AuthService; +// let resolver: Resolve>; +// let object: DSpaceObject; +// let route; +// let parentRoute; +// +// function init() { +// object = { +// self: 'test-selflink' +// } as DSpaceObject; +// +// authorizationService = jasmine.createSpyObj('authorizationService', { +// isAuthorized: observableOf(true) +// }); +// router = jasmine.createSpyObj('router', { +// parseUrl: {} +// }); +// resolver = jasmine.createSpyObj('resolver', { +// resolve: createSuccessfulRemoteDataObject$(object) +// }); +// authService = jasmine.createSpyObj('authService', { +// isAuthenticated: observableOf(true) +// }); +// parentRoute = { +// params: { +// id: '3e1a5327-dabb-41ff-af93-e6cab9d032f0' +// } +// }; +// route = { +// params: { +// }, +// parent: parentRoute +// }; +// guard = new DsoPageSomeFeatureGuardImpl(resolver, authorizationService, router, authService, []); +// } +// +// beforeEach(() => { +// init(); +// }); +// +// describe('getObjectUrl', () => { +// it('should return the resolved object\'s selflink', (done) => { +// guard.getObjectUrl(route, undefined).subscribe((selflink) => { +// expect(selflink).toEqual(object.self); +// done(); +// }); +// }); +// }); +// +// describe('getRouteWithDSOId', () => { +// it('should return the route that has the UUID of the DSO', () => { +// const foundRoute = (guard as any).getRouteWithDSOId(route); +// expect(foundRoute).toBe(parentRoute); +// }); +// }); +// }); diff --git a/src/app/core/data/feature-authorization/feature-authorization-guard/dso-page-some-feature.guard.ts b/src/app/core/data/feature-authorization/feature-authorization-guard/dso-page-some-feature.guard.ts index 86837093459..165edd7f5bb 100644 --- a/src/app/core/data/feature-authorization/feature-authorization-guard/dso-page-some-feature.guard.ts +++ b/src/app/core/data/feature-authorization/feature-authorization-guard/dso-page-some-feature.guard.ts @@ -1,46 +1,51 @@ -import { ActivatedRouteSnapshot, Resolve, Router, RouterStateSnapshot } from '@angular/router'; -import { RemoteData } from '../../remote-data'; -import { AuthorizationDataService } from '../authorization-data.service'; +import { DSpaceObject } from '../../../shared/dspace-object.model'; +import { ResolveFn, ActivatedRouteSnapshot, RouterStateSnapshot, CanActivateFn } from '@angular/router'; import { Observable } from 'rxjs'; +import { RemoteData } from '../../remote-data'; +import { hasValue, hasNoValue } from '../../../../shared/empty.util'; import { getAllSucceededRemoteDataPayload } from '../../../shared/operators'; import { map } from 'rxjs/operators'; -import { DSpaceObject } from '../../../shared/dspace-object.model'; -import { AuthService } from '../../../auth/auth.service'; -import { hasNoValue, hasValue } from '../../../../shared/empty.util'; -import { SomeFeatureAuthorizationGuard } from './some-feature-authorization.guard'; +import { + StringGuardParamFn, + SomeFeatureGuardParamFn, + someFeatureAuthorizationGuard +} from './some-feature-authorization.guard'; +import { FeatureID } from '../feature-id'; + +export declare type DSOGetObjectURlFn = (resolve: ResolveFn>>) => StringGuardParamFn; + /** - * Abstract Guard for preventing unauthorized access to {@link DSpaceObject} pages that require rights for any specific feature in a list - * This guard utilizes a resolver to retrieve the relevant object to check authorizations for + * Method to resolve resolve (parent) route that contains the UUID of the DSO + * @param route The current route */ -export abstract class DsoPageSomeFeatureGuard extends SomeFeatureAuthorizationGuard { - constructor(protected resolver: Resolve>, - protected authorizationService: AuthorizationDataService, - protected router: Router, - protected authService: AuthService) { - super(authorizationService, router, authService); +const getRouteWithDSOId = (route: ActivatedRouteSnapshot): ActivatedRouteSnapshot => { + let routeWithDSOId = route; + while (hasNoValue(routeWithDSOId.params.id) && hasValue(routeWithDSOId.parent)) { + routeWithDSOId = routeWithDSOId.parent; } + return routeWithDSOId; +}; - /** - * Check authorization rights for the object resolved using the provided resolver - */ - getObjectUrl(route: ActivatedRouteSnapshot, state: RouterStateSnapshot): Observable { - const routeWithObjectID = this.getRouteWithDSOId(route); - return (this.resolver.resolve(routeWithObjectID, state) as Observable>).pipe( + + +const defaultDSOGetObjectUrl: DSOGetObjectURlFn = (resolve: ResolveFn>>): StringGuardParamFn => { + return (route: ActivatedRouteSnapshot, state: RouterStateSnapshot): Observable => { + const routeWithObjectID = getRouteWithDSOId(route); + return (resolve(routeWithObjectID, state) as Observable>).pipe( getAllSucceededRemoteDataPayload(), map((dso) => dso.self) ); - } + }; +}; - /** - * Method to resolve resolve (parent) route that contains the UUID of the DSO - * @param route The current route - */ - protected getRouteWithDSOId(route: ActivatedRouteSnapshot): ActivatedRouteSnapshot { - let routeWithDSOId = route; - while (hasNoValue(routeWithDSOId.params.id) && hasValue(routeWithDSOId.parent)) { - routeWithDSOId = routeWithDSOId.parent; - } - return routeWithDSOId; - } -} +/** + * Guard for preventing unauthorized access to {@link DSpaceObject} pages that require rights for any specific feature in a list + * This guard utilizes a resolver to retrieve the relevant object to check authorizations for + */ +export const dsoPageSomeFeatureGuard = ( + getResolveFn: () => ResolveFn>>, + getFeatureIDs: SomeFeatureGuardParamFn, + getObjectUrl: DSOGetObjectURlFn = defaultDSOGetObjectUrl, + getEPersonUuid?: StringGuardParamFn, +): CanActivateFn => someFeatureAuthorizationGuard((route: ActivatedRouteSnapshot, state: RouterStateSnapshot): Observable => getFeatureIDs(route, state), getObjectUrl(getResolveFn()), getEPersonUuid); diff --git a/src/app/core/data/feature-authorization/feature-authorization-guard/group-administrator.guard.ts b/src/app/core/data/feature-authorization/feature-authorization-guard/group-administrator.guard.ts index 5afd572326f..6d4e02dbcfb 100644 --- a/src/app/core/data/feature-authorization/feature-authorization-guard/group-administrator.guard.ts +++ b/src/app/core/data/feature-authorization/feature-authorization-guard/group-administrator.guard.ts @@ -1,27 +1,11 @@ -import { Injectable } from '@angular/core'; -import { SingleFeatureAuthorizationGuard } from './single-feature-authorization.guard'; -import { AuthorizationDataService } from '../authorization-data.service'; -import { ActivatedRouteSnapshot, Router, RouterStateSnapshot } from '@angular/router'; -import { AuthService } from '../../../auth/auth.service'; -import { Observable, of as observableOf } from 'rxjs'; +import { CanActivateFn } from '@angular/router'; +import { singleFeatureAuthorizationGuard } from './single-feature-authorization.guard'; +import { of as observableOf } from 'rxjs'; import { FeatureID } from '../feature-id'; /** * Prevent unauthorized activating and loading of routes when the current authenticated user doesn't have group * management rights */ -@Injectable({ - providedIn: 'root' -}) -export class GroupAdministratorGuard extends SingleFeatureAuthorizationGuard { - constructor(protected authorizationService: AuthorizationDataService, protected router: Router, protected authService: AuthService) { - super(authorizationService, router, authService); - } - - /** - * Check group management rights - */ - getFeatureID(route: ActivatedRouteSnapshot, state: RouterStateSnapshot): Observable { - return observableOf(FeatureID.CanManageGroups); - } -} +export const groupAdministratorGuard: CanActivateFn = + singleFeatureAuthorizationGuard(() => observableOf(FeatureID.CanManageGroups)); diff --git a/src/app/core/data/feature-authorization/feature-authorization-guard/single-feature-authorization.guard.spec.ts b/src/app/core/data/feature-authorization/feature-authorization-guard/single-feature-authorization.guard.spec.ts index 635aa3530bd..92d1118ea9c 100644 --- a/src/app/core/data/feature-authorization/feature-authorization-guard/single-feature-authorization.guard.spec.ts +++ b/src/app/core/data/feature-authorization/feature-authorization-guard/single-feature-authorization.guard.spec.ts @@ -1,39 +1,13 @@ -import { SingleFeatureAuthorizationGuard } from './single-feature-authorization.guard'; import { AuthorizationDataService } from '../authorization-data.service'; import { FeatureID } from '../feature-id'; -import { Observable, of as observableOf } from 'rxjs'; -import { ActivatedRouteSnapshot, Router, RouterStateSnapshot } from '@angular/router'; +import { of as observableOf } from 'rxjs'; +import { Router } from '@angular/router'; import { AuthService } from '../../../auth/auth.service'; +import { singleFeatureAuthorizationGuard } from './single-feature-authorization.guard'; -/** - * Test implementation of abstract class SingleFeatureAuthorizationGuard - * Provide the return values of the overwritten getters as constructor arguments - */ -class SingleFeatureAuthorizationGuardImpl extends SingleFeatureAuthorizationGuard { - constructor(protected authorizationService: AuthorizationDataService, - protected router: Router, - protected authService: AuthService, - protected featureId: FeatureID, - protected objectUrl: string, - protected ePersonUuid: string) { - super(authorizationService, router, authService); - } - - getFeatureID(route: ActivatedRouteSnapshot, state: RouterStateSnapshot): Observable { - return observableOf(this.featureId); - } - - getObjectUrl(route: ActivatedRouteSnapshot, state: RouterStateSnapshot): Observable { - return observableOf(this.objectUrl); - } - - getEPersonUuid(route: ActivatedRouteSnapshot, state: RouterStateSnapshot): Observable { - return observableOf(this.ePersonUuid); - } -} describe('SingleFeatureAuthorizationGuard', () => { - let guard: SingleFeatureAuthorizationGuard; + let guard: any; let authorizationService: AuthorizationDataService; let router: Router; let authService: AuthService; @@ -48,15 +22,16 @@ describe('SingleFeatureAuthorizationGuard', () => { ePersonUuid = 'fake-eperson-uuid'; authorizationService = jasmine.createSpyObj('authorizationService', { - isAuthorized: observableOf(true) + isAuthorized: observableOf(true), }); router = jasmine.createSpyObj('router', { - parseUrl: {} + parseUrl: {}, }); authService = jasmine.createSpyObj('authService', { - isAuthenticated: observableOf(true) + isAuthenticated: observableOf(true), }); - guard = new SingleFeatureAuthorizationGuardImpl(authorizationService, router, authService, featureId, objectUrl, ePersonUuid); + + guard = singleFeatureAuthorizationGuard; } beforeEach(() => { @@ -64,9 +39,13 @@ describe('SingleFeatureAuthorizationGuard', () => { }); describe('canActivate', () => { - it('should call authorizationService.isAuthenticated with the appropriate arguments', () => { - guard.canActivate(undefined, { url: 'current-url' } as any).subscribe(); - expect(authorizationService.isAuthorized).toHaveBeenCalledWith(featureId, objectUrl, ePersonUuid); - }); + it('should call authorizationService.isAuthorized with the appropriate arguments', (done) => { + guard(observableOf(featureId), observableOf(objectUrl), observableOf(ePersonUuid))(undefined, { url: 'current-url' } as any).subscribe(() => { + expect(authorizationService.isAuthorized).toHaveBeenCalledWith(featureId, objectUrl, ePersonUuid); + done(); + }); + + }, 10000); }); }); + diff --git a/src/app/core/data/feature-authorization/feature-authorization-guard/single-feature-authorization.guard.ts b/src/app/core/data/feature-authorization/feature-authorization-guard/single-feature-authorization.guard.ts index cb71d2f4181..4b37f1d0bac 100644 --- a/src/app/core/data/feature-authorization/feature-authorization-guard/single-feature-authorization.guard.ts +++ b/src/app/core/data/feature-authorization/feature-authorization-guard/single-feature-authorization.guard.ts @@ -1,27 +1,27 @@ -import { ActivatedRouteSnapshot, RouterStateSnapshot } from '@angular/router'; -import { FeatureID } from '../feature-id'; +import { ActivatedRouteSnapshot, RouterStateSnapshot, CanActivateFn } from '@angular/router'; import { Observable } from 'rxjs'; -import { map} from 'rxjs/operators'; -import { SomeFeatureAuthorizationGuard } from './some-feature-authorization.guard'; +import { FeatureID } from '../feature-id'; +import { map } from 'rxjs/operators'; +import { StringGuardParamFn, someFeatureAuthorizationGuard } from './some-feature-authorization.guard'; + +export declare type SingleFeatureGuardParamFn = (route: ActivatedRouteSnapshot, state: RouterStateSnapshot) => Observable; /** - * Abstract Guard for preventing unauthorized activating and loading of routes when a user - * doesn't have authorized rights on a specific feature and/or object. - * Override the desired getters in the parent class for checking specific authorization on a feature and/or object. + * Guard for preventing unauthorized activating and loading of routes when a user doesn't have + * authorized rights on a specific feature and/or object. + * + * @param getFeatureID The feature to check authorization for + * @param getObjectUrl The URL of the object to check if the user has authorized rights for, + * Optional, if not provided, the {@link Site}'s URL will be assumed + * @param getEPersonUuid The UUID of the user to check authorization rights for. + * Optional, if not provided, the authenticated user's UUID will be assumed. */ -export abstract class SingleFeatureAuthorizationGuard extends SomeFeatureAuthorizationGuard { - /** - * The features to check authorization for - */ - getFeatureIDs(route: ActivatedRouteSnapshot, state: RouterStateSnapshot): Observable { - return this.getFeatureID(route, state).pipe( - map((featureID) => [featureID]), - ); - } - /** - * The type of feature to check authorization for - * Override this method to define a feature - */ - abstract getFeatureID(route: ActivatedRouteSnapshot, state: RouterStateSnapshot): Observable; -} +export const singleFeatureAuthorizationGuard = ( + getFeatureID: SingleFeatureGuardParamFn, + getObjectUrl?: StringGuardParamFn, + getEPersonUuid?: StringGuardParamFn, +): CanActivateFn => someFeatureAuthorizationGuard( + (route: ActivatedRouteSnapshot, state: RouterStateSnapshot): Observable => getFeatureID(route, state).pipe( + map((featureID: FeatureID) => [featureID]), +), getObjectUrl, getEPersonUuid); diff --git a/src/app/core/data/feature-authorization/feature-authorization-guard/site-administrator.guard.ts b/src/app/core/data/feature-authorization/feature-authorization-guard/site-administrator.guard.ts index cc6f50c1613..bcef5b7c8fc 100644 --- a/src/app/core/data/feature-authorization/feature-authorization-guard/site-administrator.guard.ts +++ b/src/app/core/data/feature-authorization/feature-authorization-guard/site-administrator.guard.ts @@ -1,27 +1,11 @@ -import { Injectable } from '@angular/core'; -import { SingleFeatureAuthorizationGuard } from './single-feature-authorization.guard'; +import { CanActivateFn } from '@angular/router'; import { FeatureID } from '../feature-id'; -import { AuthorizationDataService } from '../authorization-data.service'; -import { ActivatedRouteSnapshot, Router, RouterStateSnapshot } from '@angular/router'; -import { Observable, of as observableOf } from 'rxjs'; -import { AuthService } from '../../../auth/auth.service'; +import { singleFeatureAuthorizationGuard } from './single-feature-authorization.guard'; +import { of as observableOf } from 'rxjs'; /** * Prevent unauthorized activating and loading of routes when the current authenticated user doesn't have administrator * rights to the {@link Site} */ -@Injectable({ - providedIn: 'root' -}) -export class SiteAdministratorGuard extends SingleFeatureAuthorizationGuard { - constructor(protected authorizationService: AuthorizationDataService, protected router: Router, protected authService: AuthService) { - super(authorizationService, router, authService); - } - - /** - * Check administrator authorization rights - */ - getFeatureID(route: ActivatedRouteSnapshot, state: RouterStateSnapshot): Observable { - return observableOf(FeatureID.AdministratorOf); - } -} +export const siteAdministratorGuard: CanActivateFn = + singleFeatureAuthorizationGuard(() => observableOf(FeatureID.AdministratorOf)); diff --git a/src/app/core/data/feature-authorization/feature-authorization-guard/site-register.guard.ts b/src/app/core/data/feature-authorization/feature-authorization-guard/site-register.guard.ts index bdbb8250e27..bf1945ac337 100644 --- a/src/app/core/data/feature-authorization/feature-authorization-guard/site-register.guard.ts +++ b/src/app/core/data/feature-authorization/feature-authorization-guard/site-register.guard.ts @@ -1,27 +1,11 @@ -import { SingleFeatureAuthorizationGuard } from './single-feature-authorization.guard'; -import { Injectable } from '@angular/core'; -import { AuthorizationDataService } from '../authorization-data.service'; -import { ActivatedRouteSnapshot, Router, RouterStateSnapshot } from '@angular/router'; -import { Observable, of as observableOf } from 'rxjs'; +import { CanActivateFn } from '@angular/router'; +import { singleFeatureAuthorizationGuard } from './single-feature-authorization.guard'; +import { of as observableOf } from 'rxjs'; import { FeatureID } from '../feature-id'; -import { AuthService } from '../../../auth/auth.service'; /** * Prevent unauthorized activating and loading of routes when the current authenticated user doesn't have registration * rights to the {@link Site} */ -@Injectable({ - providedIn: 'root' -}) -export class SiteRegisterGuard extends SingleFeatureAuthorizationGuard { - constructor(protected authorizationService: AuthorizationDataService, protected router: Router, protected authService: AuthService) { - super(authorizationService, router, authService); - } - - /** - * Check registration authorization rights - */ - getFeatureID(route: ActivatedRouteSnapshot, state: RouterStateSnapshot): Observable { - return observableOf(FeatureID.EPersonRegistration); - } -} +export const siteRegisterGuard: CanActivateFn = + singleFeatureAuthorizationGuard(() => observableOf(FeatureID.EPersonRegistration)); diff --git a/src/app/core/data/feature-authorization/feature-authorization-guard/some-feature-authorization.guard.spec.ts b/src/app/core/data/feature-authorization/feature-authorization-guard/some-feature-authorization.guard.spec.ts index 90153d2d148..a3578d3eda8 100644 --- a/src/app/core/data/feature-authorization/feature-authorization-guard/some-feature-authorization.guard.spec.ts +++ b/src/app/core/data/feature-authorization/feature-authorization-guard/some-feature-authorization.guard.spec.ts @@ -1,110 +1,110 @@ -import { AuthorizationDataService } from '../authorization-data.service'; -import { FeatureID } from '../feature-id'; -import { Observable, of as observableOf } from 'rxjs'; -import { ActivatedRouteSnapshot, Router, RouterStateSnapshot } from '@angular/router'; -import { AuthService } from '../../../auth/auth.service'; -import { SomeFeatureAuthorizationGuard } from './some-feature-authorization.guard'; - -/** - * Test implementation of abstract class SomeFeatureAuthorizationGuard - * Provide the return values of the overwritten getters as constructor arguments - */ -class SomeFeatureAuthorizationGuardImpl extends SomeFeatureAuthorizationGuard { - constructor(protected authorizationService: AuthorizationDataService, - protected router: Router, - protected authService: AuthService, - protected featureIds: FeatureID[], - protected objectUrl: string, - protected ePersonUuid: string) { - super(authorizationService, router, authService); - } - - getFeatureIDs(route: ActivatedRouteSnapshot, state: RouterStateSnapshot): Observable { - return observableOf(this.featureIds); - } - - getObjectUrl(route: ActivatedRouteSnapshot, state: RouterStateSnapshot): Observable { - return observableOf(this.objectUrl); - } - - getEPersonUuid(route: ActivatedRouteSnapshot, state: RouterStateSnapshot): Observable { - return observableOf(this.ePersonUuid); - } -} - -describe('SomeFeatureAuthorizationGuard', () => { - let guard: SomeFeatureAuthorizationGuard; - let authorizationService: AuthorizationDataService; - let router: Router; - let authService: AuthService; - - let featureIds: FeatureID[]; - let authorizedFeatureIds: FeatureID[]; - let objectUrl: string; - let ePersonUuid: string; - - function init() { - featureIds = [FeatureID.LoginOnBehalfOf, FeatureID.CanDelete]; - authorizedFeatureIds = []; - objectUrl = 'fake-object-url'; - ePersonUuid = 'fake-eperson-uuid'; - - authorizationService = Object.assign({ - isAuthorized(featureId?: FeatureID): Observable { - return observableOf(authorizedFeatureIds.indexOf(featureId) > -1); - } - }); - router = jasmine.createSpyObj('router', { - parseUrl: {} - }); - authService = jasmine.createSpyObj('authService', { - isAuthenticated: observableOf(true) - }); - guard = new SomeFeatureAuthorizationGuardImpl(authorizationService, router, authService, featureIds, objectUrl, ePersonUuid); - } - - beforeEach(() => { - init(); - }); - - describe('canActivate', () => { - describe('when the user isn\'t authorized', () => { - beforeEach(() => { - authorizedFeatureIds = []; - }); - - it('should not return true', (done) => { - guard.canActivate(undefined, { url: 'current-url' } as any).subscribe((result) => { - expect(result).not.toEqual(true); - done(); - }); - }); - }); - - describe('when the user is authorized for at least one of the guard\'s features', () => { - beforeEach(() => { - authorizedFeatureIds = [featureIds[0]]; - }); - - it('should return true', (done) => { - guard.canActivate(undefined, { url: 'current-url' } as any).subscribe((result) => { - expect(result).toEqual(true); - done(); - }); - }); - }); - - describe('when the user is authorized for all of the guard\'s features', () => { - beforeEach(() => { - authorizedFeatureIds = featureIds; - }); - - it('should return true', (done) => { - guard.canActivate(undefined, { url: 'current-url' } as any).subscribe((result) => { - expect(result).toEqual(true); - done(); - }); - }); - }); - }); -}); +// import { AuthorizationDataService } from '../authorization-data.service'; +// import { FeatureID } from '../feature-id'; +// import { Observable, of as observableOf } from 'rxjs'; +// import { ActivatedRouteSnapshot, Router, RouterStateSnapshot } from '@angular/router'; +// import { AuthService } from '../../../auth/auth.service'; +// import { SomeFeatureAuthorizationGuard } from './some-feature-authorization.guard'; +// +// /** +// * Test implementation of abstract class SomeFeatureAuthorizationGuard +// * Provide the return values of the overwritten getters as constructor arguments +// */ +// class SomeFeatureAuthorizationGuardImpl extends SomeFeatureAuthorizationGuard { +// constructor(protected authorizationService: AuthorizationDataService, +// protected router: Router, +// protected authService: AuthService, +// protected featureIds: FeatureID[], +// protected objectUrl: string, +// protected ePersonUuid: string) { +// super(authorizationService, router, authService); +// } +// +// getFeatureIDs(route: ActivatedRouteSnapshot, state: RouterStateSnapshot): Observable { +// return observableOf(this.featureIds); +// } +// +// getObjectUrl(route: ActivatedRouteSnapshot, state: RouterStateSnapshot): Observable { +// return observableOf(this.objectUrl); +// } +// +// getEPersonUuid(route: ActivatedRouteSnapshot, state: RouterStateSnapshot): Observable { +// return observableOf(this.ePersonUuid); +// } +// } +// +// describe('SomeFeatureAuthorizationGuard', () => { +// let guard: SomeFeatureAuthorizationGuard; +// let authorizationService: AuthorizationDataService; +// let router: Router; +// let authService: AuthService; +// +// let featureIds: FeatureID[]; +// let authorizedFeatureIds: FeatureID[]; +// let objectUrl: string; +// let ePersonUuid: string; +// +// function init() { +// featureIds = [FeatureID.LoginOnBehalfOf, FeatureID.CanDelete]; +// authorizedFeatureIds = []; +// objectUrl = 'fake-object-url'; +// ePersonUuid = 'fake-eperson-uuid'; +// +// authorizationService = Object.assign({ +// isAuthorized(featureId?: FeatureID): Observable { +// return observableOf(authorizedFeatureIds.indexOf(featureId) > -1); +// } +// }); +// router = jasmine.createSpyObj('router', { +// parseUrl: {} +// }); +// authService = jasmine.createSpyObj('authService', { +// isAuthenticated: observableOf(true) +// }); +// guard = new SomeFeatureAuthorizationGuardImpl(authorizationService, router, authService, featureIds, objectUrl, ePersonUuid); +// } +// +// beforeEach(() => { +// init(); +// }); +// +// describe('canActivate', () => { +// describe('when the user isn\'t authorized', () => { +// beforeEach(() => { +// authorizedFeatureIds = []; +// }); +// +// it('should not return true', (done) => { +// guard.canActivate(undefined, { url: 'current-url' } as any).subscribe((result) => { +// expect(result).not.toEqual(true); +// done(); +// }); +// }); +// }); +// +// describe('when the user is authorized for at least one of the guard\'s features', () => { +// beforeEach(() => { +// authorizedFeatureIds = [featureIds[0]]; +// }); +// +// it('should return true', (done) => { +// guard.canActivate(undefined, { url: 'current-url' } as any).subscribe((result) => { +// expect(result).toEqual(true); +// done(); +// }); +// }); +// }); +// +// describe('when the user is authorized for all of the guard\'s features', () => { +// beforeEach(() => { +// authorizedFeatureIds = featureIds; +// }); +// +// it('should return true', (done) => { +// guard.canActivate(undefined, { url: 'current-url' } as any).subscribe((result) => { +// expect(result).toEqual(true); +// done(); +// }); +// }); +// }); +// }); +// }); diff --git a/src/app/core/data/feature-authorization/feature-authorization-guard/some-feature-authorization.guard.ts b/src/app/core/data/feature-authorization/feature-authorization-guard/some-feature-authorization.guard.ts index b909640ea64..f5879612684 100644 --- a/src/app/core/data/feature-authorization/feature-authorization-guard/some-feature-authorization.guard.ts +++ b/src/app/core/data/feature-authorization/feature-authorization-guard/some-feature-authorization.guard.ts @@ -1,54 +1,45 @@ -import { ActivatedRouteSnapshot, CanActivate, Router, RouterStateSnapshot, UrlTree } from '@angular/router'; -import { AuthorizationDataService } from '../authorization-data.service'; +import { ActivatedRouteSnapshot, RouterStateSnapshot, CanActivateFn, Router, UrlTree } from '@angular/router'; +import { Observable, of as observableOf, combineLatest as observableCombineLatest } from 'rxjs'; import { FeatureID } from '../feature-id'; -import { combineLatest as observableCombineLatest, Observable, of as observableOf } from 'rxjs'; -import { switchMap } from 'rxjs/operators'; +import { AuthorizationDataService } from '../authorization-data.service'; import { AuthService } from '../../../auth/auth.service'; +import { inject } from '@angular/core'; +import { switchMap } from 'rxjs/operators'; import { returnForbiddenUrlTreeOrLoginOnAllFalse } from '../../../shared/authorized.operators'; +export declare type SomeFeatureGuardParamFn = (route: ActivatedRouteSnapshot, state: RouterStateSnapshot) => Observable; +export declare type StringGuardParamFn = (route: ActivatedRouteSnapshot, state: RouterStateSnapshot) => Observable; +export const defaultStringGuardParamFn = () => observableOf(undefined); + /** - * Abstract Guard for preventing unauthorized activating and loading of routes when a user - * doesn't have authorized rights on any of the specified features and/or object. - * Override the desired getters in the parent class for checking specific authorization on a list of features and/or object. - */ -export abstract class SomeFeatureAuthorizationGuard implements CanActivate { - constructor(protected authorizationService: AuthorizationDataService, - protected router: Router, - protected authService: AuthService) { - } + * Guard for preventing unauthorized activating and loading of routes when a user doesn't have + * authorized rights on any of the specified features and/or object. - /** - * True when user has authorization rights for the feature and object provided - * Redirect the user to the unauthorized page when they are not authorized for the given feature - */ - canActivate(route: ActivatedRouteSnapshot, state: RouterStateSnapshot): Observable { - return observableCombineLatest(this.getFeatureIDs(route, state), this.getObjectUrl(route, state), this.getEPersonUuid(route, state)).pipe( - switchMap(([featureIDs, objectUrl, ePersonUuid]) => - observableCombineLatest(...featureIDs.map((featureID) => this.authorizationService.isAuthorized(featureID, objectUrl, ePersonUuid))) + * @param getFeatureIDs The features to check authorization for + * @param getObjectUrl The URL of the object to check if the user has authorized rights for, + * Optional, if not provided, the {@link Site}'s URL will be assumed + * @param getEPersonUuid The UUID of the user to check authorization rights for. + * Optional, if not provided, the authenticated user's UUID will be assumed. + */ +export const someFeatureAuthorizationGuard = ( + getFeatureIDs: SomeFeatureGuardParamFn, + getObjectUrl: StringGuardParamFn = defaultStringGuardParamFn, + getEPersonUuid: StringGuardParamFn = defaultStringGuardParamFn, +): CanActivateFn => { + return (route: ActivatedRouteSnapshot, state: RouterStateSnapshot): Observable => { + const authorizationService = inject(AuthorizationDataService); + const router = inject(Router); + const authService = inject(AuthService); + return observableCombineLatest([ + getFeatureIDs(route, state), + getObjectUrl(route, state), + getEPersonUuid(route, state) + ]).pipe( + switchMap(([featureIDs, objectUrl, ePersonUuid]: [FeatureID[], string, string]) => + observableCombineLatest(featureIDs.map((featureID) => authorizationService.isAuthorized(featureID, objectUrl, ePersonUuid))), ), - returnForbiddenUrlTreeOrLoginOnAllFalse(this.router, this.authService, state.url) + returnForbiddenUrlTreeOrLoginOnAllFalse(router, authService, state.url), ); - } - - /** - * The features to check authorization for - * Override this method to define a list of features - */ - abstract getFeatureIDs(route: ActivatedRouteSnapshot, state: RouterStateSnapshot): Observable; - - /** - * The URL of the object to check if the user has authorized rights for - * Override this method to define an object URL. If not provided, the {@link Site}'s URL will be used - */ - getObjectUrl(route: ActivatedRouteSnapshot, state: RouterStateSnapshot): Observable { - return observableOf(undefined); - } + }; +}; - /** - * The UUID of the user to check authorization rights for - * Override this method to define an {@link EPerson} UUID. If not provided, the authenticated user's UUID will be used. - */ - getEPersonUuid(route: ActivatedRouteSnapshot, state: RouterStateSnapshot): Observable { - return observableOf(undefined); - } -} diff --git a/src/app/core/data/feature-authorization/feature-authorization-guard/statistics-administrator.guard.ts b/src/app/core/data/feature-authorization/feature-authorization-guard/statistics-administrator.guard.ts index 680495686eb..ff58dbb4bed 100644 --- a/src/app/core/data/feature-authorization/feature-authorization-guard/statistics-administrator.guard.ts +++ b/src/app/core/data/feature-authorization/feature-authorization-guard/statistics-administrator.guard.ts @@ -1,27 +1,11 @@ -import { Injectable } from '@angular/core'; -import { SingleFeatureAuthorizationGuard } from './single-feature-authorization.guard'; -import { AuthorizationDataService } from '../authorization-data.service'; -import { ActivatedRouteSnapshot, Router, RouterStateSnapshot } from '@angular/router'; -import { AuthService } from '../../../auth/auth.service'; -import { Observable, of as observableOf } from 'rxjs'; +import { CanActivateFn } from '@angular/router'; +import { singleFeatureAuthorizationGuard } from './single-feature-authorization.guard'; +import { of as observableOf } from 'rxjs'; import { FeatureID } from '../feature-id'; /** * Prevent unauthorized activating and loading of routes when the current authenticated user doesn't have group * management rights */ -@Injectable({ - providedIn: 'root' -}) -export class StatisticsAdministratorGuard extends SingleFeatureAuthorizationGuard { - constructor(protected authorizationService: AuthorizationDataService, protected router: Router, protected authService: AuthService) { - super(authorizationService, router, authService); - } - - /** - * Check group management rights - */ - getFeatureID(route: ActivatedRouteSnapshot, state: RouterStateSnapshot): Observable { - return observableOf(FeatureID.CanViewUsageStatistics); - } -} +export const statisticsAdministratorGuard: CanActivateFn = + singleFeatureAuthorizationGuard(() => observableOf(FeatureID.CanViewUsageStatistics)); diff --git a/src/app/health-page/health-page.routing.module.ts b/src/app/health-page/health-page.routing.module.ts index 82d541dc312..59303d1bbc6 100644 --- a/src/app/health-page/health-page.routing.module.ts +++ b/src/app/health-page/health-page.routing.module.ts @@ -4,7 +4,7 @@ import { NgModule } from '@angular/core'; import { I18nBreadcrumbResolver } from '../core/breadcrumbs/i18n-breadcrumb.resolver'; import { HealthPageComponent } from './health-page.component'; import { - SiteAdministratorGuard + siteAdministratorGuard } from '../core/data/feature-authorization/feature-authorization-guard/site-administrator.guard'; @NgModule({ @@ -17,7 +17,7 @@ import { breadcrumbKey: 'health', title: 'health-page.title', }, - canActivate: [SiteAdministratorGuard], + canActivate: [siteAdministratorGuard], component: HealthPageComponent } ]) diff --git a/src/app/item-page/edit-item-page/edit-item-page.routing.module.ts b/src/app/item-page/edit-item-page/edit-item-page.routing.module.ts index f22027916fe..6713c176633 100644 --- a/src/app/item-page/edit-item-page/edit-item-page.routing.module.ts +++ b/src/app/item-page/edit-item-page/edit-item-page.routing.module.ts @@ -29,20 +29,19 @@ import { ITEM_EDIT_WITHDRAW_PATH, ITEM_EDIT_REGISTER_DOI_PATH } from './edit-item-page.routing-paths'; -import { ItemPageReinstateGuard } from './item-page-reinstate.guard'; -import { ItemPageWithdrawGuard } from './item-page-withdraw.guard'; -import { ItemPageMetadataGuard } from './item-page-metadata.guard'; -import { ItemPageAdministratorGuard } from '../item-page-administrator.guard'; -import { ItemPageStatusGuard } from './item-page-status.guard'; -import { ItemPageBitstreamsGuard } from './item-page-bitstreams.guard'; -import { ItemPageRelationshipsGuard } from './item-page-relationships.guard'; -import { ItemPageVersionHistoryGuard } from './item-page-version-history.guard'; -import { ItemPageCollectionMapperGuard } from './item-page-collection-mapper.guard'; import { ThemedDsoEditMetadataComponent } from '../../dso-shared/dso-edit-metadata/themed-dso-edit-metadata.component'; -import { ItemPageRegisterDoiGuard } from './item-page-register-doi.guard'; import { ItemCurateComponent } from './item-curate/item-curate.component'; import { ThemedItemStatusComponent } from './item-status/themed-item-status.component'; import { ItemAccessControlComponent } from './item-access-control/item-access-control.component'; +import { itemPageStatusGuard } from './item-page-status.guard'; +import { itemPageBitstreamsGuard } from './item-page-bitstreams.guard'; +import { itemPageMetadataGuard } from './item-page-metadata.guard'; +import { itemPageRelationshipsGuard } from './item-page-relationships.guard'; +import { itemPageCollectionMapperGuard } from './item-page-collection-mapper.guard'; +import { itemPageWithdrawGuard } from './item-page-withdraw.guard'; +import { itemPageReinstateGuard } from './item-page-reinstate.guard'; +import { itemPageRegisterDoiGuard } from './item-page-register-doi.guard'; +import { itemPageVersionHistoryGuard } from './item-page-version-history.guard'; /** * Routing module that handles the routing for the Edit Item page administrator functionality @@ -70,19 +69,19 @@ import { ItemAccessControlComponent } from './item-access-control/item-access-co path: 'status', component: ThemedItemStatusComponent, data: { title: 'item.edit.tabs.status.title', showBreadcrumbs: true }, - canActivate: [ItemPageStatusGuard] + canActivate: [itemPageStatusGuard] }, { path: 'bitstreams', component: ItemBitstreamsComponent, data: { title: 'item.edit.tabs.bitstreams.title', showBreadcrumbs: true }, - canActivate: [ItemPageBitstreamsGuard] + canActivate: [itemPageBitstreamsGuard] }, { path: 'metadata', component: ThemedDsoEditMetadataComponent, data: { title: 'item.edit.tabs.metadata.title', showBreadcrumbs: true }, - canActivate: [ItemPageMetadataGuard] + canActivate: [itemPageMetadataGuard] }, { path: 'curate', @@ -93,7 +92,7 @@ import { ItemAccessControlComponent } from './item-access-control/item-access-co path: 'relationships', component: ItemRelationshipsComponent, data: { title: 'item.edit.tabs.relationships.title', showBreadcrumbs: true }, - canActivate: [ItemPageRelationshipsGuard] + canActivate: [itemPageRelationshipsGuard] }, /* TODO - uncomment & fix when view page exists { @@ -111,7 +110,7 @@ import { ItemAccessControlComponent } from './item-access-control/item-access-co path: 'versionhistory', component: ItemVersionHistoryComponent, data: { title: 'item.edit.tabs.versionhistory.title', showBreadcrumbs: true }, - canActivate: [ItemPageVersionHistoryGuard] + canActivate: [itemPageVersionHistoryGuard] }, { path: 'access-control', @@ -122,7 +121,7 @@ import { ItemAccessControlComponent } from './item-access-control/item-access-co path: 'mapper', component: ItemCollectionMapperComponent, data: { title: 'item.edit.tabs.item-mapper.title', showBreadcrumbs: true }, - canActivate: [ItemPageCollectionMapperGuard] + canActivate: [itemPageCollectionMapperGuard] } ] }, @@ -133,12 +132,12 @@ import { ItemAccessControlComponent } from './item-access-control/item-access-co { path: ITEM_EDIT_WITHDRAW_PATH, component: ItemWithdrawComponent, - canActivate: [ItemPageWithdrawGuard] + canActivate: [itemPageWithdrawGuard] }, { path: ITEM_EDIT_REINSTATE_PATH, component: ItemReinstateComponent, - canActivate: [ItemPageReinstateGuard] + canActivate: [itemPageReinstateGuard] }, { path: ITEM_EDIT_PRIVATE_PATH, @@ -160,7 +159,7 @@ import { ItemAccessControlComponent } from './item-access-control/item-access-co { path: ITEM_EDIT_REGISTER_DOI_PATH, component: ItemRegisterDoiComponent, - canActivate: [ItemPageRegisterDoiGuard], + canActivate: [itemPageRegisterDoiGuard], data: { title: 'item.edit.register-doi.title' }, }, { @@ -198,16 +197,6 @@ import { ItemAccessControlComponent } from './item-access-control/item-access-co I18nBreadcrumbsService, ResourcePolicyResolver, ResourcePolicyTargetResolver, - ItemPageReinstateGuard, - ItemPageWithdrawGuard, - ItemPageAdministratorGuard, - ItemPageMetadataGuard, - ItemPageStatusGuard, - ItemPageBitstreamsGuard, - ItemPageRelationshipsGuard, - ItemPageVersionHistoryGuard, - ItemPageCollectionMapperGuard, - ItemPageRegisterDoiGuard, ] }) export class EditItemPageRoutingModule { diff --git a/src/app/item-page/edit-item-page/item-page-bitstreams.guard.ts b/src/app/item-page/edit-item-page/item-page-bitstreams.guard.ts index 764c6ac7c87..9b72e9c6e32 100644 --- a/src/app/item-page/edit-item-page/item-page-bitstreams.guard.ts +++ b/src/app/item-page/edit-item-page/item-page-bitstreams.guard.ts @@ -1,31 +1,23 @@ -import { Injectable } from '@angular/core'; -import { DsoPageSingleFeatureGuard } from '../../core/data/feature-authorization/feature-authorization-guard/dso-page-single-feature.guard'; +import { inject } from '@angular/core'; +import { + dsoPageSingleFeatureGuard +} from '../../core/data/feature-authorization/feature-authorization-guard/dso-page-single-feature.guard'; import { Item } from '../../core/shared/item.model'; import { ItemPageResolver } from '../item-page.resolver'; -import { AuthorizationDataService } from '../../core/data/feature-authorization/authorization-data.service'; -import { ActivatedRouteSnapshot, Router, RouterStateSnapshot } from '@angular/router'; +import { CanActivateFn, ResolveFn } from '@angular/router'; import { Observable, of as observableOf } from 'rxjs'; import { FeatureID } from '../../core/data/feature-authorization/feature-id'; -import { AuthService } from '../../core/auth/auth.service'; +import { RemoteData } from '../../core/data/remote-data'; -@Injectable({ - providedIn: 'root' -}) /** * Guard for preventing unauthorized access to certain {@link Item} pages requiring manage bitstreams rights + * Check manage bitstreams authorization rights */ -export class ItemPageBitstreamsGuard extends DsoPageSingleFeatureGuard { - constructor(protected resolver: ItemPageResolver, - protected authorizationService: AuthorizationDataService, - protected router: Router, - protected authService: AuthService) { - super(resolver, authorizationService, router, authService); - } - - /** - * Check manage bitstreams authorization rights - */ - getFeatureID(next: ActivatedRouteSnapshot, state: RouterStateSnapshot): Observable { - return observableOf(FeatureID.CanManageBitstreamBundles); - } -} +export const itemPageBitstreamsGuard: CanActivateFn = + dsoPageSingleFeatureGuard( + () => { + const itemPageResolver = inject(ItemPageResolver); + return itemPageResolver.resolve as ResolveFn>>; + }, + () => observableOf(FeatureID.CanManageBitstreamBundles) + ); diff --git a/src/app/item-page/edit-item-page/item-page-collection-mapper.guard.ts b/src/app/item-page/edit-item-page/item-page-collection-mapper.guard.ts index 2380377aea0..98045173f24 100644 --- a/src/app/item-page/edit-item-page/item-page-collection-mapper.guard.ts +++ b/src/app/item-page/edit-item-page/item-page-collection-mapper.guard.ts @@ -1,31 +1,23 @@ -import { Injectable } from '@angular/core'; -import { ActivatedRouteSnapshot, Router, RouterStateSnapshot } from '@angular/router'; -import { AuthorizationDataService } from '../../core/data/feature-authorization/authorization-data.service'; -import { ItemPageResolver } from '../item-page.resolver'; +import { inject } from '@angular/core'; +import { + dsoPageSingleFeatureGuard +} from '../../core/data/feature-authorization/feature-authorization-guard/dso-page-single-feature.guard'; import { Item } from '../../core/shared/item.model'; -import { DsoPageSingleFeatureGuard } from '../../core/data/feature-authorization/feature-authorization-guard/dso-page-single-feature.guard'; +import { ItemPageResolver } from '../item-page.resolver'; +import { CanActivateFn, ResolveFn } from '@angular/router'; import { Observable, of as observableOf } from 'rxjs'; import { FeatureID } from '../../core/data/feature-authorization/feature-id'; -import { AuthService } from '../../core/auth/auth.service'; +import { RemoteData } from '../../core/data/remote-data'; -@Injectable({ - providedIn: 'root' -}) /** * Guard for preventing unauthorized access to certain {@link Item} pages requiring manage mappings rights + * Check manage mappings authorization rights */ -export class ItemPageCollectionMapperGuard extends DsoPageSingleFeatureGuard { - constructor(protected resolver: ItemPageResolver, - protected authorizationService: AuthorizationDataService, - protected router: Router, - protected authService: AuthService) { - super(resolver, authorizationService, router, authService); - } - - /** - * Check manage mappings authorization rights - */ - getFeatureID(next: ActivatedRouteSnapshot, state: RouterStateSnapshot): Observable { - return observableOf(FeatureID.CanManageMappings); - } -} +export const itemPageCollectionMapperGuard: CanActivateFn = + dsoPageSingleFeatureGuard( + () => { + const itemPageResolver = inject(ItemPageResolver); + return itemPageResolver.resolve as ResolveFn>>; + }, + () => observableOf(FeatureID.CanManageMappings) + ); diff --git a/src/app/item-page/edit-item-page/item-page-metadata.guard.ts b/src/app/item-page/edit-item-page/item-page-metadata.guard.ts index a6846bec4e6..00f64233be0 100644 --- a/src/app/item-page/edit-item-page/item-page-metadata.guard.ts +++ b/src/app/item-page/edit-item-page/item-page-metadata.guard.ts @@ -1,31 +1,23 @@ -import { Injectable } from '@angular/core'; -import { ActivatedRouteSnapshot, Router, RouterStateSnapshot } from '@angular/router'; -import { AuthorizationDataService } from '../../core/data/feature-authorization/authorization-data.service'; -import { ItemPageResolver } from '../item-page.resolver'; +import { inject } from '@angular/core'; +import { + dsoPageSingleFeatureGuard +} from '../../core/data/feature-authorization/feature-authorization-guard/dso-page-single-feature.guard'; import { Item } from '../../core/shared/item.model'; -import { DsoPageSingleFeatureGuard } from '../../core/data/feature-authorization/feature-authorization-guard/dso-page-single-feature.guard'; +import { ItemPageResolver } from '../item-page.resolver'; +import { CanActivateFn, ResolveFn } from '@angular/router'; import { Observable, of as observableOf } from 'rxjs'; import { FeatureID } from '../../core/data/feature-authorization/feature-id'; -import { AuthService } from '../../core/auth/auth.service'; +import { RemoteData } from '../../core/data/remote-data'; -@Injectable({ - providedIn: 'root' -}) /** * Guard for preventing unauthorized access to certain {@link Item} pages requiring edit metadata rights + * Check edit metadata authorization rights */ -export class ItemPageMetadataGuard extends DsoPageSingleFeatureGuard { - constructor(protected resolver: ItemPageResolver, - protected authorizationService: AuthorizationDataService, - protected router: Router, - protected authService: AuthService) { - super(resolver, authorizationService, router, authService); - } - - /** - * Check edit metadata authorization rights - */ - getFeatureID(next: ActivatedRouteSnapshot, state: RouterStateSnapshot): Observable { - return observableOf(FeatureID.CanEditMetadata); - } -} +export const itemPageMetadataGuard: CanActivateFn = + dsoPageSingleFeatureGuard( + () => { + const itemPageResolver = inject(ItemPageResolver); + return itemPageResolver.resolve as ResolveFn>>; + }, + () => observableOf(FeatureID.CanEditMetadata) + ); diff --git a/src/app/item-page/edit-item-page/item-page-register-doi.guard.ts b/src/app/item-page/edit-item-page/item-page-register-doi.guard.ts index 6a20d4d3486..0758eede08a 100644 --- a/src/app/item-page/edit-item-page/item-page-register-doi.guard.ts +++ b/src/app/item-page/edit-item-page/item-page-register-doi.guard.ts @@ -1,31 +1,23 @@ -import { Injectable } from '@angular/core'; -import { DsoPageSingleFeatureGuard } from '../../core/data/feature-authorization/feature-authorization-guard/dso-page-single-feature.guard'; +import { inject } from '@angular/core'; +import { + dsoPageSingleFeatureGuard +} from '../../core/data/feature-authorization/feature-authorization-guard/dso-page-single-feature.guard'; import { Item } from '../../core/shared/item.model'; import { ItemPageResolver } from '../item-page.resolver'; -import { AuthorizationDataService } from '../../core/data/feature-authorization/authorization-data.service'; -import { ActivatedRouteSnapshot, Router, RouterStateSnapshot } from '@angular/router'; +import { CanActivateFn, ResolveFn } from '@angular/router'; import { Observable, of as observableOf } from 'rxjs'; import { FeatureID } from '../../core/data/feature-authorization/feature-id'; -import { AuthService } from '../../core/auth/auth.service'; +import { RemoteData } from '../../core/data/remote-data'; -@Injectable({ - providedIn: 'root' -}) /** * Guard for preventing unauthorized access to certain {@link Item} pages requiring DOI registration rights + * Check DOI registration authorization rights */ -export class ItemPageRegisterDoiGuard extends DsoPageSingleFeatureGuard { - constructor(protected resolver: ItemPageResolver, - protected authorizationService: AuthorizationDataService, - protected router: Router, - protected authService: AuthService) { - super(resolver, authorizationService, router, authService); - } - - /** - * Check DOI registration authorization rights - */ - getFeatureID(next: ActivatedRouteSnapshot, state: RouterStateSnapshot): Observable { - return observableOf(FeatureID.CanRegisterDOI); - } -} +export const itemPageRegisterDoiGuard: CanActivateFn = + dsoPageSingleFeatureGuard( + () => { + const itemPageResolver = inject(ItemPageResolver); + return itemPageResolver.resolve as ResolveFn>>; + }, + () => observableOf(FeatureID.CanRegisterDOI) + ); diff --git a/src/app/item-page/edit-item-page/item-page-reinstate.guard.ts b/src/app/item-page/edit-item-page/item-page-reinstate.guard.ts index 88c9c20b126..e0d19a3cef4 100644 --- a/src/app/item-page/edit-item-page/item-page-reinstate.guard.ts +++ b/src/app/item-page/edit-item-page/item-page-reinstate.guard.ts @@ -1,31 +1,23 @@ -import { Injectable } from '@angular/core'; -import { DsoPageSingleFeatureGuard } from '../../core/data/feature-authorization/feature-authorization-guard/dso-page-single-feature.guard'; +import { inject } from '@angular/core'; +import { + dsoPageSingleFeatureGuard +} from '../../core/data/feature-authorization/feature-authorization-guard/dso-page-single-feature.guard'; import { Item } from '../../core/shared/item.model'; import { ItemPageResolver } from '../item-page.resolver'; -import { AuthorizationDataService } from '../../core/data/feature-authorization/authorization-data.service'; -import { ActivatedRouteSnapshot, Router, RouterStateSnapshot } from '@angular/router'; +import { CanActivateFn, ResolveFn } from '@angular/router'; import { Observable, of as observableOf } from 'rxjs'; import { FeatureID } from '../../core/data/feature-authorization/feature-id'; -import { AuthService } from '../../core/auth/auth.service'; +import { RemoteData } from '../../core/data/remote-data'; -@Injectable({ - providedIn: 'root' -}) /** * Guard for preventing unauthorized access to certain {@link Item} pages requiring reinstate rights + * Check reinstate authorization rights */ -export class ItemPageReinstateGuard extends DsoPageSingleFeatureGuard { - constructor(protected resolver: ItemPageResolver, - protected authorizationService: AuthorizationDataService, - protected router: Router, - protected authService: AuthService) { - super(resolver, authorizationService, router, authService); - } - - /** - * Check reinstate authorization rights - */ - getFeatureID(next: ActivatedRouteSnapshot, state: RouterStateSnapshot): Observable { - return observableOf(FeatureID.ReinstateItem); - } -} +export const itemPageReinstateGuard: CanActivateFn = + dsoPageSingleFeatureGuard( + () => { + const itemPageResolver = inject(ItemPageResolver); + return itemPageResolver.resolve as ResolveFn>>; + }, + () => observableOf(FeatureID.ReinstateItem) + ); diff --git a/src/app/item-page/edit-item-page/item-page-relationships.guard.ts b/src/app/item-page/edit-item-page/item-page-relationships.guard.ts index 77da92ae027..dfb5bc4b7b2 100644 --- a/src/app/item-page/edit-item-page/item-page-relationships.guard.ts +++ b/src/app/item-page/edit-item-page/item-page-relationships.guard.ts @@ -1,31 +1,23 @@ -import { Injectable } from '@angular/core'; -import { ActivatedRouteSnapshot, Router, RouterStateSnapshot } from '@angular/router'; -import { AuthorizationDataService } from '../../core/data/feature-authorization/authorization-data.service'; -import { ItemPageResolver } from '../item-page.resolver'; +import { inject } from '@angular/core'; +import { + dsoPageSingleFeatureGuard +} from '../../core/data/feature-authorization/feature-authorization-guard/dso-page-single-feature.guard'; import { Item } from '../../core/shared/item.model'; -import { DsoPageSingleFeatureGuard } from '../../core/data/feature-authorization/feature-authorization-guard/dso-page-single-feature.guard'; +import { ItemPageResolver } from '../item-page.resolver'; +import { CanActivateFn, ResolveFn } from '@angular/router'; import { Observable, of as observableOf } from 'rxjs'; import { FeatureID } from '../../core/data/feature-authorization/feature-id'; -import { AuthService } from '../../core/auth/auth.service'; +import { RemoteData } from '../../core/data/remote-data'; -@Injectable({ - providedIn: 'root' -}) /** * Guard for preventing unauthorized access to certain {@link Item} pages requiring manage relationships rights + * Check manage relationships authorization rights */ -export class ItemPageRelationshipsGuard extends DsoPageSingleFeatureGuard { - constructor(protected resolver: ItemPageResolver, - protected authorizationService: AuthorizationDataService, - protected router: Router, - protected authService: AuthService) { - super(resolver, authorizationService, router, authService); - } - - /** - * Check manage relationships authorization rights - */ - getFeatureID(next: ActivatedRouteSnapshot, state: RouterStateSnapshot): Observable { - return observableOf(FeatureID.CanManageRelationships); - } -} +export const itemPageRelationshipsGuard: CanActivateFn = + dsoPageSingleFeatureGuard( + () => { + const itemPageResolver = inject(ItemPageResolver); + return itemPageResolver.resolve as ResolveFn>>; + }, + () => observableOf(FeatureID.CanManageRelationships) + ); diff --git a/src/app/item-page/edit-item-page/item-page-status.guard.ts b/src/app/item-page/edit-item-page/item-page-status.guard.ts index ade565da69b..c2be1a72e7e 100644 --- a/src/app/item-page/edit-item-page/item-page-status.guard.ts +++ b/src/app/item-page/edit-item-page/item-page-status.guard.ts @@ -1,32 +1,24 @@ -import { Injectable } from '@angular/core'; -import { Item } from '../../core/shared/item.model'; +import { + dsoPageSomeFeatureGuard +} from '../../core/data/feature-authorization/feature-authorization-guard/dso-page-some-feature.guard'; +import { CanActivateFn, ResolveFn } from '@angular/router'; +import { inject } from '@angular/core'; import { ItemPageResolver } from '../item-page.resolver'; -import { AuthorizationDataService } from '../../core/data/feature-authorization/authorization-data.service'; -import { ActivatedRouteSnapshot, Router, RouterStateSnapshot } from '@angular/router'; import { Observable, of as observableOf } from 'rxjs'; +import { RemoteData } from '../../core/data/remote-data'; +import { Item } from '../../core/shared/item.model'; import { FeatureID } from '../../core/data/feature-authorization/feature-id'; -import { AuthService } from '../../core/auth/auth.service'; -import { DsoPageSomeFeatureGuard } from '../../core/data/feature-authorization/feature-authorization-guard/dso-page-some-feature.guard'; -@Injectable({ - providedIn: 'root' -}) /** * Guard for preventing unauthorized access to certain {@link Item} pages requiring any of the rights required for * the status page + * Check authorization rights */ -export class ItemPageStatusGuard extends DsoPageSomeFeatureGuard { - constructor(protected resolver: ItemPageResolver, - protected authorizationService: AuthorizationDataService, - protected router: Router, - protected authService: AuthService) { - super(resolver, authorizationService, router, authService); - } - - /** - * Check authorization rights - */ - getFeatureIDs(next: ActivatedRouteSnapshot, state: RouterStateSnapshot): Observable { - return observableOf([FeatureID.CanManageMappings, FeatureID.WithdrawItem, FeatureID.ReinstateItem, FeatureID.CanManagePolicies, FeatureID.CanMakePrivate, FeatureID.CanDelete, FeatureID.CanMove, FeatureID.CanRegisterDOI]); - } -} +export const itemPageStatusGuard: CanActivateFn = + dsoPageSomeFeatureGuard( + () => { + const itemPageResolver = inject(ItemPageResolver); + return itemPageResolver.resolve as ResolveFn>>; + }, + () => observableOf([FeatureID.CanManageMappings, FeatureID.WithdrawItem, FeatureID.ReinstateItem, FeatureID.CanManagePolicies, FeatureID.CanMakePrivate, FeatureID.CanDelete, FeatureID.CanMove, FeatureID.CanRegisterDOI]) + ); diff --git a/src/app/item-page/edit-item-page/item-page-version-history.guard.ts b/src/app/item-page/edit-item-page/item-page-version-history.guard.ts index dccdd9e641a..ecd19072d03 100644 --- a/src/app/item-page/edit-item-page/item-page-version-history.guard.ts +++ b/src/app/item-page/edit-item-page/item-page-version-history.guard.ts @@ -1,31 +1,23 @@ -import { Injectable } from '@angular/core'; -import { ActivatedRouteSnapshot, Router, RouterStateSnapshot } from '@angular/router'; -import { AuthorizationDataService } from '../../core/data/feature-authorization/authorization-data.service'; -import { ItemPageResolver } from '../item-page.resolver'; +import { inject } from '@angular/core'; +import { + dsoPageSingleFeatureGuard +} from '../../core/data/feature-authorization/feature-authorization-guard/dso-page-single-feature.guard'; import { Item } from '../../core/shared/item.model'; -import { DsoPageSingleFeatureGuard } from '../../core/data/feature-authorization/feature-authorization-guard/dso-page-single-feature.guard'; +import { ItemPageResolver } from '../item-page.resolver'; +import { CanActivateFn, ResolveFn } from '@angular/router'; import { Observable, of as observableOf } from 'rxjs'; import { FeatureID } from '../../core/data/feature-authorization/feature-id'; -import { AuthService } from '../../core/auth/auth.service'; +import { RemoteData } from '../../core/data/remote-data'; -@Injectable({ - providedIn: 'root' -}) /** * Guard for preventing unauthorized access to certain {@link Item} pages requiring manage versions rights + * Check manage versions authorization rights */ -export class ItemPageVersionHistoryGuard extends DsoPageSingleFeatureGuard { - constructor(protected resolver: ItemPageResolver, - protected authorizationService: AuthorizationDataService, - protected router: Router, - protected authService: AuthService) { - super(resolver, authorizationService, router, authService); - } - - /** - * Check manage versions authorization rights - */ - getFeatureID(next: ActivatedRouteSnapshot, state: RouterStateSnapshot): Observable { - return observableOf(FeatureID.CanManageVersions); - } -} +export const itemPageVersionHistoryGuard: CanActivateFn = + dsoPageSingleFeatureGuard( + () => { + const itemPageResolver = inject(ItemPageResolver); + return itemPageResolver.resolve as ResolveFn>>; + }, + () => observableOf(FeatureID.CanManageVersions) + ); diff --git a/src/app/item-page/edit-item-page/item-page-withdraw.guard.ts b/src/app/item-page/edit-item-page/item-page-withdraw.guard.ts index de9b7d01478..38148973a74 100644 --- a/src/app/item-page/edit-item-page/item-page-withdraw.guard.ts +++ b/src/app/item-page/edit-item-page/item-page-withdraw.guard.ts @@ -1,31 +1,23 @@ -import { DsoPageSingleFeatureGuard } from '../../core/data/feature-authorization/feature-authorization-guard/dso-page-single-feature.guard'; +import { inject } from '@angular/core'; +import { + dsoPageSingleFeatureGuard +} from '../../core/data/feature-authorization/feature-authorization-guard/dso-page-single-feature.guard'; import { Item } from '../../core/shared/item.model'; -import { Injectable } from '@angular/core'; import { ItemPageResolver } from '../item-page.resolver'; -import { AuthorizationDataService } from '../../core/data/feature-authorization/authorization-data.service'; -import { ActivatedRouteSnapshot, Router, RouterStateSnapshot } from '@angular/router'; +import { CanActivateFn, ResolveFn } from '@angular/router'; import { Observable, of as observableOf } from 'rxjs'; import { FeatureID } from '../../core/data/feature-authorization/feature-id'; -import { AuthService } from '../../core/auth/auth.service'; +import { RemoteData } from '../../core/data/remote-data'; -@Injectable({ - providedIn: 'root' -}) /** * Guard for preventing unauthorized access to certain {@link Item} pages requiring withdraw rights + * Check withdraw authorization rights */ -export class ItemPageWithdrawGuard extends DsoPageSingleFeatureGuard { - constructor(protected resolver: ItemPageResolver, - protected authorizationService: AuthorizationDataService, - protected router: Router, - protected authService: AuthService) { - super(resolver, authorizationService, router, authService); - } - - /** - * Check withdraw authorization rights - */ - getFeatureID(next: ActivatedRouteSnapshot, state: RouterStateSnapshot): Observable { - return observableOf(FeatureID.WithdrawItem); - } -} +export const itemPageWithdrawGuard: CanActivateFn = + dsoPageSingleFeatureGuard( + () => { + const itemPageResolver = inject(ItemPageResolver); + return itemPageResolver.resolve as ResolveFn>>; + }, + () => observableOf(FeatureID.WithdrawItem) + ); diff --git a/src/app/item-page/item-page-administrator.guard.ts b/src/app/item-page/item-page-administrator.guard.ts index 5d3464aa754..359e7f1849d 100644 --- a/src/app/item-page/item-page-administrator.guard.ts +++ b/src/app/item-page/item-page-administrator.guard.ts @@ -1,31 +1,22 @@ -import { Injectable } from '@angular/core'; -import { ActivatedRouteSnapshot, Router, RouterStateSnapshot } from '@angular/router'; -import { AuthorizationDataService } from '../core/data/feature-authorization/authorization-data.service'; -import { ItemPageResolver } from './item-page.resolver'; -import { Item } from '../core/shared/item.model'; -import { DsoPageSingleFeatureGuard } from '../core/data/feature-authorization/feature-authorization-guard/dso-page-single-feature.guard'; +import { inject } from '@angular/core'; +import { CanActivateFn, ResolveFn } from '@angular/router'; import { Observable, of as observableOf } from 'rxjs'; +import { ItemPageResolver } from './item-page.resolver'; +import { + dsoPageSingleFeatureGuard +} from '../core/data/feature-authorization/feature-authorization-guard/dso-page-single-feature.guard'; import { FeatureID } from '../core/data/feature-authorization/feature-id'; -import { AuthService } from '../core/auth/auth.service'; +import { RemoteData } from '../core/data/remote-data'; +import { Item } from '../core/shared/item.model'; -@Injectable({ - providedIn: 'root' -}) /** * Guard for preventing unauthorized access to certain {@link Item} pages requiring administrator rights */ -export class ItemPageAdministratorGuard extends DsoPageSingleFeatureGuard { - constructor(protected resolver: ItemPageResolver, - protected authorizationService: AuthorizationDataService, - protected router: Router, - protected authService: AuthService) { - super(resolver, authorizationService, router, authService); - } - - /** - * Check administrator authorization rights - */ - getFeatureID(next: ActivatedRouteSnapshot, state: RouterStateSnapshot): Observable { - return observableOf(FeatureID.AdministratorOf); - } -} +export const itemPageAdministratorGuard: CanActivateFn = + dsoPageSingleFeatureGuard( + () => { + const itemPageResolver = inject(ItemPageResolver); + return itemPageResolver.resolve as ResolveFn>>; + }, + () => observableOf(FeatureID.AdministratorOf) + ); diff --git a/src/app/item-page/item-page-routing.module.ts b/src/app/item-page/item-page-routing.module.ts index 0c855ab34dd..86b117e7b13 100644 --- a/src/app/item-page/item-page-routing.module.ts +++ b/src/app/item-page/item-page-routing.module.ts @@ -8,7 +8,6 @@ import { DSOBreadcrumbsService } from '../core/breadcrumbs/dso-breadcrumbs.servi import { LinkService } from '../core/cache/builders/link.service'; import { UploadBitstreamComponent } from './bitstreams/upload/upload-bitstream.component'; import { ITEM_EDIT_PATH, ORCID_PATH, UPLOAD_BITSTREAM_PATH } from './item-page-routing-paths'; -import { ItemPageAdministratorGuard } from './item-page-administrator.guard'; import { LinkMenuItemModel } from '../shared/menu/menu-item/models/link.model'; import { ThemedItemPageComponent } from './simple/themed-item-page.component'; import { ThemedFullItemPageComponent } from './full/themed-full-item-page.component'; @@ -17,8 +16,8 @@ import { VersionPageComponent } from './version-page/version-page/version-page.c import { BitstreamRequestACopyPageComponent } from './bitstreams/request-a-copy/bitstream-request-a-copy-page.component'; import { REQUEST_COPY_MODULE_PATH } from '../app-routing-paths'; import { OrcidPageComponent } from './orcid-page/orcid-page.component'; -import { OrcidPageGuard } from './orcid-page/orcid-page.guard'; import { DSOEditMenuResolver } from '../shared/dso-page/dso-edit-menu.resolver'; +import { orcidPageGuard } from './orcid-page/orcid-page.guard'; @NgModule({ imports: [ @@ -58,7 +57,7 @@ import { DSOEditMenuResolver } from '../shared/dso-page/dso-edit-menu.resolver'; { path: ORCID_PATH, component: OrcidPageComponent, - canActivate: [AuthenticatedGuard, OrcidPageGuard] + canActivate: [AuthenticatedGuard, orcidPageGuard] } ], data: { @@ -96,9 +95,7 @@ import { DSOEditMenuResolver } from '../shared/dso-page/dso-edit-menu.resolver'; ItemBreadcrumbResolver, DSOBreadcrumbsService, LinkService, - ItemPageAdministratorGuard, VersionResolver, - OrcidPageGuard ] }) diff --git a/src/app/item-page/orcid-page/orcid-page.guard.ts b/src/app/item-page/orcid-page/orcid-page.guard.ts index 97c528e9ae2..947be3ee9fa 100644 --- a/src/app/item-page/orcid-page/orcid-page.guard.ts +++ b/src/app/item-page/orcid-page/orcid-page.guard.ts @@ -1,31 +1,23 @@ -import { Injectable } from '@angular/core'; -import { ActivatedRouteSnapshot, Router, RouterStateSnapshot } from '@angular/router'; -import { Observable, of as observableOf } from 'rxjs'; -import { AuthService } from '../../core/auth/auth.service'; -import { AuthorizationDataService } from '../../core/data/feature-authorization/authorization-data.service'; -import { DsoPageSingleFeatureGuard } from '../../core/data/feature-authorization/feature-authorization-guard/dso-page-single-feature.guard'; -import { FeatureID } from '../../core/data/feature-authorization/feature-id'; +import { inject } from '@angular/core'; +import { + dsoPageSingleFeatureGuard +} from '../../core/data/feature-authorization/feature-authorization-guard/dso-page-single-feature.guard'; import { Item } from '../../core/shared/item.model'; import { ItemPageResolver } from '../item-page.resolver'; +import { CanActivateFn, ResolveFn } from '@angular/router'; +import { Observable, of as observableOf } from 'rxjs'; +import { FeatureID } from '../../core/data/feature-authorization/feature-id'; +import { RemoteData } from '../../core/data/remote-data'; -@Injectable({ - providedIn: 'root' -}) /** * Guard for preventing unauthorized access to certain {@link Item} pages requiring administrator rights + * Check administrator authorization rights */ -export class OrcidPageGuard extends DsoPageSingleFeatureGuard { - constructor(protected resolver: ItemPageResolver, - protected authorizationService: AuthorizationDataService, - protected router: Router, - protected authService: AuthService) { - super(resolver, authorizationService, router, authService); - } - - /** - * Check administrator authorization rights - */ - getFeatureID(next: ActivatedRouteSnapshot, state: RouterStateSnapshot): Observable { - return observableOf(FeatureID.CanSynchronizeWithORCID); - } -} +export const orcidPageGuard: CanActivateFn = + dsoPageSingleFeatureGuard( + () => { + const itemPageResolver = inject(ItemPageResolver); + return itemPageResolver.resolve as ResolveFn>>; + }, + () => observableOf(FeatureID.CanSynchronizeWithORCID) + ); diff --git a/src/app/statistics-page/statistics-page-routing.module.ts b/src/app/statistics-page/statistics-page-routing.module.ts index ef6f68d5570..e5389db601e 100644 --- a/src/app/statistics-page/statistics-page-routing.module.ts +++ b/src/app/statistics-page/statistics-page-routing.module.ts @@ -10,7 +10,9 @@ import { ThemedCommunityStatisticsPageComponent } from './community-statistics-p import { ThemedItemStatisticsPageComponent } from './item-statistics-page/themed-item-statistics-page.component'; import { ThemedSiteStatisticsPageComponent } from './site-statistics-page/themed-site-statistics-page.component'; import { ItemResolver } from '../item-page/item.resolver'; -import { StatisticsAdministratorGuard } from '../core/data/feature-authorization/feature-authorization-guard/statistics-administrator.guard'; +import { + statisticsAdministratorGuard +} from '../core/data/feature-authorization/feature-authorization-guard/statistics-administrator.guard'; @NgModule({ imports: [ @@ -31,7 +33,7 @@ import { StatisticsAdministratorGuard } from '../core/data/feature-authorization component: ThemedSiteStatisticsPageComponent, }, ], - canActivate: [StatisticsAdministratorGuard] + canActivate: [statisticsAdministratorGuard] }, { path: `items/:id`, @@ -44,7 +46,7 @@ import { StatisticsAdministratorGuard } from '../core/data/feature-authorization breadcrumbKey: 'statistics' }, component: ThemedItemStatisticsPageComponent, - canActivate: [StatisticsAdministratorGuard] + canActivate: [statisticsAdministratorGuard] }, { path: `collections/:id`, @@ -57,7 +59,7 @@ import { StatisticsAdministratorGuard } from '../core/data/feature-authorization breadcrumbKey: 'statistics' }, component: ThemedCollectionStatisticsPageComponent, - canActivate: [StatisticsAdministratorGuard] + canActivate: [statisticsAdministratorGuard] }, { path: `communities/:id`, @@ -70,7 +72,7 @@ import { StatisticsAdministratorGuard } from '../core/data/feature-authorization breadcrumbKey: 'statistics' }, component: ThemedCommunityStatisticsPageComponent, - canActivate: [StatisticsAdministratorGuard] + canActivate: [statisticsAdministratorGuard] }, ] ) diff --git a/src/app/system-wide-alert/system-wide-alert-routing.module.ts b/src/app/system-wide-alert/system-wide-alert-routing.module.ts index beb1b32187a..97aa6614a7c 100644 --- a/src/app/system-wide-alert/system-wide-alert-routing.module.ts +++ b/src/app/system-wide-alert/system-wide-alert-routing.module.ts @@ -1,16 +1,17 @@ import { NgModule } from '@angular/core'; import { RouterModule } from '@angular/router'; + +import { SystemWideAlertFormComponent } from './alert-form/system-wide-alert-form.component'; import { - SiteAdministratorGuard + siteAdministratorGuard } from '../core/data/feature-authorization/feature-authorization-guard/site-administrator.guard'; -import { SystemWideAlertFormComponent } from './alert-form/system-wide-alert-form.component'; @NgModule({ imports: [ RouterModule.forChild([ { path: '', - canActivate: [SiteAdministratorGuard], + canActivate: [siteAdministratorGuard], component: SystemWideAlertFormComponent, }, From f63aea103da672e4a7ab275e845c5a5a03bcc17b Mon Sep 17 00:00:00 2001 From: Art Lowel Date: Tue, 30 Apr 2024 14:25:01 +0200 Subject: [PATCH 02/17] add tests --- ...single-feature-authorization.guard.spec.ts | 40 +++- .../some-feature-authorization.guard.spec.ts | 222 +++++++++--------- 2 files changed, 139 insertions(+), 123 deletions(-) diff --git a/src/app/core/data/feature-authorization/feature-authorization-guard/single-feature-authorization.guard.spec.ts b/src/app/core/data/feature-authorization/feature-authorization-guard/single-feature-authorization.guard.spec.ts index 92d1118ea9c..fc631ee9acb 100644 --- a/src/app/core/data/feature-authorization/feature-authorization-guard/single-feature-authorization.guard.spec.ts +++ b/src/app/core/data/feature-authorization/feature-authorization-guard/single-feature-authorization.guard.spec.ts @@ -1,12 +1,13 @@ import { AuthorizationDataService } from '../authorization-data.service'; import { FeatureID } from '../feature-id'; -import { of as observableOf } from 'rxjs'; -import { Router } from '@angular/router'; +import { of as observableOf, Observable } from 'rxjs'; +import { Router, UrlTree } from '@angular/router'; import { AuthService } from '../../../auth/auth.service'; import { singleFeatureAuthorizationGuard } from './single-feature-authorization.guard'; +import { waitForAsync, TestBed } from '@angular/core/testing'; -describe('SingleFeatureAuthorizationGuard', () => { +describe('singleFeatureAuthorizationGuard', () => { let guard: any; let authorizationService: AuthorizationDataService; let router: Router; @@ -31,21 +32,34 @@ describe('SingleFeatureAuthorizationGuard', () => { isAuthenticated: observableOf(true), }); - guard = singleFeatureAuthorizationGuard; + TestBed.configureTestingModule({ + providers: [ + { provide: AuthorizationDataService, useValue: authorizationService }, + { provide: Router, useValue: router }, + { provide: AuthService, useValue: authService }, + ] + }); } - beforeEach(() => { + beforeEach(waitForAsync(() => { init(); - }); + })); - describe('canActivate', () => { - it('should call authorizationService.isAuthorized with the appropriate arguments', (done) => { - guard(observableOf(featureId), observableOf(objectUrl), observableOf(ePersonUuid))(undefined, { url: 'current-url' } as any).subscribe(() => { - expect(authorizationService.isAuthorized).toHaveBeenCalledWith(featureId, objectUrl, ePersonUuid); - done(); - }); + it('should call authorizationService.isAuthenticated with the appropriate arguments', (done: DoneFn) => { + const result$ = TestBed.runInInjectionContext(() => { + return singleFeatureAuthorizationGuard( + () => observableOf(featureId), + () => observableOf(objectUrl), + () => observableOf(ePersonUuid), + )(undefined, { url: 'current-url' } as any) + }) as Observable; - }, 10000); + + result$.subscribe(() => { + expect(authorizationService.isAuthorized).toHaveBeenCalledWith(featureId, objectUrl, ePersonUuid); + done(); + }) }); + }); diff --git a/src/app/core/data/feature-authorization/feature-authorization-guard/some-feature-authorization.guard.spec.ts b/src/app/core/data/feature-authorization/feature-authorization-guard/some-feature-authorization.guard.spec.ts index a3578d3eda8..9239eb998d3 100644 --- a/src/app/core/data/feature-authorization/feature-authorization-guard/some-feature-authorization.guard.spec.ts +++ b/src/app/core/data/feature-authorization/feature-authorization-guard/some-feature-authorization.guard.spec.ts @@ -1,110 +1,112 @@ -// import { AuthorizationDataService } from '../authorization-data.service'; -// import { FeatureID } from '../feature-id'; -// import { Observable, of as observableOf } from 'rxjs'; -// import { ActivatedRouteSnapshot, Router, RouterStateSnapshot } from '@angular/router'; -// import { AuthService } from '../../../auth/auth.service'; -// import { SomeFeatureAuthorizationGuard } from './some-feature-authorization.guard'; -// -// /** -// * Test implementation of abstract class SomeFeatureAuthorizationGuard -// * Provide the return values of the overwritten getters as constructor arguments -// */ -// class SomeFeatureAuthorizationGuardImpl extends SomeFeatureAuthorizationGuard { -// constructor(protected authorizationService: AuthorizationDataService, -// protected router: Router, -// protected authService: AuthService, -// protected featureIds: FeatureID[], -// protected objectUrl: string, -// protected ePersonUuid: string) { -// super(authorizationService, router, authService); -// } -// -// getFeatureIDs(route: ActivatedRouteSnapshot, state: RouterStateSnapshot): Observable { -// return observableOf(this.featureIds); -// } -// -// getObjectUrl(route: ActivatedRouteSnapshot, state: RouterStateSnapshot): Observable { -// return observableOf(this.objectUrl); -// } -// -// getEPersonUuid(route: ActivatedRouteSnapshot, state: RouterStateSnapshot): Observable { -// return observableOf(this.ePersonUuid); -// } -// } -// -// describe('SomeFeatureAuthorizationGuard', () => { -// let guard: SomeFeatureAuthorizationGuard; -// let authorizationService: AuthorizationDataService; -// let router: Router; -// let authService: AuthService; -// -// let featureIds: FeatureID[]; -// let authorizedFeatureIds: FeatureID[]; -// let objectUrl: string; -// let ePersonUuid: string; -// -// function init() { -// featureIds = [FeatureID.LoginOnBehalfOf, FeatureID.CanDelete]; -// authorizedFeatureIds = []; -// objectUrl = 'fake-object-url'; -// ePersonUuid = 'fake-eperson-uuid'; -// -// authorizationService = Object.assign({ -// isAuthorized(featureId?: FeatureID): Observable { -// return observableOf(authorizedFeatureIds.indexOf(featureId) > -1); -// } -// }); -// router = jasmine.createSpyObj('router', { -// parseUrl: {} -// }); -// authService = jasmine.createSpyObj('authService', { -// isAuthenticated: observableOf(true) -// }); -// guard = new SomeFeatureAuthorizationGuardImpl(authorizationService, router, authService, featureIds, objectUrl, ePersonUuid); -// } -// -// beforeEach(() => { -// init(); -// }); -// -// describe('canActivate', () => { -// describe('when the user isn\'t authorized', () => { -// beforeEach(() => { -// authorizedFeatureIds = []; -// }); -// -// it('should not return true', (done) => { -// guard.canActivate(undefined, { url: 'current-url' } as any).subscribe((result) => { -// expect(result).not.toEqual(true); -// done(); -// }); -// }); -// }); -// -// describe('when the user is authorized for at least one of the guard\'s features', () => { -// beforeEach(() => { -// authorizedFeatureIds = [featureIds[0]]; -// }); -// -// it('should return true', (done) => { -// guard.canActivate(undefined, { url: 'current-url' } as any).subscribe((result) => { -// expect(result).toEqual(true); -// done(); -// }); -// }); -// }); -// -// describe('when the user is authorized for all of the guard\'s features', () => { -// beforeEach(() => { -// authorizedFeatureIds = featureIds; -// }); -// -// it('should return true', (done) => { -// guard.canActivate(undefined, { url: 'current-url' } as any).subscribe((result) => { -// expect(result).toEqual(true); -// done(); -// }); -// }); -// }); -// }); -// }); +import { AuthorizationDataService } from '../authorization-data.service'; +import { FeatureID } from '../feature-id'; +import { Observable, of as observableOf } from 'rxjs'; +import { Router, UrlTree } from '@angular/router'; +import { AuthService } from '../../../auth/auth.service'; +import { waitForAsync, TestBed } from '@angular/core/testing'; +import { someFeatureAuthorizationGuard } from './some-feature-authorization.guard'; + +describe('someFeatureAuthorizationGuard', () => { + let authorizationService: AuthorizationDataService; + let router: Router; + let authService: AuthService; + + let featureIds: FeatureID[]; + let authorizedFeatureIds: FeatureID[]; + let objectUrl: string; + let ePersonUuid: string; + + function init() { + featureIds = [FeatureID.LoginOnBehalfOf, FeatureID.CanDelete]; + authorizedFeatureIds = []; + objectUrl = 'fake-object-url'; + ePersonUuid = 'fake-eperson-uuid'; + + authorizationService = Object.assign({ + isAuthorized(featureId?: FeatureID): Observable { + return observableOf(authorizedFeatureIds.indexOf(featureId) > -1); + } + }); + router = jasmine.createSpyObj('router', { + parseUrl: {} + }); + authService = jasmine.createSpyObj('authService', { + isAuthenticated: observableOf(true) + }); + + TestBed.configureTestingModule({ + providers: [ + { provide: AuthorizationDataService, useValue: authorizationService }, + { provide: Router, useValue: router }, + { provide: AuthService, useValue: authService }, + ] + }); + } + + beforeEach(waitForAsync(() => { + init(); + })); + + describe('when the user isn\'t authorized', () => { + beforeEach(() => { + authorizedFeatureIds = []; + }); + + it('should not return true', (done: DoneFn) => { + const result$ = TestBed.runInInjectionContext(() => { + return someFeatureAuthorizationGuard( + () => observableOf(featureIds), + () => observableOf(objectUrl), + () => observableOf(ePersonUuid), + )(undefined, { url: 'current-url' } as any) + }) as Observable; + + result$.subscribe((result) => { + expect(result).not.toEqual(true); + done(); + }); + }); + }); + + describe('when the user is authorized for at least one of the guard\'s features', () => { + beforeEach(() => { + authorizedFeatureIds = [featureIds[0]]; + }); + + it('should return true', (done) => { + const result$ = TestBed.runInInjectionContext(() => { + return someFeatureAuthorizationGuard( + () => observableOf(featureIds), + () => observableOf(objectUrl), + () => observableOf(ePersonUuid), + )(undefined, { url: 'current-url' } as any) + }) as Observable; + + result$.subscribe((result) => { + expect(result).toEqual(true); + done(); + }); + }); + }); + + describe('when the user is authorized for all of the guard\'s features', () => { + beforeEach(() => { + authorizedFeatureIds = featureIds; + }); + + it('should return true', (done) => { + const result$ = TestBed.runInInjectionContext(() => { + return someFeatureAuthorizationGuard( + () => observableOf(featureIds), + () => observableOf(objectUrl), + () => observableOf(ePersonUuid), + )(undefined, { url: 'current-url' } as any) + }) as Observable; + + result$.subscribe((result) => { + expect(result).toEqual(true); + done(); + }); + }); + }); +}); From 9b9bcf718f4e3d6c1f584dcecfa0102b95e5ee76 Mon Sep 17 00:00:00 2001 From: Art Lowel Date: Tue, 30 Apr 2024 14:33:02 +0200 Subject: [PATCH 03/17] refactor test --- .../some-feature-authorization.guard.spec.ts | 27 ++++++++----------- 1 file changed, 11 insertions(+), 16 deletions(-) diff --git a/src/app/core/data/feature-authorization/feature-authorization-guard/some-feature-authorization.guard.spec.ts b/src/app/core/data/feature-authorization/feature-authorization-guard/some-feature-authorization.guard.spec.ts index 9239eb998d3..8f95cd2ee2a 100644 --- a/src/app/core/data/feature-authorization/feature-authorization-guard/some-feature-authorization.guard.spec.ts +++ b/src/app/core/data/feature-authorization/feature-authorization-guard/some-feature-authorization.guard.spec.ts @@ -1,12 +1,13 @@ import { AuthorizationDataService } from '../authorization-data.service'; import { FeatureID } from '../feature-id'; import { Observable, of as observableOf } from 'rxjs'; -import { Router, UrlTree } from '@angular/router'; +import { Router, UrlTree, CanActivateFn } from '@angular/router'; import { AuthService } from '../../../auth/auth.service'; import { waitForAsync, TestBed } from '@angular/core/testing'; import { someFeatureAuthorizationGuard } from './some-feature-authorization.guard'; describe('someFeatureAuthorizationGuard', () => { + let guard: CanActivateFn; let authorizationService: AuthorizationDataService; let router: Router; let authService: AuthService; @@ -41,6 +42,12 @@ describe('someFeatureAuthorizationGuard', () => { { provide: AuthService, useValue: authService }, ] }); + + guard = someFeatureAuthorizationGuard( + () => observableOf(featureIds), + () => observableOf(objectUrl), + () => observableOf(ePersonUuid), + ); } beforeEach(waitForAsync(() => { @@ -54,11 +61,7 @@ describe('someFeatureAuthorizationGuard', () => { it('should not return true', (done: DoneFn) => { const result$ = TestBed.runInInjectionContext(() => { - return someFeatureAuthorizationGuard( - () => observableOf(featureIds), - () => observableOf(objectUrl), - () => observableOf(ePersonUuid), - )(undefined, { url: 'current-url' } as any) + return guard(undefined, { url: 'current-url' } as any) }) as Observable; result$.subscribe((result) => { @@ -75,11 +78,7 @@ describe('someFeatureAuthorizationGuard', () => { it('should return true', (done) => { const result$ = TestBed.runInInjectionContext(() => { - return someFeatureAuthorizationGuard( - () => observableOf(featureIds), - () => observableOf(objectUrl), - () => observableOf(ePersonUuid), - )(undefined, { url: 'current-url' } as any) + return guard(undefined, { url: 'current-url' } as any) }) as Observable; result$.subscribe((result) => { @@ -96,11 +95,7 @@ describe('someFeatureAuthorizationGuard', () => { it('should return true', (done) => { const result$ = TestBed.runInInjectionContext(() => { - return someFeatureAuthorizationGuard( - () => observableOf(featureIds), - () => observableOf(objectUrl), - () => observableOf(ePersonUuid), - )(undefined, { url: 'current-url' } as any) + return guard(undefined, { url: 'current-url' } as any) }) as Observable; result$.subscribe((result) => { From 5592be87909665d7a324abb8b4962e27682c3cbf Mon Sep 17 00:00:00 2001 From: Zahraa Chreim Date: Wed, 1 May 2024 09:46:46 +0300 Subject: [PATCH 04/17] 114645: Refactor Class-Based guards tests --- .../group-registry/group-page.guard.spec.ts | 183 ++++++++++-------- .../group-registry/group-page.guard.ts | 3 +- .../dso-page-single-feature.guard.spec.ts | 176 ++++++++--------- .../dso-page-some-feature.guard.spec.ts | 183 +++++++++--------- ...single-feature-authorization.guard.spec.ts | 31 ++- .../some-feature-authorization.guard.spec.ts | 102 +++++----- 6 files changed, 358 insertions(+), 320 deletions(-) diff --git a/src/app/access-control/group-registry/group-page.guard.spec.ts b/src/app/access-control/group-registry/group-page.guard.spec.ts index b1bfd82c27a..9a301faab0f 100644 --- a/src/app/access-control/group-registry/group-page.guard.spec.ts +++ b/src/app/access-control/group-registry/group-page.guard.spec.ts @@ -1,83 +1,100 @@ -// import { GroupPageGuard } from './group-page.guard'; -// import { HALEndpointService } from '../../core/shared/hal-endpoint.service'; -// import { AuthorizationDataService } from '../../core/data/feature-authorization/authorization-data.service'; -// import { ActivatedRouteSnapshot, Router } from '@angular/router'; -// import { of as observableOf } from 'rxjs'; -// import { AuthService } from '../../core/auth/auth.service'; -// import { FeatureID } from '../../core/data/feature-authorization/feature-id'; -// -// describe('GroupPageGuard', () => { -// const groupsEndpointUrl = 'https://test.org/api/eperson/groups'; -// const groupUuid = '0d6f89df-f95a-4829-943c-f21f434fb892'; -// const groupEndpointUrl = `${groupsEndpointUrl}/${groupUuid}`; -// const routeSnapshotWithGroupId = { -// params: { -// groupId: groupUuid, -// } -// } as unknown as ActivatedRouteSnapshot; -// -// let guard: GroupPageGuard; -// let halEndpointService: HALEndpointService; -// let authorizationService: AuthorizationDataService; -// let router: Router; -// let authService: AuthService; -// -// beforeEach(() => { -// halEndpointService = jasmine.createSpyObj(['getEndpoint']); -// (halEndpointService as any).getEndpoint.and.returnValue(observableOf(groupsEndpointUrl)); -// -// authorizationService = jasmine.createSpyObj(['isAuthorized']); -// // NOTE: value is set in beforeEach -// -// router = jasmine.createSpyObj(['parseUrl']); -// (router as any).parseUrl.and.returnValue = {}; -// -// authService = jasmine.createSpyObj(['isAuthenticated']); -// (authService as any).isAuthenticated.and.returnValue(observableOf(true)); -// -// guard = new GroupPageGuard(halEndpointService, authorizationService, router, authService); -// }); -// -// it('should be created', () => { -// expect(guard).toBeTruthy(); -// }); -// -// describe('canActivate', () => { -// describe('when the current user can manage the group', () => { -// beforeEach(() => { -// (authorizationService as any).isAuthorized.and.returnValue(observableOf(true)); -// }); -// -// it('should return true', (done) => { -// guard.canActivate( -// routeSnapshotWithGroupId, { url: 'current-url'} as any -// ).subscribe((result) => { -// expect(authorizationService.isAuthorized).toHaveBeenCalledWith( -// FeatureID.CanManageGroup, groupEndpointUrl, undefined -// ); -// expect(result).toBeTrue(); -// done(); -// }); -// }); -// }); -// -// describe('when the current user can not manage the group', () => { -// beforeEach(() => { -// (authorizationService as any).isAuthorized.and.returnValue(observableOf(false)); -// }); -// -// it('should not return true', (done) => { -// guard.canActivate( -// routeSnapshotWithGroupId, { url: 'current-url'} as any -// ).subscribe((result) => { -// expect(authorizationService.isAuthorized).toHaveBeenCalledWith( -// FeatureID.CanManageGroup, groupEndpointUrl, undefined -// ); -// expect(result).not.toBeTrue(); -// done(); -// }); -// }); -// }); -// }); -// -// }); +import { HALEndpointService } from '../../core/shared/hal-endpoint.service'; +import { AuthorizationDataService } from '../../core/data/feature-authorization/authorization-data.service'; +import { ActivatedRouteSnapshot, Router, UrlTree } from '@angular/router'; +import { of as observableOf, Observable } from 'rxjs'; +import { AuthService } from '../../core/auth/auth.service'; +import { TestBed, waitForAsync } from '@angular/core/testing'; +import { groupPageGuard } from './group-page.guard'; +import { FeatureID } from '../../core/data/feature-authorization/feature-id'; +jasmine.DEFAULT_TIMEOUT_INTERVAL = 10000; // Increase timeout to 10 seconds + +describe('GroupPageGuard', () => { + const groupsEndpointUrl = 'https://test.org/api/eperson/groups'; + const groupUuid = '0d6f89df-f95a-4829-943c-f21f434fb892'; + const groupEndpointUrl = `${groupsEndpointUrl}/${groupUuid}`; + const routeSnapshotWithGroupId = { + params: { + groupId: groupUuid, + } + } as unknown as ActivatedRouteSnapshot; + + let halEndpointService: HALEndpointService; + let authorizationService: AuthorizationDataService; + let router: Router; + let authService: AuthService; + + function init() { + halEndpointService = jasmine.createSpyObj(['getEndpoint']); + ( halEndpointService as any ).getEndpoint.and.returnValue(observableOf(groupsEndpointUrl)); + + authorizationService = jasmine.createSpyObj(['isAuthorized']); + // NOTE: value is set in beforeEach + + router = jasmine.createSpyObj(['parseUrl']); + ( router as any ).parseUrl.and.returnValue = {}; + + authService = jasmine.createSpyObj(['isAuthenticated']); + ( authService as any ).isAuthenticated.and.returnValue(observableOf(true)); + + TestBed.configureTestingModule({ + providers: [ + { provide: AuthorizationDataService, useValue: authorizationService }, + { provide: Router, useValue: router }, + { provide: AuthService, useValue: authService }, + { provide: HALEndpointService, useValue: halEndpointService }, + ] + }); + } + + beforeEach(waitForAsync(() => { + init(); + })); + + it('should be created', () => { + expect(groupPageGuard).toBeTruthy(); + }); + + describe('canActivate', () => { + describe('when the current user can manage the group', () => { + beforeEach(() => { + ( authorizationService as any ).isAuthorized.and.returnValue(observableOf(true)); + }); + + it('should return true', (done) => { + const result$ = TestBed.runInInjectionContext(() => { + return groupPageGuard()(routeSnapshotWithGroupId, { url: 'current-url' } as any); + }) as Observable; + + result$.subscribe((result) => { + expect(authorizationService.isAuthorized).toHaveBeenCalledWith( + FeatureID.CanManageGroup, groupEndpointUrl, undefined + ); + expect(result).toBeTrue(); + done(); + }); + }); + }); + + describe('when the current user can not manage the group', () => { + beforeEach(() => { + (authorizationService as any).isAuthorized.and.returnValue(observableOf(false)); + }); + + it('should not return true', (done) => { + const result$ = TestBed.runInInjectionContext(() => { + return groupPageGuard()(routeSnapshotWithGroupId, { url: 'current-url' } as any); + }) as Observable; + + result$.subscribe((result) => { + expect(authorizationService.isAuthorized).toHaveBeenCalledWith( + FeatureID.CanManageGroup, groupEndpointUrl, undefined + ); + expect(result).not.toBeTrue(); + done(); + }); + + }); + }); + }); + +}); diff --git a/src/app/access-control/group-registry/group-page.guard.ts b/src/app/access-control/group-registry/group-page.guard.ts index b5e39bdff50..ef49088a762 100644 --- a/src/app/access-control/group-registry/group-page.guard.ts +++ b/src/app/access-control/group-registry/group-page.guard.ts @@ -4,7 +4,7 @@ import { inject } from '@angular/core'; import { map } from 'rxjs/operators'; import { HALEndpointService } from '../../core/shared/hal-endpoint.service'; import { - StringGuardParamFn, someFeatureAuthorizationGuard, SomeFeatureGuardParamFn + StringGuardParamFn, someFeatureAuthorizationGuard } from '../../core/data/feature-authorization/feature-authorization-guard/some-feature-authorization.guard'; import { FeatureID } from '../../core/data/feature-authorization/feature-id'; @@ -21,7 +21,6 @@ const defaultGroupPageGetObjectUrl: StringGuardParamFn = ( }; export const groupPageGuard = ( - getFeatureIDs: SomeFeatureGuardParamFn, getObjectUrl = defaultGroupPageGetObjectUrl, getEPersonUuid?: StringGuardParamFn, ): CanActivateFn => someFeatureAuthorizationGuard( diff --git a/src/app/core/data/feature-authorization/feature-authorization-guard/dso-page-single-feature.guard.spec.ts b/src/app/core/data/feature-authorization/feature-authorization-guard/dso-page-single-feature.guard.spec.ts index fdd173ab4dc..0d967fc03d1 100644 --- a/src/app/core/data/feature-authorization/feature-authorization-guard/dso-page-single-feature.guard.spec.ts +++ b/src/app/core/data/feature-authorization/feature-authorization-guard/dso-page-single-feature.guard.spec.ts @@ -1,87 +1,89 @@ -// import { AuthorizationDataService } from '../authorization-data.service'; -// import { ActivatedRouteSnapshot, Resolve, Router, RouterStateSnapshot } from '@angular/router'; -// import { RemoteData } from '../../remote-data'; -// import { Observable, of as observableOf } from 'rxjs'; -// import { createSuccessfulRemoteDataObject$ } from '../../../../shared/remote-data.utils'; -// import { DSpaceObject } from '../../../shared/dspace-object.model'; -// import { DsoPageSingleFeatureGuard } from './dso-page-single-feature.guard'; -// import { FeatureID } from '../feature-id'; -// import { AuthService } from '../../../auth/auth.service'; -// -// /** -// * Test implementation of abstract class DsoPageSingleFeatureGuard -// */ -// class DsoPageSingleFeatureGuardImpl extends DsoPageSingleFeatureGuard { -// constructor(protected resolver: Resolve>, -// protected authorizationService: AuthorizationDataService, -// protected router: Router, -// protected authService: AuthService, -// protected featureID: FeatureID) { -// super(resolver, authorizationService, router, authService); -// } -// -// getFeatureID(route: ActivatedRouteSnapshot, state: RouterStateSnapshot): Observable { -// return observableOf(this.featureID); -// } -// } -// -// describe('DsoPageSingleFeatureGuard', () => { -// let guard: DsoPageSingleFeatureGuard; -// let authorizationService: AuthorizationDataService; -// let router: Router; -// let authService: AuthService; -// let resolver: Resolve>; -// let object: DSpaceObject; -// let route; -// let parentRoute; -// -// function init() { -// object = { -// self: 'test-selflink' -// } as DSpaceObject; -// -// authorizationService = jasmine.createSpyObj('authorizationService', { -// isAuthorized: observableOf(true) -// }); -// router = jasmine.createSpyObj('router', { -// parseUrl: {} -// }); -// resolver = jasmine.createSpyObj('resolver', { -// resolve: createSuccessfulRemoteDataObject$(object) -// }); -// authService = jasmine.createSpyObj('authService', { -// isAuthenticated: observableOf(true) -// }); -// parentRoute = { -// params: { -// id: '3e1a5327-dabb-41ff-af93-e6cab9d032f0' -// } -// }; -// route = { -// params: { -// }, -// parent: parentRoute -// }; -// guard = new DsoPageSingleFeatureGuardImpl(resolver, authorizationService, router, authService, undefined); -// } -// -// beforeEach(() => { -// init(); -// }); -// -// describe('getObjectUrl', () => { -// it('should return the resolved object\'s selflink', (done) => { -// guard.getObjectUrl(route, undefined).subscribe((selflink) => { -// expect(selflink).toEqual(object.self); -// done(); -// }); -// }); -// }); -// -// describe('getRouteWithDSOId', () => { -// it('should return the route that has the UUID of the DSO', () => { -// const foundRoute = (guard as any).getRouteWithDSOId(route); -// expect(foundRoute).toBe(parentRoute); -// }); -// }); -// }); +import { AuthorizationDataService } from '../authorization-data.service'; +import { Router, UrlTree, ResolveFn } from '@angular/router'; +import { RemoteData } from '../../remote-data'; +import { Observable, of as observableOf } from 'rxjs'; +import { createSuccessfulRemoteDataObject$ } from '../../../../shared/remote-data.utils'; +import { DSpaceObject } from '../../../shared/dspace-object.model'; +import { AuthService } from '../../../auth/auth.service'; +import { TestBed } from '@angular/core/testing'; +import { dsoPageSingleFeatureGuard } from './dso-page-single-feature.guard'; +import { FeatureID } from '../feature-id'; + + +describe('DsoPageSingleFeatureGuard', () => { + let authorizationService: AuthorizationDataService; + let router: Router; + let authService: AuthService; + let resolver: ResolveFn>>; + // let resolver: jasmine.SpyObj<() => Observable>>; + let object: DSpaceObject; + let route; + let parentRoute; + + let featureId: FeatureID; + + function init() { + object = { + self: 'test-selflink' + } as DSpaceObject; + + authorizationService = jasmine.createSpyObj('authorizationService', { + isAuthorized: observableOf(true) + }); + router = jasmine.createSpyObj('router', { + parseUrl: {} + }); + resolver = jasmine.createSpyObj('resolver', { + resolve: createSuccessfulRemoteDataObject$(object) + }); + // resolver = jasmine.createSpy('resolver') // Mocking the resolver function + // .and.returnValue(observableOf({})); // Returning an observable directly + authService = jasmine.createSpyObj('authService', { + isAuthenticated: observableOf(true) + }); + parentRoute = { + params: { + id: '3e1a5327-dabb-41ff-af93-e6cab9d032f0' + } + }; + route = { + params: { + }, + parent: parentRoute + }; + + featureId = FeatureID.LoginOnBehalfOf; + + TestBed.configureTestingModule({ + providers: [ + { provide: AuthorizationDataService, useValue: authorizationService }, + { provide: Router, useValue: router }, + { provide: AuthService, useValue: authService }, + ] + }); + } + + beforeEach(() => { + init(); + }); + + describe('canActivate', () => { + it('should call authorizationService.isAuthenticated with the appropriate arguments', (done) => { + const result$ = TestBed.runInInjectionContext(() => { + return dsoPageSingleFeatureGuard( + () => resolver, () => observableOf(featureId) + )(route, { url: 'current-url' } as any); + }) as Observable; + + console.log('result$', result$); + + result$.subscribe(() => { + expect(authorizationService.isAuthorized).toHaveBeenCalledWith(featureId); + done(); + }); + + // expect(authorizationService.isAuthorized).toHaveBeenCalledWith(featureId); + // done(); + }); + }); +}); diff --git a/src/app/core/data/feature-authorization/feature-authorization-guard/dso-page-some-feature.guard.spec.ts b/src/app/core/data/feature-authorization/feature-authorization-guard/dso-page-some-feature.guard.spec.ts index 8735aa92935..f0f5e5e63f8 100644 --- a/src/app/core/data/feature-authorization/feature-authorization-guard/dso-page-some-feature.guard.spec.ts +++ b/src/app/core/data/feature-authorization/feature-authorization-guard/dso-page-some-feature.guard.spec.ts @@ -1,87 +1,96 @@ -// import { AuthorizationDataService } from '../authorization-data.service'; -// import { ActivatedRouteSnapshot, Resolve, Router, RouterStateSnapshot } from '@angular/router'; -// import { RemoteData } from '../../remote-data'; -// import { Observable, of as observableOf } from 'rxjs'; -// import { createSuccessfulRemoteDataObject$ } from '../../../../shared/remote-data.utils'; -// import { DSpaceObject } from '../../../shared/dspace-object.model'; -// import { FeatureID } from '../feature-id'; -// import { AuthService } from '../../../auth/auth.service'; -// import { DsoPageSomeFeatureGuard } from './dso-page-some-feature.guard'; -// -// /** -// * Test implementation of abstract class DsoPageSomeFeatureGuard -// */ -// class DsoPageSomeFeatureGuardImpl extends DsoPageSomeFeatureGuard { -// constructor(protected resolver: Resolve>, -// protected authorizationService: AuthorizationDataService, -// protected router: Router, -// protected authService: AuthService, -// protected featureIDs: FeatureID[]) { -// super(resolver, authorizationService, router, authService); -// } -// -// getFeatureIDs(route: ActivatedRouteSnapshot, state: RouterStateSnapshot): Observable { -// return observableOf(this.featureIDs); -// } -// } -// -// describe('DsoPageSomeFeatureGuard', () => { -// let guard: DsoPageSomeFeatureGuard; -// let authorizationService: AuthorizationDataService; -// let router: Router; -// let authService: AuthService; -// let resolver: Resolve>; -// let object: DSpaceObject; -// let route; -// let parentRoute; -// -// function init() { -// object = { -// self: 'test-selflink' -// } as DSpaceObject; -// -// authorizationService = jasmine.createSpyObj('authorizationService', { -// isAuthorized: observableOf(true) -// }); -// router = jasmine.createSpyObj('router', { -// parseUrl: {} -// }); -// resolver = jasmine.createSpyObj('resolver', { -// resolve: createSuccessfulRemoteDataObject$(object) -// }); -// authService = jasmine.createSpyObj('authService', { -// isAuthenticated: observableOf(true) -// }); -// parentRoute = { -// params: { -// id: '3e1a5327-dabb-41ff-af93-e6cab9d032f0' -// } -// }; -// route = { -// params: { -// }, -// parent: parentRoute -// }; -// guard = new DsoPageSomeFeatureGuardImpl(resolver, authorizationService, router, authService, []); -// } -// -// beforeEach(() => { -// init(); -// }); -// -// describe('getObjectUrl', () => { -// it('should return the resolved object\'s selflink', (done) => { -// guard.getObjectUrl(route, undefined).subscribe((selflink) => { -// expect(selflink).toEqual(object.self); -// done(); -// }); -// }); -// }); -// -// describe('getRouteWithDSOId', () => { -// it('should return the route that has the UUID of the DSO', () => { -// const foundRoute = (guard as any).getRouteWithDSOId(route); -// expect(foundRoute).toBe(parentRoute); -// }); -// }); -// }); +import { AuthorizationDataService } from '../authorization-data.service'; +import { Resolve, Router, UrlTree, ResolveFn } from '@angular/router'; +import { RemoteData } from '../../remote-data'; +import { Observable, of as observableOf } from 'rxjs'; +import { createSuccessfulRemoteDataObject$ } from '../../../../shared/remote-data.utils'; +import { DSpaceObject } from '../../../shared/dspace-object.model'; +import { AuthService } from '../../../auth/auth.service'; +import { dsoPageSomeFeatureGuard } from './dso-page-some-feature.guard'; +import { TestBed } from '@angular/core/testing'; +import { FeatureID } from '../feature-id'; + + +describe('DsoPageSomeFeatureGuard', () => { + let authorizationService: AuthorizationDataService; + let router: Router; + let authService: AuthService; + // let resolver: Resolve>; + let resolver: ResolveFn>>; + let object: DSpaceObject; + let route; + let parentRoute; + + let featureIds: FeatureID[]; + + function init() { + object = { + self: 'test-selflink' + } as DSpaceObject; + + authorizationService = jasmine.createSpyObj('authorizationService', { + isAuthorized: observableOf(true) + }); + router = jasmine.createSpyObj('router', { + parseUrl: {} + }); + // resolver = jasmine.createSpyObj('resolver', { + // resolve: createSuccessfulRemoteDataObject$(object) + // }); + authService = jasmine.createSpyObj('authService', { + isAuthenticated: observableOf(true) + }); + parentRoute = { + params: { + id: '3e1a5327-dabb-41ff-af93-e6cab9d032f0' + } + }; + route = { + params: { + }, + parent: parentRoute + }; + + TestBed.configureTestingModule({ + providers: [ + { provide: AuthorizationDataService, useValue: authorizationService }, + { provide: Router, useValue: router }, + { provide: AuthService, useValue: authService }, + ] + }); + + } + + beforeEach(() => { + init(); + }); + + describe('getObjectUrl', () => { + it('should return the resolved object\'s selflink', (done) => { + + const result$ = TestBed.runInInjectionContext(() => { + return dsoPageSomeFeatureGuard( + () => resolver, () => observableOf(featureIds) + )(route, { url: 'current-url' } as any); + }) as Observable; + + console.log('result$', result$); + + result$.subscribe(() => { + expect(authorizationService.isAuthorized).toHaveBeenCalledWith(featureIds[0]); + done(); + }); + + // guard.getObjectUrl(route, undefined).subscribe((selflink) => { + // expect(selflink).toEqual(object.self); + // done(); + // }); + }); + }); + + // describe('getRouteWithDSOId', () => { + // it('should return the route that has the UUID of the DSO', () => { + // const foundRoute = (guard as any).getRouteWithDSOId(route); + // expect(foundRoute).toBe(parentRoute); + // }); + // }); +}); diff --git a/src/app/core/data/feature-authorization/feature-authorization-guard/single-feature-authorization.guard.spec.ts b/src/app/core/data/feature-authorization/feature-authorization-guard/single-feature-authorization.guard.spec.ts index fc631ee9acb..dc518ddaffc 100644 --- a/src/app/core/data/feature-authorization/feature-authorization-guard/single-feature-authorization.guard.spec.ts +++ b/src/app/core/data/feature-authorization/feature-authorization-guard/single-feature-authorization.guard.spec.ts @@ -6,9 +6,7 @@ import { AuthService } from '../../../auth/auth.service'; import { singleFeatureAuthorizationGuard } from './single-feature-authorization.guard'; import { waitForAsync, TestBed } from '@angular/core/testing'; - describe('singleFeatureAuthorizationGuard', () => { - let guard: any; let authorizationService: AuthorizationDataService; let router: Router; let authService: AuthService; @@ -45,21 +43,22 @@ describe('singleFeatureAuthorizationGuard', () => { init(); })); - it('should call authorizationService.isAuthenticated with the appropriate arguments', (done: DoneFn) => { - const result$ = TestBed.runInInjectionContext(() => { - return singleFeatureAuthorizationGuard( - () => observableOf(featureId), - () => observableOf(objectUrl), - () => observableOf(ePersonUuid), - )(undefined, { url: 'current-url' } as any) - }) as Observable; + describe('canActivate', () => { + it('should call authorizationService.isAuthenticated with the appropriate arguments', (done: DoneFn) => { + const result$ = TestBed.runInInjectionContext(() => { + return singleFeatureAuthorizationGuard( + () => observableOf(featureId), + () => observableOf(objectUrl), + () => observableOf(ePersonUuid), + )(undefined, { url: 'current-url' } as any); + }) as Observable; - result$.subscribe(() => { - expect(authorizationService.isAuthorized).toHaveBeenCalledWith(featureId, objectUrl, ePersonUuid); - done(); - }) - }); + result$.subscribe(() => { + expect(authorizationService.isAuthorized).toHaveBeenCalledWith(featureId, objectUrl, ePersonUuid); + done(); + }); + }); + }); }); - diff --git a/src/app/core/data/feature-authorization/feature-authorization-guard/some-feature-authorization.guard.spec.ts b/src/app/core/data/feature-authorization/feature-authorization-guard/some-feature-authorization.guard.spec.ts index 8f95cd2ee2a..c12668d6a5e 100644 --- a/src/app/core/data/feature-authorization/feature-authorization-guard/some-feature-authorization.guard.spec.ts +++ b/src/app/core/data/feature-authorization/feature-authorization-guard/some-feature-authorization.guard.spec.ts @@ -1,13 +1,12 @@ import { AuthorizationDataService } from '../authorization-data.service'; import { FeatureID } from '../feature-id'; import { Observable, of as observableOf } from 'rxjs'; -import { Router, UrlTree, CanActivateFn } from '@angular/router'; +import { Router, UrlTree } from '@angular/router'; import { AuthService } from '../../../auth/auth.service'; -import { waitForAsync, TestBed } from '@angular/core/testing'; import { someFeatureAuthorizationGuard } from './some-feature-authorization.guard'; +import { TestBed, waitForAsync } from '@angular/core/testing'; -describe('someFeatureAuthorizationGuard', () => { - let guard: CanActivateFn; +describe('SomeFeatureAuthorizationGuard', () => { let authorizationService: AuthorizationDataService; let router: Router; let authService: AuthService; @@ -28,9 +27,11 @@ describe('someFeatureAuthorizationGuard', () => { return observableOf(authorizedFeatureIds.indexOf(featureId) > -1); } }); + router = jasmine.createSpyObj('router', { parseUrl: {} }); + authService = jasmine.createSpyObj('authService', { isAuthenticated: observableOf(true) }); @@ -42,65 +43,76 @@ describe('someFeatureAuthorizationGuard', () => { { provide: AuthService, useValue: authService }, ] }); - - guard = someFeatureAuthorizationGuard( - () => observableOf(featureIds), - () => observableOf(objectUrl), - () => observableOf(ePersonUuid), - ); } beforeEach(waitForAsync(() => { init(); })); - describe('when the user isn\'t authorized', () => { - beforeEach(() => { - authorizedFeatureIds = []; - }); + describe('canActivate', () => { + describe('when the user isn\'t authorized', () => { + beforeEach(() => { + authorizedFeatureIds = []; + }); + + it('should not return true', (done) => { - it('should not return true', (done: DoneFn) => { - const result$ = TestBed.runInInjectionContext(() => { - return guard(undefined, { url: 'current-url' } as any) - }) as Observable; + const result$ = TestBed.runInInjectionContext(() => { + return someFeatureAuthorizationGuard( + () => observableOf(featureIds), + () => observableOf(objectUrl), + () => observableOf(ePersonUuid), + )(undefined, { url: 'current-url' } as any); + }) as Observable; - result$.subscribe((result) => { - expect(result).not.toEqual(true); - done(); + result$.subscribe((result) => { + expect(result).not.toEqual(true); + done(); + }); }); }); - }); - describe('when the user is authorized for at least one of the guard\'s features', () => { - beforeEach(() => { - authorizedFeatureIds = [featureIds[0]]; - }); + describe('when the user is authorized for at least one of the guard\'s features', () => { + beforeEach(() => { + authorizedFeatureIds = [featureIds[0]]; + }); - it('should return true', (done) => { - const result$ = TestBed.runInInjectionContext(() => { - return guard(undefined, { url: 'current-url' } as any) - }) as Observable; + it('should return true', (done) => { - result$.subscribe((result) => { - expect(result).toEqual(true); - done(); + const result$ = TestBed.runInInjectionContext(() => { + return someFeatureAuthorizationGuard( + () => observableOf(featureIds), + () => observableOf(objectUrl), + () => observableOf(ePersonUuid), + )(undefined, { url: 'current-url' } as any); + }) as Observable; + + result$.subscribe((result) => { + expect(result).toEqual(true); + done(); + }); }); }); - }); - describe('when the user is authorized for all of the guard\'s features', () => { - beforeEach(() => { - authorizedFeatureIds = featureIds; - }); + describe('when the user is authorized for all of the guard\'s features', () => { + beforeEach(() => { + authorizedFeatureIds = featureIds; + }); + + it('should return true', (done) => { - it('should return true', (done) => { - const result$ = TestBed.runInInjectionContext(() => { - return guard(undefined, { url: 'current-url' } as any) - }) as Observable; + const result$ = TestBed.runInInjectionContext(() => { + return someFeatureAuthorizationGuard( + () => observableOf(featureIds), + () => observableOf(objectUrl), + () => observableOf(ePersonUuid), + )(undefined, { url: 'current-url' } as any); + }) as Observable; - result$.subscribe((result) => { - expect(result).toEqual(true); - done(); + result$.subscribe((result) => { + expect(result).toEqual(true); + done(); + }); }); }); }); From 0ec1d28bd19a3ab6c6eb8a2ef766f5cdfcad7215 Mon Sep 17 00:00:00 2001 From: Zahraa Chreim Date: Wed, 1 May 2024 09:51:56 +0300 Subject: [PATCH 05/17] 114645: Fix Lint Errors --- .../dso-page-some-feature.guard.spec.ts | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/app/core/data/feature-authorization/feature-authorization-guard/dso-page-some-feature.guard.spec.ts b/src/app/core/data/feature-authorization/feature-authorization-guard/dso-page-some-feature.guard.spec.ts index f0f5e5e63f8..c4e567181da 100644 --- a/src/app/core/data/feature-authorization/feature-authorization-guard/dso-page-some-feature.guard.spec.ts +++ b/src/app/core/data/feature-authorization/feature-authorization-guard/dso-page-some-feature.guard.spec.ts @@ -1,8 +1,7 @@ import { AuthorizationDataService } from '../authorization-data.service'; -import { Resolve, Router, UrlTree, ResolveFn } from '@angular/router'; +import { Router, UrlTree, ResolveFn } from '@angular/router'; import { RemoteData } from '../../remote-data'; import { Observable, of as observableOf } from 'rxjs'; -import { createSuccessfulRemoteDataObject$ } from '../../../../shared/remote-data.utils'; import { DSpaceObject } from '../../../shared/dspace-object.model'; import { AuthService } from '../../../auth/auth.service'; import { dsoPageSomeFeatureGuard } from './dso-page-some-feature.guard'; From aecf2c7b6117fcb740d2299c8c63152601a8a2be Mon Sep 17 00:00:00 2001 From: Art Lowel Date: Thu, 2 May 2024 11:22:58 +0200 Subject: [PATCH 06/17] add tests for dsoPageSomeFeatureGuard --- .../dso-page-some-feature.guard.spec.ts | 52 +++++++++++-------- 1 file changed, 30 insertions(+), 22 deletions(-) diff --git a/src/app/core/data/feature-authorization/feature-authorization-guard/dso-page-some-feature.guard.spec.ts b/src/app/core/data/feature-authorization/feature-authorization-guard/dso-page-some-feature.guard.spec.ts index c4e567181da..ff81343366e 100644 --- a/src/app/core/data/feature-authorization/feature-authorization-guard/dso-page-some-feature.guard.spec.ts +++ b/src/app/core/data/feature-authorization/feature-authorization-guard/dso-page-some-feature.guard.spec.ts @@ -4,12 +4,17 @@ import { RemoteData } from '../../remote-data'; import { Observable, of as observableOf } from 'rxjs'; import { DSpaceObject } from '../../../shared/dspace-object.model'; import { AuthService } from '../../../auth/auth.service'; -import { dsoPageSomeFeatureGuard } from './dso-page-some-feature.guard'; +import { + dsoPageSomeFeatureGuard, + defaultDSOGetObjectUrl, + getRouteWithDSOId +} from './dso-page-some-feature.guard'; import { TestBed } from '@angular/core/testing'; import { FeatureID } from '../feature-id'; +import { createSuccessfulRemoteDataObject$ } from '../../../../shared/remote-data.utils'; -describe('DsoPageSomeFeatureGuard', () => { +describe('dsoPageSomeFeatureGuard and its functions', () => { let authorizationService: AuthorizationDataService; let router: Router; let authService: AuthService; @@ -25,16 +30,14 @@ describe('DsoPageSomeFeatureGuard', () => { object = { self: 'test-selflink' } as DSpaceObject; - + featureIds = [FeatureID.LoginOnBehalfOf, FeatureID.CanDelete]; authorizationService = jasmine.createSpyObj('authorizationService', { isAuthorized: observableOf(true) }); router = jasmine.createSpyObj('router', { parseUrl: {} }); - // resolver = jasmine.createSpyObj('resolver', { - // resolve: createSuccessfulRemoteDataObject$(object) - // }); + resolver = () => createSuccessfulRemoteDataObject$(object); authService = jasmine.createSpyObj('authService', { isAuthenticated: observableOf(true) }); @@ -63,33 +66,38 @@ describe('DsoPageSomeFeatureGuard', () => { init(); }); - describe('getObjectUrl', () => { + + describe('defaultDSOGetObjectUrl', () => { it('should return the resolved object\'s selflink', (done) => { + defaultDSOGetObjectUrl(resolver)(route, undefined).subscribe((selflink) => { + expect(selflink).toEqual(object.self); + done(); + }); + }); + }) + describe('getRouteWithDSOId', () => { + it('should return the route that has the UUID of the DSO', () => { + const foundRoute = getRouteWithDSOId(route); + expect(foundRoute).toBe(parentRoute); + }); + }) + + + describe('dsoPageSomeFeatureGuard', () => { + it('should call authorizationService.isAuthenticated with the appropriate arguments', (done) => { const result$ = TestBed.runInInjectionContext(() => { return dsoPageSomeFeatureGuard( () => resolver, () => observableOf(featureIds) )(route, { url: 'current-url' } as any); }) as Observable; - console.log('result$', result$); - result$.subscribe(() => { - expect(authorizationService.isAuthorized).toHaveBeenCalledWith(featureIds[0]); + featureIds.forEach((featureId: FeatureID) => { + expect(authorizationService.isAuthorized).toHaveBeenCalledWith(featureId, object.self, undefined); + }); done(); }); - - // guard.getObjectUrl(route, undefined).subscribe((selflink) => { - // expect(selflink).toEqual(object.self); - // done(); - // }); }); }); - - // describe('getRouteWithDSOId', () => { - // it('should return the route that has the UUID of the DSO', () => { - // const foundRoute = (guard as any).getRouteWithDSOId(route); - // expect(foundRoute).toBe(parentRoute); - // }); - // }); }); From 45dac71e9b550ba893707335b301bf78b9dee3cd Mon Sep 17 00:00:00 2001 From: Zahraa Chreim Date: Thu, 2 May 2024 14:06:48 +0300 Subject: [PATCH 07/17] 114645: Add export in dsoPageSomeFeatureGuard and Fix some Lint issues --- .../dso-page-some-feature.guard.spec.ts | 5 ++--- .../dso-page-some-feature.guard.ts | 4 ++-- 2 files changed, 4 insertions(+), 5 deletions(-) diff --git a/src/app/core/data/feature-authorization/feature-authorization-guard/dso-page-some-feature.guard.spec.ts b/src/app/core/data/feature-authorization/feature-authorization-guard/dso-page-some-feature.guard.spec.ts index ff81343366e..ef500598a01 100644 --- a/src/app/core/data/feature-authorization/feature-authorization-guard/dso-page-some-feature.guard.spec.ts +++ b/src/app/core/data/feature-authorization/feature-authorization-guard/dso-page-some-feature.guard.spec.ts @@ -18,7 +18,6 @@ describe('dsoPageSomeFeatureGuard and its functions', () => { let authorizationService: AuthorizationDataService; let router: Router; let authService: AuthService; - // let resolver: Resolve>; let resolver: ResolveFn>>; let object: DSpaceObject; let route; @@ -74,14 +73,14 @@ describe('dsoPageSomeFeatureGuard and its functions', () => { done(); }); }); - }) + }); describe('getRouteWithDSOId', () => { it('should return the route that has the UUID of the DSO', () => { const foundRoute = getRouteWithDSOId(route); expect(foundRoute).toBe(parentRoute); }); - }) + }); describe('dsoPageSomeFeatureGuard', () => { diff --git a/src/app/core/data/feature-authorization/feature-authorization-guard/dso-page-some-feature.guard.ts b/src/app/core/data/feature-authorization/feature-authorization-guard/dso-page-some-feature.guard.ts index 165edd7f5bb..e920687ecf6 100644 --- a/src/app/core/data/feature-authorization/feature-authorization-guard/dso-page-some-feature.guard.ts +++ b/src/app/core/data/feature-authorization/feature-authorization-guard/dso-page-some-feature.guard.ts @@ -19,7 +19,7 @@ export declare type DSOGetObjectURlFn = (resolve: Resolv * Method to resolve resolve (parent) route that contains the UUID of the DSO * @param route The current route */ -const getRouteWithDSOId = (route: ActivatedRouteSnapshot): ActivatedRouteSnapshot => { +export const getRouteWithDSOId = (route: ActivatedRouteSnapshot): ActivatedRouteSnapshot => { let routeWithDSOId = route; while (hasNoValue(routeWithDSOId.params.id) && hasValue(routeWithDSOId.parent)) { routeWithDSOId = routeWithDSOId.parent; @@ -29,7 +29,7 @@ const getRouteWithDSOId = (route: ActivatedRouteSnapshot): ActivatedRouteSnapsho -const defaultDSOGetObjectUrl: DSOGetObjectURlFn = (resolve: ResolveFn>>): StringGuardParamFn => { +export const defaultDSOGetObjectUrl: DSOGetObjectURlFn = (resolve: ResolveFn>>): StringGuardParamFn => { return (route: ActivatedRouteSnapshot, state: RouterStateSnapshot): Observable => { const routeWithObjectID = getRouteWithDSOId(route); return (resolve(routeWithObjectID, state) as Observable>).pipe( From 08028dc15286a89aadd3747ded5f0565801e1ccf Mon Sep 17 00:00:00 2001 From: Zahraa Chreim Date: Thu, 2 May 2024 14:21:34 +0300 Subject: [PATCH 08/17] 114645: Add tests for dsoPageSingleFeatureGuard --- .../dso-page-single-feature.guard.spec.ts | 33 +++++++++++-------- 1 file changed, 20 insertions(+), 13 deletions(-) diff --git a/src/app/core/data/feature-authorization/feature-authorization-guard/dso-page-single-feature.guard.spec.ts b/src/app/core/data/feature-authorization/feature-authorization-guard/dso-page-single-feature.guard.spec.ts index 0d967fc03d1..dac5af9c48d 100644 --- a/src/app/core/data/feature-authorization/feature-authorization-guard/dso-page-single-feature.guard.spec.ts +++ b/src/app/core/data/feature-authorization/feature-authorization-guard/dso-page-single-feature.guard.spec.ts @@ -8,6 +8,7 @@ import { AuthService } from '../../../auth/auth.service'; import { TestBed } from '@angular/core/testing'; import { dsoPageSingleFeatureGuard } from './dso-page-single-feature.guard'; import { FeatureID } from '../feature-id'; +import { defaultDSOGetObjectUrl, getRouteWithDSOId } from './dso-page-some-feature.guard'; describe('DsoPageSingleFeatureGuard', () => { @@ -15,7 +16,6 @@ describe('DsoPageSingleFeatureGuard', () => { let router: Router; let authService: AuthService; let resolver: ResolveFn>>; - // let resolver: jasmine.SpyObj<() => Observable>>; let object: DSpaceObject; let route; let parentRoute; @@ -33,11 +33,7 @@ describe('DsoPageSingleFeatureGuard', () => { router = jasmine.createSpyObj('router', { parseUrl: {} }); - resolver = jasmine.createSpyObj('resolver', { - resolve: createSuccessfulRemoteDataObject$(object) - }); - // resolver = jasmine.createSpy('resolver') // Mocking the resolver function - // .and.returnValue(observableOf({})); // Returning an observable directly + resolver = () => createSuccessfulRemoteDataObject$(object); authService = jasmine.createSpyObj('authService', { isAuthenticated: observableOf(true) }); @@ -67,7 +63,23 @@ describe('DsoPageSingleFeatureGuard', () => { init(); }); - describe('canActivate', () => { + describe('defaultDSOGetObjectUrl', () => { + it('should return the resolved object\'s selflink', (done) => { + defaultDSOGetObjectUrl(resolver)(route, undefined).subscribe((selflink) => { + expect(selflink).toEqual(object.self); + done(); + }); + }); + }); + + describe('getRouteWithDSOId', () => { + it('should return the route that has the UUID of the DSO', () => { + const foundRoute = getRouteWithDSOId(route); + expect(foundRoute).toBe(parentRoute); + }); + }); + + describe('dsoPageSingleFeatureGuard', () => { it('should call authorizationService.isAuthenticated with the appropriate arguments', (done) => { const result$ = TestBed.runInInjectionContext(() => { return dsoPageSingleFeatureGuard( @@ -75,15 +87,10 @@ describe('DsoPageSingleFeatureGuard', () => { )(route, { url: 'current-url' } as any); }) as Observable; - console.log('result$', result$); - result$.subscribe(() => { - expect(authorizationService.isAuthorized).toHaveBeenCalledWith(featureId); + expect(authorizationService.isAuthorized).toHaveBeenCalledWith(featureId, object.self, undefined); done(); }); - - // expect(authorizationService.isAuthorized).toHaveBeenCalledWith(featureId); - // done(); }); }); }); From 4658ac8a2ec4c014b23b1464d216e2af2e1ef1dc Mon Sep 17 00:00:00 2001 From: Zahraa Chreim Date: Wed, 8 May 2024 09:19:04 +0300 Subject: [PATCH 09/17] 114858: Create and Add Guards to item&bitstream routes --- .../bitstream-page-authorizations.guard.ts | 21 +++++++++++++++++ .../bitstream-page-routing.module.ts | 3 ++- .../edit-item-page.routing.module.ts | 10 +++++++- .../edit-item-page/item-page-delete.guard.ts | 23 +++++++++++++++++++ .../item-page-edit-authorizations.guard.ts | 23 +++++++++++++++++++ .../edit-item-page/item-page-move.guard.ts | 23 +++++++++++++++++++ .../edit-item-page/item-page-private.guard.ts | 23 +++++++++++++++++++ 7 files changed, 124 insertions(+), 2 deletions(-) create mode 100644 src/app/bitstream-page/bitstream-page-authorizations.guard.ts create mode 100644 src/app/item-page/edit-item-page/item-page-delete.guard.ts create mode 100644 src/app/item-page/edit-item-page/item-page-edit-authorizations.guard.ts create mode 100644 src/app/item-page/edit-item-page/item-page-move.guard.ts create mode 100644 src/app/item-page/edit-item-page/item-page-private.guard.ts diff --git a/src/app/bitstream-page/bitstream-page-authorizations.guard.ts b/src/app/bitstream-page/bitstream-page-authorizations.guard.ts new file mode 100644 index 00000000000..9a14f120333 --- /dev/null +++ b/src/app/bitstream-page/bitstream-page-authorizations.guard.ts @@ -0,0 +1,21 @@ +import { inject } from '@angular/core'; +import { CanActivateFn, ResolveFn } from '@angular/router'; +import { Observable, of as observableOf } from 'rxjs'; +import { BitstreamPageResolver } from './bitstream-page.resolver'; +import { Bitstream } from '../core/shared/bitstream.model'; +import { dsoPageSingleFeatureGuard } from '../core/data/feature-authorization/feature-authorization-guard/dso-page-single-feature.guard'; +import { FeatureID } from '../core/data/feature-authorization/feature-id'; +import { RemoteData } from '../core/data/remote-data'; + +/** + * Guard for preventing unauthorized access to certain {@link Bitstream} pages requiring specific authorizations. + * Checks authorization rights for managing policies. + */ +export const bitstreamPageAuthorizationsGuard: CanActivateFn = + dsoPageSingleFeatureGuard( + () => { + const bitstreamPageResolver = inject(BitstreamPageResolver); + return bitstreamPageResolver.resolve as ResolveFn>>; + }, + () => observableOf(FeatureID.CanManagePolicies) + ); diff --git a/src/app/bitstream-page/bitstream-page-routing.module.ts b/src/app/bitstream-page/bitstream-page-routing.module.ts index 3960ccb7436..124c0d94dbe 100644 --- a/src/app/bitstream-page/bitstream-page-routing.module.ts +++ b/src/app/bitstream-page/bitstream-page-routing.module.ts @@ -13,6 +13,7 @@ import { BitstreamBreadcrumbResolver } from '../core/breadcrumbs/bitstream-bread import { BitstreamBreadcrumbsService } from '../core/breadcrumbs/bitstream-breadcrumbs.service'; import { I18nBreadcrumbResolver } from '../core/breadcrumbs/i18n-breadcrumb.resolver'; import { ThemedEditBitstreamPageComponent } from './edit-bitstream-page/themed-edit-bitstream-page.component'; +import { bitstreamPageAuthorizationsGuard } from './bitstream-page-authorizations.guard'; const EDIT_BITSTREAM_PATH = ':id/edit'; const EDIT_BITSTREAM_AUTHORIZATIONS_PATH = ':id/authorizations'; @@ -58,7 +59,7 @@ const EDIT_BITSTREAM_AUTHORIZATIONS_PATH = ':id/authorizations'; }, { path: EDIT_BITSTREAM_AUTHORIZATIONS_PATH, - + canActivate: [bitstreamPageAuthorizationsGuard], children: [ { path: 'create', diff --git a/src/app/item-page/edit-item-page/edit-item-page.routing.module.ts b/src/app/item-page/edit-item-page/edit-item-page.routing.module.ts index 6713c176633..da1ee39170d 100644 --- a/src/app/item-page/edit-item-page/edit-item-page.routing.module.ts +++ b/src/app/item-page/edit-item-page/edit-item-page.routing.module.ts @@ -42,6 +42,10 @@ import { itemPageWithdrawGuard } from './item-page-withdraw.guard'; import { itemPageReinstateGuard } from './item-page-reinstate.guard'; import { itemPageRegisterDoiGuard } from './item-page-register-doi.guard'; import { itemPageVersionHistoryGuard } from './item-page-version-history.guard'; +import { itemPagePrivateGuard } from './item-page-private.guard'; +import { itemPageMoveGuard } from './item-page-move.guard'; +import { itemPageDeleteGuard } from './item-page-delete.guard'; +import { itemPageEditAuthorizationsGuard } from './item-page-edit-authorizations.guard'; /** * Routing module that handles the routing for the Edit Item page administrator functionality @@ -142,6 +146,7 @@ import { itemPageVersionHistoryGuard } from './item-page-version-history.guard'; { path: ITEM_EDIT_PRIVATE_PATH, component: ItemPrivateComponent, + canActivate: [itemPagePrivateGuard] }, { path: ITEM_EDIT_PUBLIC_PATH, @@ -150,11 +155,13 @@ import { itemPageVersionHistoryGuard } from './item-page-version-history.guard'; { path: ITEM_EDIT_DELETE_PATH, component: ItemDeleteComponent, + canActivate: [itemPageDeleteGuard] }, { path: ITEM_EDIT_MOVE_PATH, component: ItemMoveComponent, data: { title: 'item.edit.move.title' }, + canActivate: [itemPageMoveGuard] }, { path: ITEM_EDIT_REGISTER_DOI_PATH, @@ -179,7 +186,8 @@ import { itemPageVersionHistoryGuard } from './item-page-version-history.guard'; resourcePolicy: ResourcePolicyResolver }, component: ResourcePolicyEditComponent, - data: { title: 'resource-policies.edit.page.title' } + data: { title: 'resource-policies.edit.page.title' }, + canActivate: [itemPageEditAuthorizationsGuard] }, { path: '', diff --git a/src/app/item-page/edit-item-page/item-page-delete.guard.ts b/src/app/item-page/edit-item-page/item-page-delete.guard.ts new file mode 100644 index 00000000000..37cb4d781d5 --- /dev/null +++ b/src/app/item-page/edit-item-page/item-page-delete.guard.ts @@ -0,0 +1,23 @@ +import { inject } from '@angular/core'; +import { + dsoPageSingleFeatureGuard +} from '../../core/data/feature-authorization/feature-authorization-guard/dso-page-single-feature.guard'; +import { Item } from '../../core/shared/item.model'; +import { ItemPageResolver } from '../item-page.resolver'; +import { CanActivateFn, ResolveFn } from '@angular/router'; +import { Observable, of as observableOf } from 'rxjs'; +import { FeatureID } from '../../core/data/feature-authorization/feature-id'; +import { RemoteData } from '../../core/data/remote-data'; + +/** + * Guard for preventing unauthorized access to certain {@link Item} pages requiring specific authorizations. + * Checks authorization rights for deleting items. + */ +export const itemPageDeleteGuard: CanActivateFn = + dsoPageSingleFeatureGuard( + () => { + const itemPageResolver = inject(ItemPageResolver); + return itemPageResolver.resolve as ResolveFn>>; + }, + () => observableOf(FeatureID.CanDelete) + ); diff --git a/src/app/item-page/edit-item-page/item-page-edit-authorizations.guard.ts b/src/app/item-page/edit-item-page/item-page-edit-authorizations.guard.ts new file mode 100644 index 00000000000..042967dbfb1 --- /dev/null +++ b/src/app/item-page/edit-item-page/item-page-edit-authorizations.guard.ts @@ -0,0 +1,23 @@ +import { inject } from '@angular/core'; +import { + dsoPageSingleFeatureGuard +} from '../../core/data/feature-authorization/feature-authorization-guard/dso-page-single-feature.guard'; +import { Item } from '../../core/shared/item.model'; +import { ItemPageResolver } from '../item-page.resolver'; +import { CanActivateFn, ResolveFn } from '@angular/router'; +import { Observable, of as observableOf } from 'rxjs'; +import { FeatureID } from '../../core/data/feature-authorization/feature-id'; +import { RemoteData } from '../../core/data/remote-data'; + +/** + * Guard for preventing unauthorized access to certain {@link Item} pages requiring specific authorizations. + * Checks authorization rights for managing policies. + */ +export const itemPageEditAuthorizationsGuard: CanActivateFn = + dsoPageSingleFeatureGuard( + () => { + const itemPageResolver = inject(ItemPageResolver); + return itemPageResolver.resolve as ResolveFn>>; + }, + () => observableOf(FeatureID.CanManagePolicies) + ); diff --git a/src/app/item-page/edit-item-page/item-page-move.guard.ts b/src/app/item-page/edit-item-page/item-page-move.guard.ts new file mode 100644 index 00000000000..06c71738b76 --- /dev/null +++ b/src/app/item-page/edit-item-page/item-page-move.guard.ts @@ -0,0 +1,23 @@ +import { inject } from '@angular/core'; +import { + dsoPageSingleFeatureGuard +} from '../../core/data/feature-authorization/feature-authorization-guard/dso-page-single-feature.guard'; +import { Item } from '../../core/shared/item.model'; +import { ItemPageResolver } from '../item-page.resolver'; +import { CanActivateFn, ResolveFn } from '@angular/router'; +import { Observable, of as observableOf } from 'rxjs'; +import { FeatureID } from '../../core/data/feature-authorization/feature-id'; +import { RemoteData } from '../../core/data/remote-data'; + +/** + * Guard for preventing unauthorized access to certain {@link Item} pages requiring specific authorizations. + * Checks authorization rights for moving items. + */ +export const itemPageMoveGuard: CanActivateFn = + dsoPageSingleFeatureGuard( + () => { + const itemPageResolver = inject(ItemPageResolver); + return itemPageResolver.resolve as ResolveFn>>; + }, + () => observableOf(FeatureID.CanMove) + ); diff --git a/src/app/item-page/edit-item-page/item-page-private.guard.ts b/src/app/item-page/edit-item-page/item-page-private.guard.ts new file mode 100644 index 00000000000..eacfa1f8745 --- /dev/null +++ b/src/app/item-page/edit-item-page/item-page-private.guard.ts @@ -0,0 +1,23 @@ +import { inject } from '@angular/core'; +import { + dsoPageSingleFeatureGuard +} from '../../core/data/feature-authorization/feature-authorization-guard/dso-page-single-feature.guard'; +import { Item } from '../../core/shared/item.model'; +import { ItemPageResolver } from '../item-page.resolver'; +import { CanActivateFn, ResolveFn } from '@angular/router'; +import { Observable, of as observableOf } from 'rxjs'; +import { FeatureID } from '../../core/data/feature-authorization/feature-id'; +import { RemoteData } from '../../core/data/remote-data'; + +/** + * Guard for preventing unauthorized access to certain {@link Item} pages requiring specific authorizations. + * Checks authorization rights for making items private. + */ +export const itemPagePrivateGuard: CanActivateFn = + dsoPageSingleFeatureGuard( + () => { + const itemPageResolver = inject(ItemPageResolver); + return itemPageResolver.resolve as ResolveFn>>; + }, + () => observableOf(FeatureID.CanMakePrivate) + ); From 1ac3edc3f4ed2c19e0776596792e67605bf86953 Mon Sep 17 00:00:00 2001 From: Zahraa Chreim Date: Wed, 8 May 2024 15:48:55 +0300 Subject: [PATCH 10/17] 114858: Port to 8.0 --- .../access-control/access-control-routes.ts | 20 ++++---- .../group-registry/group-page.guard.spec.ts | 32 ++++++++---- .../group-registry/group-page.guard.ts | 21 +++++--- .../admin-notify-dashboard-routes.ts | 10 ++-- src/app/app-routes.ts | 20 +++++--- .../bitstream-page-authorizations.guard.ts | 17 +++---- .../collection-page-administrator.guard.ts | 21 +++----- .../collection-page/collection-page-routes.ts | 9 ++-- .../edit-collection-page-routes.ts | 9 ++-- .../community-page-administrator.guard.ts | 21 +++----- .../community-page/community-page-routes.ts | 9 ++-- .../edit-community-page-routes.ts | 9 ++-- .../collection-administrator.guard.ts | 3 +- .../community-administrator.guard.ts | 3 +- .../dso-page-single-feature.guard.spec.ts | 47 +++++++++++------- .../dso-page-single-feature.guard.ts | 26 ++++++---- .../dso-page-some-feature.guard.spec.ts | 48 ++++++++++-------- .../dso-page-some-feature.guard.ts | 33 ++++++++----- .../group-administrator.guard.ts | 3 +- ...single-feature-authorization.guard.spec.ts | 20 ++++++-- .../single-feature-authorization.guard.ts | 18 +++++-- .../site-administrator.guard.ts | 3 +- .../site-register.guard.ts | 3 +- .../some-feature-authorization.guard.spec.ts | 26 +++++++--- .../some-feature-authorization.guard.ts | 23 ++++++--- .../statistics-administrator.guard.ts | 3 +- .../forgot-password-check-guard.guard.ts | 36 ++------------ src/app/health-page/health-page-routes.ts | 9 ++-- .../edit-item-page/edit-item-page-routes.ts | 49 +++++++++---------- .../item-page-access-control.guard.ts | 44 +++-------------- .../item-page-bitstreams.guard.ts | 21 +++----- .../item-page-collection-mapper.guard.ts | 21 +++----- .../edit-item-page/item-page-curate.guard.ts | 44 +++-------------- .../edit-item-page/item-page-delete.guard.ts | 21 +++----- .../item-page-edit-authorizations.guard.ts | 21 +++----- .../item-page-metadata.guard.ts | 21 +++----- .../edit-item-page/item-page-move.guard.ts | 21 +++----- .../edit-item-page/item-page-private.guard.ts | 21 +++----- .../item-page-register-doi.guard.ts | 21 +++----- .../item-page-reinstate.guard.ts | 21 +++----- .../item-page-relationships.guard.ts | 21 +++----- .../edit-item-page/item-page-status.guard.ts | 21 +++----- .../item-page-version-history.guard.ts | 21 +++----- .../item-page-withdraw.guard.ts | 21 +++----- .../item-page-administrator.guard.ts | 21 +++----- src/app/item-page/item-page-routes.ts | 9 ++-- .../item-page/orcid-page/orcid-page.guard.ts | 21 +++----- .../statistics-page/statistics-page-routes.ts | 15 +++--- .../system-wide-alert-routes.ts | 9 ++-- 49 files changed, 430 insertions(+), 557 deletions(-) diff --git a/src/app/access-control/access-control-routes.ts b/src/app/access-control/access-control-routes.ts index a7cce461ef0..ef36a19052c 100644 --- a/src/app/access-control/access-control-routes.ts +++ b/src/app/access-control/access-control-routes.ts @@ -9,8 +9,6 @@ import { } from '@ng-dynamic-forms/core'; import { i18nBreadcrumbResolver } from '../core/breadcrumbs/i18n-breadcrumb.resolver'; -import { GroupAdministratorGuard } from '../core/data/feature-authorization/feature-authorization-guard/group-administrator.guard'; -import { SiteAdministratorGuard } from '../core/data/feature-authorization/feature-authorization-guard/site-administrator.guard'; import { EPERSON_PATH, GROUP_PATH, @@ -20,8 +18,10 @@ import { EPeopleRegistryComponent } from './epeople-registry/epeople-registry.co import { EPersonFormComponent } from './epeople-registry/eperson-form/eperson-form.component'; import { EPersonResolver } from './epeople-registry/eperson-resolver.service'; import { GroupFormComponent } from './group-registry/group-form/group-form.component'; -import { GroupPageGuard } from './group-registry/group-page.guard'; import { GroupsRegistryComponent } from './group-registry/groups-registry.component'; +import { + siteAdministratorGuard +} from '../core/data/feature-authorization/feature-authorization-guard/site-administrator.guard'; /** * Condition for displaying error messages on email form field @@ -46,7 +46,7 @@ export const ROUTES: Route[] = [ }, providers, data: { title: 'admin.access-control.epeople.title', breadcrumbKey: 'admin.access-control.epeople' }, - canActivate: mapToCanActivate([SiteAdministratorGuard]), + canActivate: [siteAdministratorGuard], }, { path: `${EPERSON_PATH}/create`, @@ -56,7 +56,7 @@ export const ROUTES: Route[] = [ }, providers, data: { title: 'admin.access-control.epeople.add.title', breadcrumbKey: 'admin.access-control.epeople.add' }, - canActivate: mapToCanActivate([SiteAdministratorGuard]), + canActivate: [siteAdministratorGuard], }, { path: `${EPERSON_PATH}/:id/edit`, @@ -67,7 +67,7 @@ export const ROUTES: Route[] = [ }, providers, data: { title: 'admin.access-control.epeople.edit.title', breadcrumbKey: 'admin.access-control.epeople.edit' }, - canActivate: mapToCanActivate([SiteAdministratorGuard]), + canActivate: [siteAdministratorGuard], }, { path: GROUP_PATH, @@ -77,7 +77,7 @@ export const ROUTES: Route[] = [ }, providers, data: { title: 'admin.access-control.groups.title', breadcrumbKey: 'admin.access-control.groups' }, - canActivate: mapToCanActivate([GroupAdministratorGuard]), + canActivate: [siteAdministratorGuard], }, { path: `${GROUP_PATH}/create`, @@ -90,7 +90,7 @@ export const ROUTES: Route[] = [ title: 'admin.access-control.groups.title.addGroup', breadcrumbKey: 'admin.access-control.groups.addGroup', }, - canActivate: mapToCanActivate([GroupAdministratorGuard]), + canActivate: [siteAdministratorGuard], }, { path: `${GROUP_PATH}/:groupId/edit`, @@ -103,7 +103,7 @@ export const ROUTES: Route[] = [ title: 'admin.access-control.groups.title.singleGroup', breadcrumbKey: 'admin.access-control.groups.singleGroup', }, - canActivate: mapToCanActivate([GroupPageGuard]), + canActivate: [siteAdministratorGuard], }, { path: 'bulk-access', @@ -112,6 +112,6 @@ export const ROUTES: Route[] = [ breadcrumb: i18nBreadcrumbResolver, }, data: { title: 'admin.access-control.bulk-access.title', breadcrumbKey: 'admin.access-control.bulk-access' }, - canActivate: mapToCanActivate([SiteAdministratorGuard]), + canActivate: [siteAdministratorGuard], }, ]; diff --git a/src/app/access-control/group-registry/group-page.guard.spec.ts b/src/app/access-control/group-registry/group-page.guard.spec.ts index 9a301faab0f..3024e42d64a 100644 --- a/src/app/access-control/group-registry/group-page.guard.spec.ts +++ b/src/app/access-control/group-registry/group-page.guard.spec.ts @@ -1,11 +1,23 @@ -import { HALEndpointService } from '../../core/shared/hal-endpoint.service'; -import { AuthorizationDataService } from '../../core/data/feature-authorization/authorization-data.service'; -import { ActivatedRouteSnapshot, Router, UrlTree } from '@angular/router'; -import { of as observableOf, Observable } from 'rxjs'; +import { + TestBed, + waitForAsync, +} from '@angular/core/testing'; +import { + ActivatedRouteSnapshot, + Router, + UrlTree, +} from '@angular/router'; +import { + Observable, + of as observableOf, +} from 'rxjs'; + import { AuthService } from '../../core/auth/auth.service'; -import { TestBed, waitForAsync } from '@angular/core/testing'; -import { groupPageGuard } from './group-page.guard'; +import { AuthorizationDataService } from '../../core/data/feature-authorization/authorization-data.service'; import { FeatureID } from '../../core/data/feature-authorization/feature-id'; +import { HALEndpointService } from '../../core/shared/hal-endpoint.service'; +import { groupPageGuard } from './group-page.guard'; + jasmine.DEFAULT_TIMEOUT_INTERVAL = 10000; // Increase timeout to 10 seconds describe('GroupPageGuard', () => { @@ -15,7 +27,7 @@ describe('GroupPageGuard', () => { const routeSnapshotWithGroupId = { params: { groupId: groupUuid, - } + }, } as unknown as ActivatedRouteSnapshot; let halEndpointService: HALEndpointService; @@ -42,7 +54,7 @@ describe('GroupPageGuard', () => { { provide: Router, useValue: router }, { provide: AuthService, useValue: authService }, { provide: HALEndpointService, useValue: halEndpointService }, - ] + ], }); } @@ -67,7 +79,7 @@ describe('GroupPageGuard', () => { result$.subscribe((result) => { expect(authorizationService.isAuthorized).toHaveBeenCalledWith( - FeatureID.CanManageGroup, groupEndpointUrl, undefined + FeatureID.CanManageGroup, groupEndpointUrl, undefined, ); expect(result).toBeTrue(); done(); @@ -87,7 +99,7 @@ describe('GroupPageGuard', () => { result$.subscribe((result) => { expect(authorizationService.isAuthorized).toHaveBeenCalledWith( - FeatureID.CanManageGroup, groupEndpointUrl, undefined + FeatureID.CanManageGroup, groupEndpointUrl, undefined, ); expect(result).not.toBeTrue(); done(); diff --git a/src/app/access-control/group-registry/group-page.guard.ts b/src/app/access-control/group-registry/group-page.guard.ts index ef49088a762..c52bed9c48b 100644 --- a/src/app/access-control/group-registry/group-page.guard.ts +++ b/src/app/access-control/group-registry/group-page.guard.ts @@ -1,22 +1,31 @@ -import { CanActivateFn, ActivatedRouteSnapshot, RouterStateSnapshot } from '@angular/router'; -import { Observable, of as observableOf } from 'rxjs'; import { inject } from '@angular/core'; +import { + ActivatedRouteSnapshot, + CanActivateFn, + RouterStateSnapshot, +} from '@angular/router'; +import { + Observable, + of as observableOf, +} from 'rxjs'; import { map } from 'rxjs/operators'; -import { HALEndpointService } from '../../core/shared/hal-endpoint.service'; + import { - StringGuardParamFn, someFeatureAuthorizationGuard + someFeatureAuthorizationGuard, + StringGuardParamFn, } from '../../core/data/feature-authorization/feature-authorization-guard/some-feature-authorization.guard'; import { FeatureID } from '../../core/data/feature-authorization/feature-id'; +import { HALEndpointService } from '../../core/shared/hal-endpoint.service'; const defaultGroupPageGetObjectUrl: StringGuardParamFn = ( route: ActivatedRouteSnapshot, - state: RouterStateSnapshot + state: RouterStateSnapshot, ): Observable => { const halEndpointService = inject(HALEndpointService); const groupsEndpoint = 'groups'; return halEndpointService.getEndpoint(groupsEndpoint).pipe( - map(groupsUrl => `${groupsUrl}/${route?.params?.groupId}`) + map(groupsUrl => `${groupsUrl}/${route?.params?.groupId}`), ); }; diff --git a/src/app/admin/admin-notify-dashboard/admin-notify-dashboard-routes.ts b/src/app/admin/admin-notify-dashboard/admin-notify-dashboard-routes.ts index c193148cc4d..b1fd827ba80 100644 --- a/src/app/admin/admin-notify-dashboard/admin-notify-dashboard-routes.ts +++ b/src/app/admin/admin-notify-dashboard/admin-notify-dashboard-routes.ts @@ -5,14 +5,16 @@ import { import { i18nBreadcrumbResolver } from '../../core/breadcrumbs/i18n-breadcrumb.resolver'; import { notifyInfoGuard } from '../../core/coar-notify/notify-info/notify-info.guard'; -import { SiteAdministratorGuard } from '../../core/data/feature-authorization/feature-authorization-guard/site-administrator.guard'; import { AdminNotifyDashboardComponent } from './admin-notify-dashboard.component'; import { AdminNotifyIncomingComponent } from './admin-notify-logs/admin-notify-incoming/admin-notify-incoming.component'; import { AdminNotifyOutgoingComponent } from './admin-notify-logs/admin-notify-outgoing/admin-notify-outgoing.component'; +import { + siteAdministratorGuard +} from '../../core/data/feature-authorization/feature-authorization-guard/site-administrator.guard'; export const ROUTES: Route[] = [ { - canActivate: [...mapToCanActivate([SiteAdministratorGuard]), notifyInfoGuard], + canActivate: [siteAdministratorGuard, notifyInfoGuard], path: '', resolve: { breadcrumb: i18nBreadcrumbResolver, @@ -30,7 +32,7 @@ export const ROUTES: Route[] = [ breadcrumb: i18nBreadcrumbResolver, }, component: AdminNotifyIncomingComponent, - canActivate: [...mapToCanActivate([SiteAdministratorGuard]), notifyInfoGuard], + canActivate: [siteAdministratorGuard, notifyInfoGuard], data: { title: 'admin.notify.dashboard.page.title', breadcrumbKey: 'admin.notify.dashboard', @@ -42,7 +44,7 @@ export const ROUTES: Route[] = [ breadcrumb: i18nBreadcrumbResolver, }, component: AdminNotifyOutgoingComponent, - canActivate: [...mapToCanActivate([SiteAdministratorGuard]), notifyInfoGuard], + canActivate: [siteAdministratorGuard, notifyInfoGuard], data: { title: 'admin.notify.dashboard.page.title', breadcrumbKey: 'admin.notify.dashboard', diff --git a/src/app/app-routes.ts b/src/app/app-routes.ts index 8b7f6acd470..f54d01301bd 100644 --- a/src/app/app-routes.ts +++ b/src/app/app-routes.ts @@ -26,12 +26,10 @@ import { COLLECTION_MODULE_PATH } from './collection-page/collection-page-routin import { COMMUNITY_MODULE_PATH } from './community-page/community-page-routing-paths'; import { authBlockingGuard } from './core/auth/auth-blocking.guard'; import { authenticatedGuard } from './core/auth/authenticated.guard'; -import { GroupAdministratorGuard } from './core/data/feature-authorization/feature-authorization-guard/group-administrator.guard'; -import { SiteAdministratorGuard } from './core/data/feature-authorization/feature-authorization-guard/site-administrator.guard'; -import { SiteRegisterGuard } from './core/data/feature-authorization/feature-authorization-guard/site-register.guard'; +import { siteRegisterGuard } from './core/data/feature-authorization/feature-authorization-guard/site-register.guard'; import { EndUserAgreementCurrentUserGuard } from './core/end-user-agreement/end-user-agreement-current-user.guard'; import { reloadGuard } from './core/reload/reload.guard'; -import { ForgotPasswordCheckGuard } from './core/rest-property/forgot-password-check-guard.guard'; +import { forgotPasswordCheckGuard } from './core/rest-property/forgot-password-check-guard.guard'; import { ServerCheckGuard } from './core/server-check/server-check.guard'; import { ThemedForbiddenComponent } from './forbidden/themed-forbidden.component'; import { ITEM_MODULE_PATH } from './item-page/item-page-routing-paths'; @@ -43,6 +41,12 @@ import { ThemedPageNotFoundComponent } from './pagenotfound/themed-pagenotfound. import { PROCESS_MODULE_PATH } from './process-page/process-page-routing.paths'; import { provideSubmissionState } from './submission/provide-submission-state'; import { SUGGESTION_MODULE_PATH } from './suggestions-page/suggestions-page-routing-paths'; +import { + siteAdministratorGuard +} from './core/data/feature-authorization/feature-authorization-guard/site-administrator.guard'; +import { + groupAdministratorGuard +} from './core/data/feature-authorization/feature-authorization-guard/group-administrator.guard'; export const APP_ROUTES: Route[] = [ { path: INTERNAL_SERVER_ERROR, component: ThemedPageInternalServerErrorComponent }, @@ -90,13 +94,13 @@ export const APP_ROUTES: Route[] = [ path: REGISTER_PATH, loadChildren: () => import('./register-page/register-page-routes') .then((m) => m.ROUTES), - canActivate: mapToCanActivate([SiteRegisterGuard]), + canActivate: [siteRegisterGuard], }, { path: FORGOT_PASSWORD_PATH, loadChildren: () => import('./forgot-password/forgot-password-routes') .then((m) => m.ROUTES), - canActivate: mapToCanActivate([EndUserAgreementCurrentUserGuard, ForgotPasswordCheckGuard]), + canActivate: [EndUserAgreementCurrentUserGuard, forgotPasswordCheckGuard], }, { path: COMMUNITY_MODULE_PATH, @@ -157,7 +161,7 @@ export const APP_ROUTES: Route[] = [ path: ADMIN_MODULE_PATH, loadChildren: () => import('./admin/admin-routes') .then((m) => m.ROUTES), - canActivate: mapToCanActivate([SiteAdministratorGuard, EndUserAgreementCurrentUserGuard]), + canActivate: [siteAdministratorGuard, EndUserAgreementCurrentUserGuard], }, { path: NOTIFICATIONS_MODULE_PATH, @@ -250,7 +254,7 @@ export const APP_ROUTES: Route[] = [ { path: ACCESS_CONTROL_MODULE_PATH, loadChildren: () => import('./access-control/access-control-routes').then((m) => m.ROUTES), - canActivate: mapToCanActivate([GroupAdministratorGuard, EndUserAgreementCurrentUserGuard]), + canActivate: [groupAdministratorGuard, EndUserAgreementCurrentUserGuard], }, { path: 'subscriptions', diff --git a/src/app/bitstream-page/bitstream-page-authorizations.guard.ts b/src/app/bitstream-page/bitstream-page-authorizations.guard.ts index 9a14f120333..134693977f9 100644 --- a/src/app/bitstream-page/bitstream-page-authorizations.guard.ts +++ b/src/app/bitstream-page/bitstream-page-authorizations.guard.ts @@ -1,11 +1,9 @@ -import { inject } from '@angular/core'; -import { CanActivateFn, ResolveFn } from '@angular/router'; -import { Observable, of as observableOf } from 'rxjs'; -import { BitstreamPageResolver } from './bitstream-page.resolver'; -import { Bitstream } from '../core/shared/bitstream.model'; +import { CanActivateFn } from '@angular/router'; +import { of as observableOf } from 'rxjs'; + import { dsoPageSingleFeatureGuard } from '../core/data/feature-authorization/feature-authorization-guard/dso-page-single-feature.guard'; import { FeatureID } from '../core/data/feature-authorization/feature-id'; -import { RemoteData } from '../core/data/remote-data'; +import { bitstreamPageResolver } from './bitstream-page.resolver'; /** * Guard for preventing unauthorized access to certain {@link Bitstream} pages requiring specific authorizations. @@ -13,9 +11,6 @@ import { RemoteData } from '../core/data/remote-data'; */ export const bitstreamPageAuthorizationsGuard: CanActivateFn = dsoPageSingleFeatureGuard( - () => { - const bitstreamPageResolver = inject(BitstreamPageResolver); - return bitstreamPageResolver.resolve as ResolveFn>>; - }, - () => observableOf(FeatureID.CanManagePolicies) + () => bitstreamPageResolver, + () => observableOf(FeatureID.CanManagePolicies), ); diff --git a/src/app/collection-page/collection-page-administrator.guard.ts b/src/app/collection-page/collection-page-administrator.guard.ts index ee48d9fb0ae..30edc72fc6a 100644 --- a/src/app/collection-page/collection-page-administrator.guard.ts +++ b/src/app/collection-page/collection-page-administrator.guard.ts @@ -1,13 +1,9 @@ -import { inject } from '@angular/core'; -import { CanActivateFn, ResolveFn } from '@angular/router'; -import { Observable, of as observableOf } from 'rxjs'; -import { - dsoPageSingleFeatureGuard -} from '../core/data/feature-authorization/feature-authorization-guard/dso-page-single-feature.guard'; +import { CanActivateFn } from '@angular/router'; +import { of as observableOf } from 'rxjs'; + +import { dsoPageSingleFeatureGuard } from '../core/data/feature-authorization/feature-authorization-guard/dso-page-single-feature.guard'; import { FeatureID } from '../core/data/feature-authorization/feature-id'; -import { RemoteData } from '../core/data/remote-data'; -import { CollectionPageResolver } from './collection-page.resolver'; -import { Collection } from '../core/shared/collection.model'; +import { collectionPageResolver } from './collection-page.resolver'; /** * Guard for preventing unauthorized access to certain {@link Collection} pages requiring administrator rights @@ -15,9 +11,6 @@ import { Collection } from '../core/shared/collection.model'; */ export const collectionPageAdministratorGuard: CanActivateFn = dsoPageSingleFeatureGuard( - () => { - const collectionPageResolver = inject(CollectionPageResolver); - return collectionPageResolver.resolve as ResolveFn>>; - }, - () => observableOf(FeatureID.AdministratorOf) + () => collectionPageResolver, + () => observableOf(FeatureID.AdministratorOf), ); diff --git a/src/app/collection-page/collection-page-routes.ts b/src/app/collection-page/collection-page-routes.ts index 889b910d6ae..f2dadc3fbe0 100644 --- a/src/app/collection-page/collection-page-routes.ts +++ b/src/app/collection-page/collection-page-routes.ts @@ -1,7 +1,4 @@ -import { - mapToCanActivate, - Route, -} from '@angular/router'; +import { Route } from '@angular/router'; import { browseByGuard } from '../browse-by/browse-by-guard'; import { browseByI18nBreadcrumbResolver } from '../browse-by/browse-by-i18n-breadcrumb.resolver'; @@ -15,7 +12,7 @@ import { dsoEditMenuResolver } from '../shared/dso-page/dso-edit-menu.resolver'; import { LinkMenuItemModel } from '../shared/menu/menu-item/models/link.model'; import { MenuItemType } from '../shared/menu/menu-item-type.model'; import { collectionPageResolver } from './collection-page.resolver'; -import { CollectionPageAdministratorGuard } from './collection-page-administrator.guard'; +import { collectionPageAdministratorGuard } from './collection-page-administrator.guard'; import { COLLECTION_CREATE_PATH, COLLECTION_EDIT_PATH, @@ -65,7 +62,7 @@ export const ROUTES: Route[] = [ path: COLLECTION_EDIT_PATH, loadChildren: () => import('./edit-collection-page/edit-collection-page-routes') .then((m) => m.ROUTES), - canActivate: mapToCanActivate([CollectionPageAdministratorGuard]), + canActivate: [collectionPageAdministratorGuard], }, { path: 'delete', diff --git a/src/app/collection-page/edit-collection-page/edit-collection-page-routes.ts b/src/app/collection-page/edit-collection-page/edit-collection-page-routes.ts index cf550c32233..19dbaa616b2 100644 --- a/src/app/collection-page/edit-collection-page/edit-collection-page-routes.ts +++ b/src/app/collection-page/edit-collection-page/edit-collection-page-routes.ts @@ -1,10 +1,7 @@ -import { - mapToCanActivate, - Route, -} from '@angular/router'; +import { Route } from '@angular/router'; import { i18nBreadcrumbResolver } from '../../core/breadcrumbs/i18n-breadcrumb.resolver'; -import { CollectionAdministratorGuard } from '../../core/data/feature-authorization/feature-authorization-guard/collection-administrator.guard'; +import { collectionAdministratorGuard } from '../../core/data/feature-authorization/feature-authorization-guard/collection-administrator.guard'; import { ResourcePolicyCreateComponent } from '../../shared/resource-policies/create/resource-policy-create.component'; import { ResourcePolicyEditComponent } from '../../shared/resource-policies/edit/resource-policy-edit.component'; import { resourcePolicyResolver } from '../../shared/resource-policies/resolvers/resource-policy.resolver'; @@ -30,7 +27,7 @@ export const ROUTES: Route[] = [ }, data: { breadcrumbKey: 'collection.edit' }, component: EditCollectionPageComponent, - canActivate: mapToCanActivate([CollectionAdministratorGuard]), + canActivate: [collectionAdministratorGuard], children: [ { path: '', diff --git a/src/app/community-page/community-page-administrator.guard.ts b/src/app/community-page/community-page-administrator.guard.ts index c41a7f0c4ce..ecbc9b86c0c 100644 --- a/src/app/community-page/community-page-administrator.guard.ts +++ b/src/app/community-page/community-page-administrator.guard.ts @@ -1,13 +1,9 @@ -import { inject } from '@angular/core'; -import { CanActivateFn, ResolveFn } from '@angular/router'; -import { Observable, of as observableOf } from 'rxjs'; -import { - dsoPageSingleFeatureGuard -} from '../core/data/feature-authorization/feature-authorization-guard/dso-page-single-feature.guard'; +import { CanActivateFn } from '@angular/router'; +import { of as observableOf } from 'rxjs'; + +import { dsoPageSingleFeatureGuard } from '../core/data/feature-authorization/feature-authorization-guard/dso-page-single-feature.guard'; import { FeatureID } from '../core/data/feature-authorization/feature-id'; -import { RemoteData } from '../core/data/remote-data'; -import { Community } from '../core/shared/community.model'; -import { CommunityPageResolver } from './community-page.resolver'; +import { communityPageResolver } from './community-page.resolver'; /** * Guard for preventing unauthorized access to certain {@link Community} pages requiring administrator rights @@ -15,9 +11,6 @@ import { CommunityPageResolver } from './community-page.resolver'; */ export const communityPageAdministratorGuard: CanActivateFn = dsoPageSingleFeatureGuard( - () => { - const communityPageResolver = inject(CommunityPageResolver); - return communityPageResolver.resolve as ResolveFn>>; - }, - () => observableOf(FeatureID.AdministratorOf) + () => communityPageResolver, + () => observableOf(FeatureID.AdministratorOf), ); diff --git a/src/app/community-page/community-page-routes.ts b/src/app/community-page/community-page-routes.ts index 656b96c311e..d9505c53b13 100644 --- a/src/app/community-page/community-page-routes.ts +++ b/src/app/community-page/community-page-routes.ts @@ -1,7 +1,4 @@ -import { - mapToCanActivate, - Route, -} from '@angular/router'; +import { Route } from '@angular/router'; import { browseByGuard } from '../browse-by/browse-by-guard'; import { browseByI18nBreadcrumbResolver } from '../browse-by/browse-by-i18n-breadcrumb.resolver'; @@ -14,7 +11,7 @@ import { dsoEditMenuResolver } from '../shared/dso-page/dso-edit-menu.resolver'; import { LinkMenuItemModel } from '../shared/menu/menu-item/models/link.model'; import { MenuItemType } from '../shared/menu/menu-item-type.model'; import { communityPageResolver } from './community-page.resolver'; -import { CommunityPageAdministratorGuard } from './community-page-administrator.guard'; +import { communityPageAdministratorGuard } from './community-page-administrator.guard'; import { COMMUNITY_CREATE_PATH, COMMUNITY_EDIT_PATH, @@ -62,7 +59,7 @@ export const ROUTES: Route[] = [ path: COMMUNITY_EDIT_PATH, loadChildren: () => import('./edit-community-page/edit-community-page-routes') .then((m) => m.ROUTES), - canActivate: mapToCanActivate([CommunityPageAdministratorGuard]), + canActivate: [communityPageAdministratorGuard], }, { path: 'delete', diff --git a/src/app/community-page/edit-community-page/edit-community-page-routes.ts b/src/app/community-page/edit-community-page/edit-community-page-routes.ts index a15312a216e..2402c2037d5 100644 --- a/src/app/community-page/edit-community-page/edit-community-page-routes.ts +++ b/src/app/community-page/edit-community-page/edit-community-page-routes.ts @@ -1,10 +1,7 @@ -import { - mapToCanActivate, - Route, -} from '@angular/router'; +import { Route } from '@angular/router'; import { i18nBreadcrumbResolver } from '../../core/breadcrumbs/i18n-breadcrumb.resolver'; -import { CommunityAdministratorGuard } from '../../core/data/feature-authorization/feature-authorization-guard/community-administrator.guard'; +import { communityAdministratorGuard } from '../../core/data/feature-authorization/feature-authorization-guard/community-administrator.guard'; import { ResourcePolicyCreateComponent } from '../../shared/resource-policies/create/resource-policy-create.component'; import { ResourcePolicyEditComponent } from '../../shared/resource-policies/edit/resource-policy-edit.component'; import { resourcePolicyResolver } from '../../shared/resource-policies/resolvers/resource-policy.resolver'; @@ -28,7 +25,7 @@ export const ROUTES: Route[] = [ }, data: { breadcrumbKey: 'community.edit' }, component: EditCommunityPageComponent, - canActivate: mapToCanActivate([CommunityAdministratorGuard]), + canActivate: [communityAdministratorGuard], children: [ { path: '', diff --git a/src/app/core/data/feature-authorization/feature-authorization-guard/collection-administrator.guard.ts b/src/app/core/data/feature-authorization/feature-authorization-guard/collection-administrator.guard.ts index 4eb5ca15668..1b1b4a9d6ca 100644 --- a/src/app/core/data/feature-authorization/feature-authorization-guard/collection-administrator.guard.ts +++ b/src/app/core/data/feature-authorization/feature-authorization-guard/collection-administrator.guard.ts @@ -1,7 +1,8 @@ import { CanActivateFn } from '@angular/router'; -import { singleFeatureAuthorizationGuard } from './single-feature-authorization.guard'; import { of as observableOf } from 'rxjs'; + import { FeatureID } from '../feature-id'; +import { singleFeatureAuthorizationGuard } from './single-feature-authorization.guard'; /** * Prevent unauthorized activating and loading of routes when the current authenticated user diff --git a/src/app/core/data/feature-authorization/feature-authorization-guard/community-administrator.guard.ts b/src/app/core/data/feature-authorization/feature-authorization-guard/community-administrator.guard.ts index 4b3630db235..6d7dac314e3 100644 --- a/src/app/core/data/feature-authorization/feature-authorization-guard/community-administrator.guard.ts +++ b/src/app/core/data/feature-authorization/feature-authorization-guard/community-administrator.guard.ts @@ -1,7 +1,8 @@ import { CanActivateFn } from '@angular/router'; -import { singleFeatureAuthorizationGuard } from './single-feature-authorization.guard'; import { of as observableOf } from 'rxjs'; + import { FeatureID } from '../feature-id'; +import { singleFeatureAuthorizationGuard } from './single-feature-authorization.guard'; /** * Prevent unauthorized activating and loading of routes when the current authenticated user diff --git a/src/app/core/data/feature-authorization/feature-authorization-guard/dso-page-single-feature.guard.spec.ts b/src/app/core/data/feature-authorization/feature-authorization-guard/dso-page-single-feature.guard.spec.ts index dac5af9c48d..18292bb943b 100644 --- a/src/app/core/data/feature-authorization/feature-authorization-guard/dso-page-single-feature.guard.spec.ts +++ b/src/app/core/data/feature-authorization/feature-authorization-guard/dso-page-single-feature.guard.spec.ts @@ -1,21 +1,32 @@ -import { AuthorizationDataService } from '../authorization-data.service'; -import { Router, UrlTree, ResolveFn } from '@angular/router'; -import { RemoteData } from '../../remote-data'; -import { Observable, of as observableOf } from 'rxjs'; +import { TestBed } from '@angular/core/testing'; +import { + ResolveFn, + Router, + UrlTree, +} from '@angular/router'; +import { + Observable, + of as observableOf, +} from 'rxjs'; + import { createSuccessfulRemoteDataObject$ } from '../../../../shared/remote-data.utils'; -import { DSpaceObject } from '../../../shared/dspace-object.model'; import { AuthService } from '../../../auth/auth.service'; -import { TestBed } from '@angular/core/testing'; -import { dsoPageSingleFeatureGuard } from './dso-page-single-feature.guard'; +import { DSpaceObject } from '../../../shared/dspace-object.model'; +import { RemoteData } from '../../remote-data'; +import { AuthorizationDataService } from '../authorization-data.service'; import { FeatureID } from '../feature-id'; -import { defaultDSOGetObjectUrl, getRouteWithDSOId } from './dso-page-some-feature.guard'; +import { dsoPageSingleFeatureGuard } from './dso-page-single-feature.guard'; +import { + defaultDSOGetObjectUrl, + getRouteWithDSOId, +} from './dso-page-some-feature.guard'; describe('DsoPageSingleFeatureGuard', () => { let authorizationService: AuthorizationDataService; let router: Router; let authService: AuthService; - let resolver: ResolveFn>>; + let resolver: ResolveFn>; let object: DSpaceObject; let route; let parentRoute; @@ -24,28 +35,28 @@ describe('DsoPageSingleFeatureGuard', () => { function init() { object = { - self: 'test-selflink' + self: 'test-selflink', } as DSpaceObject; authorizationService = jasmine.createSpyObj('authorizationService', { - isAuthorized: observableOf(true) + isAuthorized: observableOf(true), }); router = jasmine.createSpyObj('router', { - parseUrl: {} + parseUrl: {}, }); resolver = () => createSuccessfulRemoteDataObject$(object); authService = jasmine.createSpyObj('authService', { - isAuthenticated: observableOf(true) + isAuthenticated: observableOf(true), }); parentRoute = { params: { - id: '3e1a5327-dabb-41ff-af93-e6cab9d032f0' - } + id: '3e1a5327-dabb-41ff-af93-e6cab9d032f0', + }, }; route = { params: { }, - parent: parentRoute + parent: parentRoute, }; featureId = FeatureID.LoginOnBehalfOf; @@ -55,7 +66,7 @@ describe('DsoPageSingleFeatureGuard', () => { { provide: AuthorizationDataService, useValue: authorizationService }, { provide: Router, useValue: router }, { provide: AuthService, useValue: authService }, - ] + ], }); } @@ -83,7 +94,7 @@ describe('DsoPageSingleFeatureGuard', () => { it('should call authorizationService.isAuthenticated with the appropriate arguments', (done) => { const result$ = TestBed.runInInjectionContext(() => { return dsoPageSingleFeatureGuard( - () => resolver, () => observableOf(featureId) + () => resolver, () => observableOf(featureId), )(route, { url: 'current-url' } as any); }) as Observable; diff --git a/src/app/core/data/feature-authorization/feature-authorization-guard/dso-page-single-feature.guard.ts b/src/app/core/data/feature-authorization/feature-authorization-guard/dso-page-single-feature.guard.ts index 0055d983d39..5073a386532 100644 --- a/src/app/core/data/feature-authorization/feature-authorization-guard/dso-page-single-feature.guard.ts +++ b/src/app/core/data/feature-authorization/feature-authorization-guard/dso-page-single-feature.guard.ts @@ -1,21 +1,27 @@ -import { CanActivateFn, ActivatedRouteSnapshot, RouterStateSnapshot, ResolveFn } from '@angular/router'; -import { SingleFeatureGuardParamFn } from './single-feature-authorization.guard'; -import { dsoPageSomeFeatureGuard } from './dso-page-some-feature.guard'; +import { + ActivatedRouteSnapshot, + CanActivateFn, + ResolveFn, + RouterStateSnapshot, +} from '@angular/router'; import { Observable } from 'rxjs'; -import { FeatureID } from '../feature-id'; import { map } from 'rxjs/operators'; + import { DSpaceObject } from '../../../shared/dspace-object.model'; import { RemoteData } from '../../remote-data'; +import { FeatureID } from '../feature-id'; +import { dsoPageSomeFeatureGuard } from './dso-page-some-feature.guard'; +import { SingleFeatureGuardParamFn } from './single-feature-authorization.guard'; /** * Abstract Guard for preventing unauthorized access to {@link DSpaceObject} pages that require rights for a specific feature * This guard utilizes a resolver to retrieve the relevant object to check authorizations for */ export const dsoPageSingleFeatureGuard = ( - getResolveFn: () => ResolveFn>>, - getFeatureID: SingleFeatureGuardParamFn + getResolveFn: () => ResolveFn>, + getFeatureID: SingleFeatureGuardParamFn, ): CanActivateFn => dsoPageSomeFeatureGuard( - getResolveFn, - (route: ActivatedRouteSnapshot, state: RouterStateSnapshot): Observable => getFeatureID(route, state).pipe( - map((featureID: FeatureID) => [featureID]), -)); + getResolveFn, + (route: ActivatedRouteSnapshot, state: RouterStateSnapshot): Observable => getFeatureID(route, state).pipe( + map((featureID: FeatureID) => [featureID]), + )); diff --git a/src/app/core/data/feature-authorization/feature-authorization-guard/dso-page-some-feature.guard.spec.ts b/src/app/core/data/feature-authorization/feature-authorization-guard/dso-page-some-feature.guard.spec.ts index ef500598a01..08f1c96b299 100644 --- a/src/app/core/data/feature-authorization/feature-authorization-guard/dso-page-some-feature.guard.spec.ts +++ b/src/app/core/data/feature-authorization/feature-authorization-guard/dso-page-some-feature.guard.spec.ts @@ -1,24 +1,32 @@ -import { AuthorizationDataService } from '../authorization-data.service'; -import { Router, UrlTree, ResolveFn } from '@angular/router'; -import { RemoteData } from '../../remote-data'; -import { Observable, of as observableOf } from 'rxjs'; -import { DSpaceObject } from '../../../shared/dspace-object.model'; +import { TestBed } from '@angular/core/testing'; +import { + ResolveFn, + Router, + UrlTree, +} from '@angular/router'; +import { + Observable, + of as observableOf, +} from 'rxjs'; + +import { createSuccessfulRemoteDataObject$ } from '../../../../shared/remote-data.utils'; import { AuthService } from '../../../auth/auth.service'; +import { DSpaceObject } from '../../../shared/dspace-object.model'; +import { RemoteData } from '../../remote-data'; +import { AuthorizationDataService } from '../authorization-data.service'; +import { FeatureID } from '../feature-id'; import { - dsoPageSomeFeatureGuard, defaultDSOGetObjectUrl, - getRouteWithDSOId + dsoPageSomeFeatureGuard, + getRouteWithDSOId, } from './dso-page-some-feature.guard'; -import { TestBed } from '@angular/core/testing'; -import { FeatureID } from '../feature-id'; -import { createSuccessfulRemoteDataObject$ } from '../../../../shared/remote-data.utils'; describe('dsoPageSomeFeatureGuard and its functions', () => { let authorizationService: AuthorizationDataService; let router: Router; let authService: AuthService; - let resolver: ResolveFn>>; + let resolver: ResolveFn>; let object: DSpaceObject; let route; let parentRoute; @@ -27,28 +35,28 @@ describe('dsoPageSomeFeatureGuard and its functions', () => { function init() { object = { - self: 'test-selflink' + self: 'test-selflink', } as DSpaceObject; featureIds = [FeatureID.LoginOnBehalfOf, FeatureID.CanDelete]; authorizationService = jasmine.createSpyObj('authorizationService', { - isAuthorized: observableOf(true) + isAuthorized: observableOf(true), }); router = jasmine.createSpyObj('router', { - parseUrl: {} + parseUrl: {}, }); resolver = () => createSuccessfulRemoteDataObject$(object); authService = jasmine.createSpyObj('authService', { - isAuthenticated: observableOf(true) + isAuthenticated: observableOf(true), }); parentRoute = { params: { - id: '3e1a5327-dabb-41ff-af93-e6cab9d032f0' - } + id: '3e1a5327-dabb-41ff-af93-e6cab9d032f0', + }, }; route = { params: { }, - parent: parentRoute + parent: parentRoute, }; TestBed.configureTestingModule({ @@ -56,7 +64,7 @@ describe('dsoPageSomeFeatureGuard and its functions', () => { { provide: AuthorizationDataService, useValue: authorizationService }, { provide: Router, useValue: router }, { provide: AuthService, useValue: authService }, - ] + ], }); } @@ -87,7 +95,7 @@ describe('dsoPageSomeFeatureGuard and its functions', () => { it('should call authorizationService.isAuthenticated with the appropriate arguments', (done) => { const result$ = TestBed.runInInjectionContext(() => { return dsoPageSomeFeatureGuard( - () => resolver, () => observableOf(featureIds) + () => resolver, () => observableOf(featureIds), )(route, { url: 'current-url' } as any); }) as Observable; diff --git a/src/app/core/data/feature-authorization/feature-authorization-guard/dso-page-some-feature.guard.ts b/src/app/core/data/feature-authorization/feature-authorization-guard/dso-page-some-feature.guard.ts index e920687ecf6..7469f113b49 100644 --- a/src/app/core/data/feature-authorization/feature-authorization-guard/dso-page-some-feature.guard.ts +++ b/src/app/core/data/feature-authorization/feature-authorization-guard/dso-page-some-feature.guard.ts @@ -1,18 +1,27 @@ -import { DSpaceObject } from '../../../shared/dspace-object.model'; -import { ResolveFn, ActivatedRouteSnapshot, RouterStateSnapshot, CanActivateFn } from '@angular/router'; +import { + ActivatedRouteSnapshot, + CanActivateFn, + ResolveFn, + RouterStateSnapshot, +} from '@angular/router'; import { Observable } from 'rxjs'; -import { RemoteData } from '../../remote-data'; -import { hasValue, hasNoValue } from '../../../../shared/empty.util'; -import { getAllSucceededRemoteDataPayload } from '../../../shared/operators'; import { map } from 'rxjs/operators'; + import { - StringGuardParamFn, + hasNoValue, + hasValue, +} from '../../../../shared/empty.util'; +import { DSpaceObject } from '../../../shared/dspace-object.model'; +import { getAllSucceededRemoteDataPayload } from '../../../shared/operators'; +import { RemoteData } from '../../remote-data'; +import { FeatureID } from '../feature-id'; +import { + someFeatureAuthorizationGuard, SomeFeatureGuardParamFn, - someFeatureAuthorizationGuard + StringGuardParamFn, } from './some-feature-authorization.guard'; -import { FeatureID } from '../feature-id'; -export declare type DSOGetObjectURlFn = (resolve: ResolveFn>>) => StringGuardParamFn; +export declare type DSOGetObjectURlFn = (resolve: ResolveFn>) => StringGuardParamFn; /** @@ -29,12 +38,12 @@ export const getRouteWithDSOId = (route: ActivatedRouteSnapshot): ActivatedRoute -export const defaultDSOGetObjectUrl: DSOGetObjectURlFn = (resolve: ResolveFn>>): StringGuardParamFn => { +export const defaultDSOGetObjectUrl: DSOGetObjectURlFn = (resolve: ResolveFn>): StringGuardParamFn => { return (route: ActivatedRouteSnapshot, state: RouterStateSnapshot): Observable => { const routeWithObjectID = getRouteWithDSOId(route); return (resolve(routeWithObjectID, state) as Observable>).pipe( getAllSucceededRemoteDataPayload(), - map((dso) => dso.self) + map((dso) => dso.self), ); }; }; @@ -44,7 +53,7 @@ export const defaultDSOGetObjectUrl: DSOGetObjectURlFn = ( - getResolveFn: () => ResolveFn>>, + getResolveFn: () => ResolveFn>, getFeatureIDs: SomeFeatureGuardParamFn, getObjectUrl: DSOGetObjectURlFn = defaultDSOGetObjectUrl, getEPersonUuid?: StringGuardParamFn, diff --git a/src/app/core/data/feature-authorization/feature-authorization-guard/group-administrator.guard.ts b/src/app/core/data/feature-authorization/feature-authorization-guard/group-administrator.guard.ts index 6d4e02dbcfb..9641d0aace9 100644 --- a/src/app/core/data/feature-authorization/feature-authorization-guard/group-administrator.guard.ts +++ b/src/app/core/data/feature-authorization/feature-authorization-guard/group-administrator.guard.ts @@ -1,7 +1,8 @@ import { CanActivateFn } from '@angular/router'; -import { singleFeatureAuthorizationGuard } from './single-feature-authorization.guard'; import { of as observableOf } from 'rxjs'; + import { FeatureID } from '../feature-id'; +import { singleFeatureAuthorizationGuard } from './single-feature-authorization.guard'; /** * Prevent unauthorized activating and loading of routes when the current authenticated user doesn't have group diff --git a/src/app/core/data/feature-authorization/feature-authorization-guard/single-feature-authorization.guard.spec.ts b/src/app/core/data/feature-authorization/feature-authorization-guard/single-feature-authorization.guard.spec.ts index dc518ddaffc..7c15fa4cdff 100644 --- a/src/app/core/data/feature-authorization/feature-authorization-guard/single-feature-authorization.guard.spec.ts +++ b/src/app/core/data/feature-authorization/feature-authorization-guard/single-feature-authorization.guard.spec.ts @@ -1,10 +1,20 @@ +import { + TestBed, + waitForAsync, +} from '@angular/core/testing'; +import { + Router, + UrlTree, +} from '@angular/router'; +import { + Observable, + of as observableOf, +} from 'rxjs'; + +import { AuthService } from '../../../auth/auth.service'; import { AuthorizationDataService } from '../authorization-data.service'; import { FeatureID } from '../feature-id'; -import { of as observableOf, Observable } from 'rxjs'; -import { Router, UrlTree } from '@angular/router'; -import { AuthService } from '../../../auth/auth.service'; import { singleFeatureAuthorizationGuard } from './single-feature-authorization.guard'; -import { waitForAsync, TestBed } from '@angular/core/testing'; describe('singleFeatureAuthorizationGuard', () => { let authorizationService: AuthorizationDataService; @@ -35,7 +45,7 @@ describe('singleFeatureAuthorizationGuard', () => { { provide: AuthorizationDataService, useValue: authorizationService }, { provide: Router, useValue: router }, { provide: AuthService, useValue: authService }, - ] + ], }); } diff --git a/src/app/core/data/feature-authorization/feature-authorization-guard/single-feature-authorization.guard.ts b/src/app/core/data/feature-authorization/feature-authorization-guard/single-feature-authorization.guard.ts index 4b37f1d0bac..995dcb6f5c4 100644 --- a/src/app/core/data/feature-authorization/feature-authorization-guard/single-feature-authorization.guard.ts +++ b/src/app/core/data/feature-authorization/feature-authorization-guard/single-feature-authorization.guard.ts @@ -1,8 +1,16 @@ -import { ActivatedRouteSnapshot, RouterStateSnapshot, CanActivateFn } from '@angular/router'; +import { + ActivatedRouteSnapshot, + CanActivateFn, + RouterStateSnapshot, +} from '@angular/router'; import { Observable } from 'rxjs'; -import { FeatureID } from '../feature-id'; import { map } from 'rxjs/operators'; -import { StringGuardParamFn, someFeatureAuthorizationGuard } from './some-feature-authorization.guard'; + +import { FeatureID } from '../feature-id'; +import { + someFeatureAuthorizationGuard, + StringGuardParamFn, +} from './some-feature-authorization.guard'; export declare type SingleFeatureGuardParamFn = (route: ActivatedRouteSnapshot, state: RouterStateSnapshot) => Observable; @@ -23,5 +31,5 @@ export const singleFeatureAuthorizationGuard = ( getEPersonUuid?: StringGuardParamFn, ): CanActivateFn => someFeatureAuthorizationGuard( (route: ActivatedRouteSnapshot, state: RouterStateSnapshot): Observable => getFeatureID(route, state).pipe( - map((featureID: FeatureID) => [featureID]), -), getObjectUrl, getEPersonUuid); + map((featureID: FeatureID) => [featureID]), + ), getObjectUrl, getEPersonUuid); diff --git a/src/app/core/data/feature-authorization/feature-authorization-guard/site-administrator.guard.ts b/src/app/core/data/feature-authorization/feature-authorization-guard/site-administrator.guard.ts index bcef5b7c8fc..4caa1f806d9 100644 --- a/src/app/core/data/feature-authorization/feature-authorization-guard/site-administrator.guard.ts +++ b/src/app/core/data/feature-authorization/feature-authorization-guard/site-administrator.guard.ts @@ -1,7 +1,8 @@ import { CanActivateFn } from '@angular/router'; +import { of as observableOf } from 'rxjs'; + import { FeatureID } from '../feature-id'; import { singleFeatureAuthorizationGuard } from './single-feature-authorization.guard'; -import { of as observableOf } from 'rxjs'; /** * Prevent unauthorized activating and loading of routes when the current authenticated user doesn't have administrator diff --git a/src/app/core/data/feature-authorization/feature-authorization-guard/site-register.guard.ts b/src/app/core/data/feature-authorization/feature-authorization-guard/site-register.guard.ts index bf1945ac337..ee08532d38f 100644 --- a/src/app/core/data/feature-authorization/feature-authorization-guard/site-register.guard.ts +++ b/src/app/core/data/feature-authorization/feature-authorization-guard/site-register.guard.ts @@ -1,7 +1,8 @@ import { CanActivateFn } from '@angular/router'; -import { singleFeatureAuthorizationGuard } from './single-feature-authorization.guard'; import { of as observableOf } from 'rxjs'; + import { FeatureID } from '../feature-id'; +import { singleFeatureAuthorizationGuard } from './single-feature-authorization.guard'; /** * Prevent unauthorized activating and loading of routes when the current authenticated user doesn't have registration diff --git a/src/app/core/data/feature-authorization/feature-authorization-guard/some-feature-authorization.guard.spec.ts b/src/app/core/data/feature-authorization/feature-authorization-guard/some-feature-authorization.guard.spec.ts index c12668d6a5e..79e023bdd0f 100644 --- a/src/app/core/data/feature-authorization/feature-authorization-guard/some-feature-authorization.guard.spec.ts +++ b/src/app/core/data/feature-authorization/feature-authorization-guard/some-feature-authorization.guard.spec.ts @@ -1,10 +1,20 @@ +import { + TestBed, + waitForAsync, +} from '@angular/core/testing'; +import { + Router, + UrlTree, +} from '@angular/router'; +import { + Observable, + of as observableOf, +} from 'rxjs'; + +import { AuthService } from '../../../auth/auth.service'; import { AuthorizationDataService } from '../authorization-data.service'; import { FeatureID } from '../feature-id'; -import { Observable, of as observableOf } from 'rxjs'; -import { Router, UrlTree } from '@angular/router'; -import { AuthService } from '../../../auth/auth.service'; import { someFeatureAuthorizationGuard } from './some-feature-authorization.guard'; -import { TestBed, waitForAsync } from '@angular/core/testing'; describe('SomeFeatureAuthorizationGuard', () => { let authorizationService: AuthorizationDataService; @@ -25,15 +35,15 @@ describe('SomeFeatureAuthorizationGuard', () => { authorizationService = Object.assign({ isAuthorized(featureId?: FeatureID): Observable { return observableOf(authorizedFeatureIds.indexOf(featureId) > -1); - } + }, }); router = jasmine.createSpyObj('router', { - parseUrl: {} + parseUrl: {}, }); authService = jasmine.createSpyObj('authService', { - isAuthenticated: observableOf(true) + isAuthenticated: observableOf(true), }); TestBed.configureTestingModule({ @@ -41,7 +51,7 @@ describe('SomeFeatureAuthorizationGuard', () => { { provide: AuthorizationDataService, useValue: authorizationService }, { provide: Router, useValue: router }, { provide: AuthService, useValue: authService }, - ] + ], }); } diff --git a/src/app/core/data/feature-authorization/feature-authorization-guard/some-feature-authorization.guard.ts b/src/app/core/data/feature-authorization/feature-authorization-guard/some-feature-authorization.guard.ts index f5879612684..53e5e582eb8 100644 --- a/src/app/core/data/feature-authorization/feature-authorization-guard/some-feature-authorization.guard.ts +++ b/src/app/core/data/feature-authorization/feature-authorization-guard/some-feature-authorization.guard.ts @@ -1,11 +1,22 @@ -import { ActivatedRouteSnapshot, RouterStateSnapshot, CanActivateFn, Router, UrlTree } from '@angular/router'; -import { Observable, of as observableOf, combineLatest as observableCombineLatest } from 'rxjs'; -import { FeatureID } from '../feature-id'; -import { AuthorizationDataService } from '../authorization-data.service'; -import { AuthService } from '../../../auth/auth.service'; import { inject } from '@angular/core'; +import { + ActivatedRouteSnapshot, + CanActivateFn, + Router, + RouterStateSnapshot, + UrlTree, +} from '@angular/router'; +import { + combineLatest as observableCombineLatest, + Observable, + of as observableOf, +} from 'rxjs'; import { switchMap } from 'rxjs/operators'; + +import { AuthService } from '../../../auth/auth.service'; import { returnForbiddenUrlTreeOrLoginOnAllFalse } from '../../../shared/authorized.operators'; +import { AuthorizationDataService } from '../authorization-data.service'; +import { FeatureID } from '../feature-id'; export declare type SomeFeatureGuardParamFn = (route: ActivatedRouteSnapshot, state: RouterStateSnapshot) => Observable; export declare type StringGuardParamFn = (route: ActivatedRouteSnapshot, state: RouterStateSnapshot) => Observable; @@ -33,7 +44,7 @@ export const someFeatureAuthorizationGuard = ( return observableCombineLatest([ getFeatureIDs(route, state), getObjectUrl(route, state), - getEPersonUuid(route, state) + getEPersonUuid(route, state), ]).pipe( switchMap(([featureIDs, objectUrl, ePersonUuid]: [FeatureID[], string, string]) => observableCombineLatest(featureIDs.map((featureID) => authorizationService.isAuthorized(featureID, objectUrl, ePersonUuid))), diff --git a/src/app/core/data/feature-authorization/feature-authorization-guard/statistics-administrator.guard.ts b/src/app/core/data/feature-authorization/feature-authorization-guard/statistics-administrator.guard.ts index ff58dbb4bed..21cafeaba3f 100644 --- a/src/app/core/data/feature-authorization/feature-authorization-guard/statistics-administrator.guard.ts +++ b/src/app/core/data/feature-authorization/feature-authorization-guard/statistics-administrator.guard.ts @@ -1,7 +1,8 @@ import { CanActivateFn } from '@angular/router'; -import { singleFeatureAuthorizationGuard } from './single-feature-authorization.guard'; import { of as observableOf } from 'rxjs'; + import { FeatureID } from '../feature-id'; +import { singleFeatureAuthorizationGuard } from './single-feature-authorization.guard'; /** * Prevent unauthorized activating and loading of routes when the current authenticated user doesn't have group diff --git a/src/app/core/rest-property/forgot-password-check-guard.guard.ts b/src/app/core/rest-property/forgot-password-check-guard.guard.ts index cc74e8039f0..49727dda116 100644 --- a/src/app/core/rest-property/forgot-password-check-guard.guard.ts +++ b/src/app/core/rest-property/forgot-password-check-guard.guard.ts @@ -1,37 +1,11 @@ -import { Injectable } from '@angular/core'; -import { - ActivatedRouteSnapshot, - Router, - RouterStateSnapshot, -} from '@angular/router'; -import { - Observable, - of, -} from 'rxjs'; +import { CanActivateFn } from '@angular/router'; +import { of as observableOf } from 'rxjs'; -import { AuthService } from '../auth/auth.service'; -import { AuthorizationDataService } from '../data/feature-authorization/authorization-data.service'; -import { SingleFeatureAuthorizationGuard } from '../data/feature-authorization/feature-authorization-guard/single-feature-authorization.guard'; +import { singleFeatureAuthorizationGuard } from '../data/feature-authorization/feature-authorization-guard/single-feature-authorization.guard'; import { FeatureID } from '../data/feature-authorization/feature-id'; -@Injectable({ - providedIn: 'root', -}) /** * Guard that checks if the forgot-password feature is enabled */ -export class ForgotPasswordCheckGuard extends SingleFeatureAuthorizationGuard { - - constructor( - protected readonly authorizationService: AuthorizationDataService, - protected readonly router: Router, - protected readonly authService: AuthService, - ) { - super(authorizationService, router, authService); - } - - getFeatureID(route: ActivatedRouteSnapshot, state: RouterStateSnapshot): Observable { - return of(FeatureID.EPersonForgotPassword); - } - -} +export const forgotPasswordCheckGuard: CanActivateFn = + singleFeatureAuthorizationGuard(() => observableOf(FeatureID.EPersonForgotPassword)); diff --git a/src/app/health-page/health-page-routes.ts b/src/app/health-page/health-page-routes.ts index 4c02bc548f3..f87cf8e3d3d 100644 --- a/src/app/health-page/health-page-routes.ts +++ b/src/app/health-page/health-page-routes.ts @@ -1,10 +1,7 @@ -import { - mapToCanActivate, - Route, -} from '@angular/router'; +import { Route } from '@angular/router'; import { i18nBreadcrumbResolver } from '../core/breadcrumbs/i18n-breadcrumb.resolver'; -import { SiteAdministratorGuard } from '../core/data/feature-authorization/feature-authorization-guard/site-administrator.guard'; +import { siteAdministratorGuard } from '../core/data/feature-authorization/feature-authorization-guard/site-administrator.guard'; import { HealthPageComponent } from './health-page.component'; export const ROUTES: Route[] = [ @@ -15,7 +12,7 @@ export const ROUTES: Route[] = [ breadcrumbKey: 'health', title: 'health-page.title', }, - canActivate: mapToCanActivate([SiteAdministratorGuard]), + canActivate: [siteAdministratorGuard], component: HealthPageComponent, }, ]; diff --git a/src/app/item-page/edit-item-page/edit-item-page-routes.ts b/src/app/item-page/edit-item-page/edit-item-page-routes.ts index 1b1e43a883c..a1d76ded969 100644 --- a/src/app/item-page/edit-item-page/edit-item-page-routes.ts +++ b/src/app/item-page/edit-item-page/edit-item-page-routes.ts @@ -1,7 +1,4 @@ -import { - mapToCanActivate, - Route, -} from '@angular/router'; +import { Route } from '@angular/router'; import { i18nBreadcrumbResolver } from '../../core/breadcrumbs/i18n-breadcrumb.resolver'; import { ThemedDsoEditMetadataComponent } from '../../dso-shared/dso-edit-metadata/themed-dso-edit-metadata.component'; @@ -27,17 +24,17 @@ import { ItemCollectionMapperComponent } from './item-collection-mapper/item-col import { ItemCurateComponent } from './item-curate/item-curate.component'; import { ItemDeleteComponent } from './item-delete/item-delete.component'; import { ItemMoveComponent } from './item-move/item-move.component'; -import { ItemPageAccessControlGuard } from './item-page-access-control.guard'; -import { ItemPageBitstreamsGuard } from './item-page-bitstreams.guard'; -import { ItemPageCollectionMapperGuard } from './item-page-collection-mapper.guard'; -import { ItemPageCurateGuard } from './item-page-curate.guard'; -import { ItemPageMetadataGuard } from './item-page-metadata.guard'; -import { ItemPageRegisterDoiGuard } from './item-page-register-doi.guard'; -import { ItemPageReinstateGuard } from './item-page-reinstate.guard'; -import { ItemPageRelationshipsGuard } from './item-page-relationships.guard'; -import { ItemPageStatusGuard } from './item-page-status.guard'; -import { ItemPageVersionHistoryGuard } from './item-page-version-history.guard'; -import { ItemPageWithdrawGuard } from './item-page-withdraw.guard'; +import { itemPageAccessControlGuard } from './item-page-access-control.guard'; +import { itemPageBitstreamsGuard } from './item-page-bitstreams.guard'; +import { itemPageCollectionMapperGuard } from './item-page-collection-mapper.guard'; +import { itemPageCurateGuard } from './item-page-curate.guard'; +import { itemPageMetadataGuard } from './item-page-metadata.guard'; +import { itemPageRegisterDoiGuard } from './item-page-register-doi.guard'; +import { itemPageReinstateGuard } from './item-page-reinstate.guard'; +import { itemPageRelationshipsGuard } from './item-page-relationships.guard'; +import { itemPageStatusGuard } from './item-page-status.guard'; +import { itemPageVersionHistoryGuard } from './item-page-version-history.guard'; +import { itemPageWithdrawGuard } from './item-page-withdraw.guard'; import { ItemPrivateComponent } from './item-private/item-private.component'; import { ItemPublicComponent } from './item-public/item-public.component'; import { ItemRegisterDoiComponent } from './item-register-doi/item-register-doi.component'; @@ -72,31 +69,31 @@ export const ROUTES: Route[] = [ path: 'status', component: ThemedItemStatusComponent, data: { title: 'item.edit.tabs.status.title', showBreadcrumbs: true }, - canActivate: mapToCanActivate([ItemPageStatusGuard]), + canActivate: [itemPageStatusGuard], }, { path: 'bitstreams', component: ItemBitstreamsComponent, data: { title: 'item.edit.tabs.bitstreams.title', showBreadcrumbs: true }, - canActivate: mapToCanActivate([ItemPageBitstreamsGuard]), + canActivate: [itemPageBitstreamsGuard], }, { path: 'metadata', component: ThemedDsoEditMetadataComponent, data: { title: 'item.edit.tabs.metadata.title', showBreadcrumbs: true }, - canActivate: mapToCanActivate([ItemPageMetadataGuard]), + canActivate: [itemPageMetadataGuard], }, { path: 'curate', component: ItemCurateComponent, data: { title: 'item.edit.tabs.curate.title', showBreadcrumbs: true }, - canActivate: mapToCanActivate([ItemPageCurateGuard]), + canActivate: [itemPageCurateGuard], }, { path: 'relationships', component: ItemRelationshipsComponent, data: { title: 'item.edit.tabs.relationships.title', showBreadcrumbs: true }, - canActivate: mapToCanActivate([ItemPageRelationshipsGuard]), + canActivate: [itemPageRelationshipsGuard], }, /* TODO - uncomment & fix when view page exists { @@ -114,19 +111,19 @@ export const ROUTES: Route[] = [ path: 'versionhistory', component: ItemVersionHistoryComponent, data: { title: 'item.edit.tabs.versionhistory.title', showBreadcrumbs: true }, - canActivate: mapToCanActivate([ItemPageVersionHistoryGuard]), + canActivate: [itemPageVersionHistoryGuard], }, { path: 'access-control', component: ItemAccessControlComponent, data: { title: 'item.edit.tabs.access-control.title', showBreadcrumbs: true }, - canActivate: mapToCanActivate([ItemPageAccessControlGuard]), + canActivate: [itemPageAccessControlGuard], }, { path: 'mapper', component: ItemCollectionMapperComponent, data: { title: 'item.edit.tabs.item-mapper.title', showBreadcrumbs: true }, - canActivate: mapToCanActivate([ItemPageCollectionMapperGuard]), + canActivate: [itemPageCollectionMapperGuard], }, ], }, @@ -137,12 +134,12 @@ export const ROUTES: Route[] = [ { path: ITEM_EDIT_WITHDRAW_PATH, component: ItemWithdrawComponent, - canActivate: mapToCanActivate([ItemPageWithdrawGuard]), + canActivate: [itemPageWithdrawGuard], }, { path: ITEM_EDIT_REINSTATE_PATH, component: ItemReinstateComponent, - canActivate: mapToCanActivate([ItemPageReinstateGuard]), + canActivate: [itemPageReinstateGuard], }, { path: ITEM_EDIT_PRIVATE_PATH, @@ -164,7 +161,7 @@ export const ROUTES: Route[] = [ { path: ITEM_EDIT_REGISTER_DOI_PATH, component: ItemRegisterDoiComponent, - canActivate: mapToCanActivate([ItemPageRegisterDoiGuard]), + canActivate: [itemPageRegisterDoiGuard], data: { title: 'item.edit.register-doi.title' }, }, { diff --git a/src/app/item-page/edit-item-page/item-page-access-control.guard.ts b/src/app/item-page/edit-item-page/item-page-access-control.guard.ts index 42ee1f3d158..bcf5ea9a135 100644 --- a/src/app/item-page/edit-item-page/item-page-access-control.guard.ts +++ b/src/app/item-page/edit-item-page/item-page-access-control.guard.ts @@ -1,43 +1,15 @@ -import { Injectable } from '@angular/core'; -import { - ActivatedRouteSnapshot, - ResolveFn, - Router, - RouterStateSnapshot, -} from '@angular/router'; -import { - Observable, - of as observableOf, -} from 'rxjs'; +import { CanActivateFn } from '@angular/router'; +import { of as observableOf } from 'rxjs'; -import { AuthService } from '../../core/auth/auth.service'; -import { AuthorizationDataService } from '../../core/data/feature-authorization/authorization-data.service'; -import { DsoPageSingleFeatureGuard } from '../../core/data/feature-authorization/feature-authorization-guard/dso-page-single-feature.guard'; +import { dsoPageSingleFeatureGuard } from '../../core/data/feature-authorization/feature-authorization-guard/dso-page-single-feature.guard'; import { FeatureID } from '../../core/data/feature-authorization/feature-id'; -import { RemoteData } from '../../core/data/remote-data'; -import { Item } from '../../core/shared/item.model'; import { itemPageResolver } from '../item-page.resolver'; -@Injectable({ - providedIn: 'root', -}) /** * Guard for preventing unauthorized access to certain {@link Item} pages requiring administrator rights */ -export class ItemPageAccessControlGuard extends DsoPageSingleFeatureGuard { - - protected resolver: ResolveFn> = itemPageResolver; - - constructor(protected authorizationService: AuthorizationDataService, - protected router: Router, - protected authService: AuthService) { - super(authorizationService, router, authService); - } - - /** - * Check administrator authorization rights - */ - getFeatureID(next: ActivatedRouteSnapshot, state: RouterStateSnapshot): Observable { - return observableOf(FeatureID.AdministratorOf); - } -} +export const itemPageAccessControlGuard: CanActivateFn = + dsoPageSingleFeatureGuard( + () => itemPageResolver, + () => observableOf(FeatureID.AdministratorOf), + ); diff --git a/src/app/item-page/edit-item-page/item-page-bitstreams.guard.ts b/src/app/item-page/edit-item-page/item-page-bitstreams.guard.ts index 9b72e9c6e32..32af7e603e1 100644 --- a/src/app/item-page/edit-item-page/item-page-bitstreams.guard.ts +++ b/src/app/item-page/edit-item-page/item-page-bitstreams.guard.ts @@ -1,13 +1,9 @@ -import { inject } from '@angular/core'; -import { - dsoPageSingleFeatureGuard -} from '../../core/data/feature-authorization/feature-authorization-guard/dso-page-single-feature.guard'; -import { Item } from '../../core/shared/item.model'; -import { ItemPageResolver } from '../item-page.resolver'; -import { CanActivateFn, ResolveFn } from '@angular/router'; -import { Observable, of as observableOf } from 'rxjs'; +import { CanActivateFn } from '@angular/router'; +import { of as observableOf } from 'rxjs'; + +import { dsoPageSingleFeatureGuard } from '../../core/data/feature-authorization/feature-authorization-guard/dso-page-single-feature.guard'; import { FeatureID } from '../../core/data/feature-authorization/feature-id'; -import { RemoteData } from '../../core/data/remote-data'; +import { itemPageResolver } from '../item-page.resolver'; /** * Guard for preventing unauthorized access to certain {@link Item} pages requiring manage bitstreams rights @@ -15,9 +11,6 @@ import { RemoteData } from '../../core/data/remote-data'; */ export const itemPageBitstreamsGuard: CanActivateFn = dsoPageSingleFeatureGuard( - () => { - const itemPageResolver = inject(ItemPageResolver); - return itemPageResolver.resolve as ResolveFn>>; - }, - () => observableOf(FeatureID.CanManageBitstreamBundles) + () => itemPageResolver, + () => observableOf(FeatureID.CanManageBitstreamBundles), ); diff --git a/src/app/item-page/edit-item-page/item-page-collection-mapper.guard.ts b/src/app/item-page/edit-item-page/item-page-collection-mapper.guard.ts index 98045173f24..56d9675d92c 100644 --- a/src/app/item-page/edit-item-page/item-page-collection-mapper.guard.ts +++ b/src/app/item-page/edit-item-page/item-page-collection-mapper.guard.ts @@ -1,13 +1,9 @@ -import { inject } from '@angular/core'; -import { - dsoPageSingleFeatureGuard -} from '../../core/data/feature-authorization/feature-authorization-guard/dso-page-single-feature.guard'; -import { Item } from '../../core/shared/item.model'; -import { ItemPageResolver } from '../item-page.resolver'; -import { CanActivateFn, ResolveFn } from '@angular/router'; -import { Observable, of as observableOf } from 'rxjs'; +import { CanActivateFn } from '@angular/router'; +import { of as observableOf } from 'rxjs'; + +import { dsoPageSingleFeatureGuard } from '../../core/data/feature-authorization/feature-authorization-guard/dso-page-single-feature.guard'; import { FeatureID } from '../../core/data/feature-authorization/feature-id'; -import { RemoteData } from '../../core/data/remote-data'; +import { itemPageResolver } from '../item-page.resolver'; /** * Guard for preventing unauthorized access to certain {@link Item} pages requiring manage mappings rights @@ -15,9 +11,6 @@ import { RemoteData } from '../../core/data/remote-data'; */ export const itemPageCollectionMapperGuard: CanActivateFn = dsoPageSingleFeatureGuard( - () => { - const itemPageResolver = inject(ItemPageResolver); - return itemPageResolver.resolve as ResolveFn>>; - }, - () => observableOf(FeatureID.CanManageMappings) + () => itemPageResolver, + () => observableOf(FeatureID.CanManageMappings), ); diff --git a/src/app/item-page/edit-item-page/item-page-curate.guard.ts b/src/app/item-page/edit-item-page/item-page-curate.guard.ts index 392bc5c523a..40cfe00c2fb 100644 --- a/src/app/item-page/edit-item-page/item-page-curate.guard.ts +++ b/src/app/item-page/edit-item-page/item-page-curate.guard.ts @@ -1,43 +1,15 @@ -import { Injectable } from '@angular/core'; -import { - ActivatedRouteSnapshot, - ResolveFn, - Router, - RouterStateSnapshot, -} from '@angular/router'; -import { - Observable, - of as observableOf, -} from 'rxjs'; +import { CanActivateFn } from '@angular/router'; +import { of as observableOf } from 'rxjs'; -import { AuthService } from '../../core/auth/auth.service'; -import { AuthorizationDataService } from '../../core/data/feature-authorization/authorization-data.service'; -import { DsoPageSingleFeatureGuard } from '../../core/data/feature-authorization/feature-authorization-guard/dso-page-single-feature.guard'; +import { dsoPageSingleFeatureGuard } from '../../core/data/feature-authorization/feature-authorization-guard/dso-page-single-feature.guard'; import { FeatureID } from '../../core/data/feature-authorization/feature-id'; -import { RemoteData } from '../../core/data/remote-data'; -import { Item } from '../../core/shared/item.model'; import { itemPageResolver } from '../item-page.resolver'; -@Injectable({ - providedIn: 'root', -}) /** * Guard for preventing unauthorized access to certain {@link Item} pages requiring administrator rights */ -export class ItemPageCurateGuard extends DsoPageSingleFeatureGuard { - - protected resolver: ResolveFn> = itemPageResolver; - - constructor(protected authorizationService: AuthorizationDataService, - protected router: Router, - protected authService: AuthService) { - super(authorizationService, router, authService); - } - - /** - * Check administrator authorization rights - */ - getFeatureID(next: ActivatedRouteSnapshot, state: RouterStateSnapshot): Observable { - return observableOf(FeatureID.AdministratorOf); - } -} +export const itemPageCurateGuard: CanActivateFn = + dsoPageSingleFeatureGuard( + () => itemPageResolver, + () => observableOf(FeatureID.AdministratorOf), + ); diff --git a/src/app/item-page/edit-item-page/item-page-delete.guard.ts b/src/app/item-page/edit-item-page/item-page-delete.guard.ts index 37cb4d781d5..99d79ca68f0 100644 --- a/src/app/item-page/edit-item-page/item-page-delete.guard.ts +++ b/src/app/item-page/edit-item-page/item-page-delete.guard.ts @@ -1,13 +1,9 @@ -import { inject } from '@angular/core'; -import { - dsoPageSingleFeatureGuard -} from '../../core/data/feature-authorization/feature-authorization-guard/dso-page-single-feature.guard'; -import { Item } from '../../core/shared/item.model'; -import { ItemPageResolver } from '../item-page.resolver'; -import { CanActivateFn, ResolveFn } from '@angular/router'; -import { Observable, of as observableOf } from 'rxjs'; +import { CanActivateFn } from '@angular/router'; +import { of as observableOf } from 'rxjs'; + +import { dsoPageSingleFeatureGuard } from '../../core/data/feature-authorization/feature-authorization-guard/dso-page-single-feature.guard'; import { FeatureID } from '../../core/data/feature-authorization/feature-id'; -import { RemoteData } from '../../core/data/remote-data'; +import { itemPageResolver } from '../item-page.resolver'; /** * Guard for preventing unauthorized access to certain {@link Item} pages requiring specific authorizations. @@ -15,9 +11,6 @@ import { RemoteData } from '../../core/data/remote-data'; */ export const itemPageDeleteGuard: CanActivateFn = dsoPageSingleFeatureGuard( - () => { - const itemPageResolver = inject(ItemPageResolver); - return itemPageResolver.resolve as ResolveFn>>; - }, - () => observableOf(FeatureID.CanDelete) + () => itemPageResolver, + () => observableOf(FeatureID.CanDelete), ); diff --git a/src/app/item-page/edit-item-page/item-page-edit-authorizations.guard.ts b/src/app/item-page/edit-item-page/item-page-edit-authorizations.guard.ts index 042967dbfb1..c5032ac604e 100644 --- a/src/app/item-page/edit-item-page/item-page-edit-authorizations.guard.ts +++ b/src/app/item-page/edit-item-page/item-page-edit-authorizations.guard.ts @@ -1,13 +1,9 @@ -import { inject } from '@angular/core'; -import { - dsoPageSingleFeatureGuard -} from '../../core/data/feature-authorization/feature-authorization-guard/dso-page-single-feature.guard'; -import { Item } from '../../core/shared/item.model'; -import { ItemPageResolver } from '../item-page.resolver'; -import { CanActivateFn, ResolveFn } from '@angular/router'; -import { Observable, of as observableOf } from 'rxjs'; +import { CanActivateFn } from '@angular/router'; +import { of as observableOf } from 'rxjs'; + +import { dsoPageSingleFeatureGuard } from '../../core/data/feature-authorization/feature-authorization-guard/dso-page-single-feature.guard'; import { FeatureID } from '../../core/data/feature-authorization/feature-id'; -import { RemoteData } from '../../core/data/remote-data'; +import { itemPageResolver } from '../item-page.resolver'; /** * Guard for preventing unauthorized access to certain {@link Item} pages requiring specific authorizations. @@ -15,9 +11,6 @@ import { RemoteData } from '../../core/data/remote-data'; */ export const itemPageEditAuthorizationsGuard: CanActivateFn = dsoPageSingleFeatureGuard( - () => { - const itemPageResolver = inject(ItemPageResolver); - return itemPageResolver.resolve as ResolveFn>>; - }, - () => observableOf(FeatureID.CanManagePolicies) + () => itemPageResolver, + () => observableOf(FeatureID.CanManagePolicies), ); diff --git a/src/app/item-page/edit-item-page/item-page-metadata.guard.ts b/src/app/item-page/edit-item-page/item-page-metadata.guard.ts index 00f64233be0..f058eb73590 100644 --- a/src/app/item-page/edit-item-page/item-page-metadata.guard.ts +++ b/src/app/item-page/edit-item-page/item-page-metadata.guard.ts @@ -1,13 +1,9 @@ -import { inject } from '@angular/core'; -import { - dsoPageSingleFeatureGuard -} from '../../core/data/feature-authorization/feature-authorization-guard/dso-page-single-feature.guard'; -import { Item } from '../../core/shared/item.model'; -import { ItemPageResolver } from '../item-page.resolver'; -import { CanActivateFn, ResolveFn } from '@angular/router'; -import { Observable, of as observableOf } from 'rxjs'; +import { CanActivateFn } from '@angular/router'; +import { of as observableOf } from 'rxjs'; + +import { dsoPageSingleFeatureGuard } from '../../core/data/feature-authorization/feature-authorization-guard/dso-page-single-feature.guard'; import { FeatureID } from '../../core/data/feature-authorization/feature-id'; -import { RemoteData } from '../../core/data/remote-data'; +import { itemPageResolver } from '../item-page.resolver'; /** * Guard for preventing unauthorized access to certain {@link Item} pages requiring edit metadata rights @@ -15,9 +11,6 @@ import { RemoteData } from '../../core/data/remote-data'; */ export const itemPageMetadataGuard: CanActivateFn = dsoPageSingleFeatureGuard( - () => { - const itemPageResolver = inject(ItemPageResolver); - return itemPageResolver.resolve as ResolveFn>>; - }, - () => observableOf(FeatureID.CanEditMetadata) + () => itemPageResolver, + () => observableOf(FeatureID.CanEditMetadata), ); diff --git a/src/app/item-page/edit-item-page/item-page-move.guard.ts b/src/app/item-page/edit-item-page/item-page-move.guard.ts index 06c71738b76..307201ef90a 100644 --- a/src/app/item-page/edit-item-page/item-page-move.guard.ts +++ b/src/app/item-page/edit-item-page/item-page-move.guard.ts @@ -1,13 +1,9 @@ -import { inject } from '@angular/core'; -import { - dsoPageSingleFeatureGuard -} from '../../core/data/feature-authorization/feature-authorization-guard/dso-page-single-feature.guard'; -import { Item } from '../../core/shared/item.model'; -import { ItemPageResolver } from '../item-page.resolver'; -import { CanActivateFn, ResolveFn } from '@angular/router'; -import { Observable, of as observableOf } from 'rxjs'; +import { CanActivateFn } from '@angular/router'; +import { of as observableOf } from 'rxjs'; + +import { dsoPageSingleFeatureGuard } from '../../core/data/feature-authorization/feature-authorization-guard/dso-page-single-feature.guard'; import { FeatureID } from '../../core/data/feature-authorization/feature-id'; -import { RemoteData } from '../../core/data/remote-data'; +import { itemPageResolver } from '../item-page.resolver'; /** * Guard for preventing unauthorized access to certain {@link Item} pages requiring specific authorizations. @@ -15,9 +11,6 @@ import { RemoteData } from '../../core/data/remote-data'; */ export const itemPageMoveGuard: CanActivateFn = dsoPageSingleFeatureGuard( - () => { - const itemPageResolver = inject(ItemPageResolver); - return itemPageResolver.resolve as ResolveFn>>; - }, - () => observableOf(FeatureID.CanMove) + () => itemPageResolver, + () => observableOf(FeatureID.CanMove), ); diff --git a/src/app/item-page/edit-item-page/item-page-private.guard.ts b/src/app/item-page/edit-item-page/item-page-private.guard.ts index eacfa1f8745..64626542ffa 100644 --- a/src/app/item-page/edit-item-page/item-page-private.guard.ts +++ b/src/app/item-page/edit-item-page/item-page-private.guard.ts @@ -1,13 +1,9 @@ -import { inject } from '@angular/core'; -import { - dsoPageSingleFeatureGuard -} from '../../core/data/feature-authorization/feature-authorization-guard/dso-page-single-feature.guard'; -import { Item } from '../../core/shared/item.model'; -import { ItemPageResolver } from '../item-page.resolver'; -import { CanActivateFn, ResolveFn } from '@angular/router'; -import { Observable, of as observableOf } from 'rxjs'; +import { CanActivateFn } from '@angular/router'; +import { of as observableOf } from 'rxjs'; + +import { dsoPageSingleFeatureGuard } from '../../core/data/feature-authorization/feature-authorization-guard/dso-page-single-feature.guard'; import { FeatureID } from '../../core/data/feature-authorization/feature-id'; -import { RemoteData } from '../../core/data/remote-data'; +import { itemPageResolver } from '../item-page.resolver'; /** * Guard for preventing unauthorized access to certain {@link Item} pages requiring specific authorizations. @@ -15,9 +11,6 @@ import { RemoteData } from '../../core/data/remote-data'; */ export const itemPagePrivateGuard: CanActivateFn = dsoPageSingleFeatureGuard( - () => { - const itemPageResolver = inject(ItemPageResolver); - return itemPageResolver.resolve as ResolveFn>>; - }, - () => observableOf(FeatureID.CanMakePrivate) + () => itemPageResolver, + () => observableOf(FeatureID.CanMakePrivate), ); diff --git a/src/app/item-page/edit-item-page/item-page-register-doi.guard.ts b/src/app/item-page/edit-item-page/item-page-register-doi.guard.ts index 0758eede08a..9bedca518b2 100644 --- a/src/app/item-page/edit-item-page/item-page-register-doi.guard.ts +++ b/src/app/item-page/edit-item-page/item-page-register-doi.guard.ts @@ -1,13 +1,9 @@ -import { inject } from '@angular/core'; -import { - dsoPageSingleFeatureGuard -} from '../../core/data/feature-authorization/feature-authorization-guard/dso-page-single-feature.guard'; -import { Item } from '../../core/shared/item.model'; -import { ItemPageResolver } from '../item-page.resolver'; -import { CanActivateFn, ResolveFn } from '@angular/router'; -import { Observable, of as observableOf } from 'rxjs'; +import { CanActivateFn } from '@angular/router'; +import { of as observableOf } from 'rxjs'; + +import { dsoPageSingleFeatureGuard } from '../../core/data/feature-authorization/feature-authorization-guard/dso-page-single-feature.guard'; import { FeatureID } from '../../core/data/feature-authorization/feature-id'; -import { RemoteData } from '../../core/data/remote-data'; +import { itemPageResolver } from '../item-page.resolver'; /** * Guard for preventing unauthorized access to certain {@link Item} pages requiring DOI registration rights @@ -15,9 +11,6 @@ import { RemoteData } from '../../core/data/remote-data'; */ export const itemPageRegisterDoiGuard: CanActivateFn = dsoPageSingleFeatureGuard( - () => { - const itemPageResolver = inject(ItemPageResolver); - return itemPageResolver.resolve as ResolveFn>>; - }, - () => observableOf(FeatureID.CanRegisterDOI) + () => itemPageResolver, + () => observableOf(FeatureID.CanRegisterDOI), ); diff --git a/src/app/item-page/edit-item-page/item-page-reinstate.guard.ts b/src/app/item-page/edit-item-page/item-page-reinstate.guard.ts index e0d19a3cef4..3e60158d0a5 100644 --- a/src/app/item-page/edit-item-page/item-page-reinstate.guard.ts +++ b/src/app/item-page/edit-item-page/item-page-reinstate.guard.ts @@ -1,13 +1,9 @@ -import { inject } from '@angular/core'; -import { - dsoPageSingleFeatureGuard -} from '../../core/data/feature-authorization/feature-authorization-guard/dso-page-single-feature.guard'; -import { Item } from '../../core/shared/item.model'; -import { ItemPageResolver } from '../item-page.resolver'; -import { CanActivateFn, ResolveFn } from '@angular/router'; -import { Observable, of as observableOf } from 'rxjs'; +import { CanActivateFn } from '@angular/router'; +import { of as observableOf } from 'rxjs'; + +import { dsoPageSingleFeatureGuard } from '../../core/data/feature-authorization/feature-authorization-guard/dso-page-single-feature.guard'; import { FeatureID } from '../../core/data/feature-authorization/feature-id'; -import { RemoteData } from '../../core/data/remote-data'; +import { itemPageResolver } from '../item-page.resolver'; /** * Guard for preventing unauthorized access to certain {@link Item} pages requiring reinstate rights @@ -15,9 +11,6 @@ import { RemoteData } from '../../core/data/remote-data'; */ export const itemPageReinstateGuard: CanActivateFn = dsoPageSingleFeatureGuard( - () => { - const itemPageResolver = inject(ItemPageResolver); - return itemPageResolver.resolve as ResolveFn>>; - }, - () => observableOf(FeatureID.ReinstateItem) + () => itemPageResolver, + () => observableOf(FeatureID.ReinstateItem), ); diff --git a/src/app/item-page/edit-item-page/item-page-relationships.guard.ts b/src/app/item-page/edit-item-page/item-page-relationships.guard.ts index dfb5bc4b7b2..fe107977dba 100644 --- a/src/app/item-page/edit-item-page/item-page-relationships.guard.ts +++ b/src/app/item-page/edit-item-page/item-page-relationships.guard.ts @@ -1,13 +1,9 @@ -import { inject } from '@angular/core'; -import { - dsoPageSingleFeatureGuard -} from '../../core/data/feature-authorization/feature-authorization-guard/dso-page-single-feature.guard'; -import { Item } from '../../core/shared/item.model'; -import { ItemPageResolver } from '../item-page.resolver'; -import { CanActivateFn, ResolveFn } from '@angular/router'; -import { Observable, of as observableOf } from 'rxjs'; +import { CanActivateFn } from '@angular/router'; +import { of as observableOf } from 'rxjs'; + +import { dsoPageSingleFeatureGuard } from '../../core/data/feature-authorization/feature-authorization-guard/dso-page-single-feature.guard'; import { FeatureID } from '../../core/data/feature-authorization/feature-id'; -import { RemoteData } from '../../core/data/remote-data'; +import { itemPageResolver } from '../item-page.resolver'; /** * Guard for preventing unauthorized access to certain {@link Item} pages requiring manage relationships rights @@ -15,9 +11,6 @@ import { RemoteData } from '../../core/data/remote-data'; */ export const itemPageRelationshipsGuard: CanActivateFn = dsoPageSingleFeatureGuard( - () => { - const itemPageResolver = inject(ItemPageResolver); - return itemPageResolver.resolve as ResolveFn>>; - }, - () => observableOf(FeatureID.CanManageRelationships) + () => itemPageResolver, + () => observableOf(FeatureID.CanManageRelationships), ); diff --git a/src/app/item-page/edit-item-page/item-page-status.guard.ts b/src/app/item-page/edit-item-page/item-page-status.guard.ts index c2be1a72e7e..deeb2dbb5e1 100644 --- a/src/app/item-page/edit-item-page/item-page-status.guard.ts +++ b/src/app/item-page/edit-item-page/item-page-status.guard.ts @@ -1,13 +1,9 @@ -import { - dsoPageSomeFeatureGuard -} from '../../core/data/feature-authorization/feature-authorization-guard/dso-page-some-feature.guard'; -import { CanActivateFn, ResolveFn } from '@angular/router'; -import { inject } from '@angular/core'; -import { ItemPageResolver } from '../item-page.resolver'; -import { Observable, of as observableOf } from 'rxjs'; -import { RemoteData } from '../../core/data/remote-data'; -import { Item } from '../../core/shared/item.model'; +import { CanActivateFn } from '@angular/router'; +import { of as observableOf } from 'rxjs'; + +import { dsoPageSomeFeatureGuard } from '../../core/data/feature-authorization/feature-authorization-guard/dso-page-some-feature.guard'; import { FeatureID } from '../../core/data/feature-authorization/feature-id'; +import { itemPageResolver } from '../item-page.resolver'; /** * Guard for preventing unauthorized access to certain {@link Item} pages requiring any of the rights required for @@ -16,9 +12,6 @@ import { FeatureID } from '../../core/data/feature-authorization/feature-id'; */ export const itemPageStatusGuard: CanActivateFn = dsoPageSomeFeatureGuard( - () => { - const itemPageResolver = inject(ItemPageResolver); - return itemPageResolver.resolve as ResolveFn>>; - }, - () => observableOf([FeatureID.CanManageMappings, FeatureID.WithdrawItem, FeatureID.ReinstateItem, FeatureID.CanManagePolicies, FeatureID.CanMakePrivate, FeatureID.CanDelete, FeatureID.CanMove, FeatureID.CanRegisterDOI]) + () => itemPageResolver, + () => observableOf([FeatureID.CanManageMappings, FeatureID.WithdrawItem, FeatureID.ReinstateItem, FeatureID.CanManagePolicies, FeatureID.CanMakePrivate, FeatureID.CanDelete, FeatureID.CanMove, FeatureID.CanRegisterDOI]), ); diff --git a/src/app/item-page/edit-item-page/item-page-version-history.guard.ts b/src/app/item-page/edit-item-page/item-page-version-history.guard.ts index ecd19072d03..99d581dce6c 100644 --- a/src/app/item-page/edit-item-page/item-page-version-history.guard.ts +++ b/src/app/item-page/edit-item-page/item-page-version-history.guard.ts @@ -1,13 +1,9 @@ -import { inject } from '@angular/core'; -import { - dsoPageSingleFeatureGuard -} from '../../core/data/feature-authorization/feature-authorization-guard/dso-page-single-feature.guard'; -import { Item } from '../../core/shared/item.model'; -import { ItemPageResolver } from '../item-page.resolver'; -import { CanActivateFn, ResolveFn } from '@angular/router'; -import { Observable, of as observableOf } from 'rxjs'; +import { CanActivateFn } from '@angular/router'; +import { of as observableOf } from 'rxjs'; + +import { dsoPageSingleFeatureGuard } from '../../core/data/feature-authorization/feature-authorization-guard/dso-page-single-feature.guard'; import { FeatureID } from '../../core/data/feature-authorization/feature-id'; -import { RemoteData } from '../../core/data/remote-data'; +import { itemPageResolver } from '../item-page.resolver'; /** * Guard for preventing unauthorized access to certain {@link Item} pages requiring manage versions rights @@ -15,9 +11,6 @@ import { RemoteData } from '../../core/data/remote-data'; */ export const itemPageVersionHistoryGuard: CanActivateFn = dsoPageSingleFeatureGuard( - () => { - const itemPageResolver = inject(ItemPageResolver); - return itemPageResolver.resolve as ResolveFn>>; - }, - () => observableOf(FeatureID.CanManageVersions) + () => itemPageResolver, + () => observableOf(FeatureID.CanManageVersions), ); diff --git a/src/app/item-page/edit-item-page/item-page-withdraw.guard.ts b/src/app/item-page/edit-item-page/item-page-withdraw.guard.ts index 38148973a74..8e41b1c6537 100644 --- a/src/app/item-page/edit-item-page/item-page-withdraw.guard.ts +++ b/src/app/item-page/edit-item-page/item-page-withdraw.guard.ts @@ -1,13 +1,9 @@ -import { inject } from '@angular/core'; -import { - dsoPageSingleFeatureGuard -} from '../../core/data/feature-authorization/feature-authorization-guard/dso-page-single-feature.guard'; -import { Item } from '../../core/shared/item.model'; -import { ItemPageResolver } from '../item-page.resolver'; -import { CanActivateFn, ResolveFn } from '@angular/router'; -import { Observable, of as observableOf } from 'rxjs'; +import { CanActivateFn } from '@angular/router'; +import { of as observableOf } from 'rxjs'; + +import { dsoPageSingleFeatureGuard } from '../../core/data/feature-authorization/feature-authorization-guard/dso-page-single-feature.guard'; import { FeatureID } from '../../core/data/feature-authorization/feature-id'; -import { RemoteData } from '../../core/data/remote-data'; +import { itemPageResolver } from '../item-page.resolver'; /** * Guard for preventing unauthorized access to certain {@link Item} pages requiring withdraw rights @@ -15,9 +11,6 @@ import { RemoteData } from '../../core/data/remote-data'; */ export const itemPageWithdrawGuard: CanActivateFn = dsoPageSingleFeatureGuard( - () => { - const itemPageResolver = inject(ItemPageResolver); - return itemPageResolver.resolve as ResolveFn>>; - }, - () => observableOf(FeatureID.WithdrawItem) + () => itemPageResolver, + () => observableOf(FeatureID.WithdrawItem), ); diff --git a/src/app/item-page/item-page-administrator.guard.ts b/src/app/item-page/item-page-administrator.guard.ts index 359e7f1849d..411ffa1e373 100644 --- a/src/app/item-page/item-page-administrator.guard.ts +++ b/src/app/item-page/item-page-administrator.guard.ts @@ -1,22 +1,15 @@ -import { inject } from '@angular/core'; -import { CanActivateFn, ResolveFn } from '@angular/router'; -import { Observable, of as observableOf } from 'rxjs'; -import { ItemPageResolver } from './item-page.resolver'; -import { - dsoPageSingleFeatureGuard -} from '../core/data/feature-authorization/feature-authorization-guard/dso-page-single-feature.guard'; +import { CanActivateFn } from '@angular/router'; +import { of as observableOf } from 'rxjs'; + +import { dsoPageSingleFeatureGuard } from '../core/data/feature-authorization/feature-authorization-guard/dso-page-single-feature.guard'; import { FeatureID } from '../core/data/feature-authorization/feature-id'; -import { RemoteData } from '../core/data/remote-data'; -import { Item } from '../core/shared/item.model'; +import { itemPageResolver } from './item-page.resolver'; /** * Guard for preventing unauthorized access to certain {@link Item} pages requiring administrator rights */ export const itemPageAdministratorGuard: CanActivateFn = dsoPageSingleFeatureGuard( - () => { - const itemPageResolver = inject(ItemPageResolver); - return itemPageResolver.resolve as ResolveFn>>; - }, - () => observableOf(FeatureID.AdministratorOf) + () => itemPageResolver, + () => observableOf(FeatureID.AdministratorOf), ); diff --git a/src/app/item-page/item-page-routes.ts b/src/app/item-page/item-page-routes.ts index 57aa70336d5..684ea564598 100644 --- a/src/app/item-page/item-page-routes.ts +++ b/src/app/item-page/item-page-routes.ts @@ -1,7 +1,4 @@ -import { - mapToCanActivate, - Route, -} from '@angular/router'; +import { Route } from '@angular/router'; import { REQUEST_COPY_MODULE_PATH } from '../app-routing-paths'; import { authenticatedGuard } from '../core/auth/authenticated.guard'; @@ -19,7 +16,7 @@ import { UPLOAD_BITSTREAM_PATH, } from './item-page-routing-paths'; import { OrcidPageComponent } from './orcid-page/orcid-page.component'; -import { OrcidPageGuard } from './orcid-page/orcid-page.guard'; +import { orcidPageGuard } from './orcid-page/orcid-page.guard'; import { ThemedItemPageComponent } from './simple/themed-item-page.component'; import { versionResolver } from './version-page/version.resolver'; import { VersionPageComponent } from './version-page/version-page/version-page.component'; @@ -60,7 +57,7 @@ export const ROUTES: Route[] = [ { path: ORCID_PATH, component: OrcidPageComponent, - canActivate: [authenticatedGuard, ...mapToCanActivate([OrcidPageGuard])], + canActivate: [authenticatedGuard, orcidPageGuard], }, ], data: { diff --git a/src/app/item-page/orcid-page/orcid-page.guard.ts b/src/app/item-page/orcid-page/orcid-page.guard.ts index 947be3ee9fa..c06ab7d97d0 100644 --- a/src/app/item-page/orcid-page/orcid-page.guard.ts +++ b/src/app/item-page/orcid-page/orcid-page.guard.ts @@ -1,13 +1,9 @@ -import { inject } from '@angular/core'; -import { - dsoPageSingleFeatureGuard -} from '../../core/data/feature-authorization/feature-authorization-guard/dso-page-single-feature.guard'; -import { Item } from '../../core/shared/item.model'; -import { ItemPageResolver } from '../item-page.resolver'; -import { CanActivateFn, ResolveFn } from '@angular/router'; -import { Observable, of as observableOf } from 'rxjs'; +import { CanActivateFn } from '@angular/router'; +import { of as observableOf } from 'rxjs'; + +import { dsoPageSingleFeatureGuard } from '../../core/data/feature-authorization/feature-authorization-guard/dso-page-single-feature.guard'; import { FeatureID } from '../../core/data/feature-authorization/feature-id'; -import { RemoteData } from '../../core/data/remote-data'; +import { itemPageResolver } from '../item-page.resolver'; /** * Guard for preventing unauthorized access to certain {@link Item} pages requiring administrator rights @@ -15,9 +11,6 @@ import { RemoteData } from '../../core/data/remote-data'; */ export const orcidPageGuard: CanActivateFn = dsoPageSingleFeatureGuard( - () => { - const itemPageResolver = inject(ItemPageResolver); - return itemPageResolver.resolve as ResolveFn>>; - }, - () => observableOf(FeatureID.CanSynchronizeWithORCID) + () => itemPageResolver, + () => observableOf(FeatureID.CanSynchronizeWithORCID), ); diff --git a/src/app/statistics-page/statistics-page-routes.ts b/src/app/statistics-page/statistics-page-routes.ts index 41a91ee3aaf..69bcc6b41c1 100644 --- a/src/app/statistics-page/statistics-page-routes.ts +++ b/src/app/statistics-page/statistics-page-routes.ts @@ -1,12 +1,9 @@ -import { - mapToCanActivate, - Route, -} from '@angular/router'; +import { Route } from '@angular/router'; import { collectionPageResolver } from '../collection-page/collection-page.resolver'; import { communityPageResolver } from '../community-page/community-page.resolver'; import { i18nBreadcrumbResolver } from '../core/breadcrumbs/i18n-breadcrumb.resolver'; -import { StatisticsAdministratorGuard } from '../core/data/feature-authorization/feature-authorization-guard/statistics-administrator.guard'; +import { statisticsAdministratorGuard } from '../core/data/feature-authorization/feature-authorization-guard/statistics-administrator.guard'; import { itemResolver } from '../item-page/item.resolver'; import { ThemedCollectionStatisticsPageComponent } from './collection-statistics-page/themed-collection-statistics-page.component'; import { ThemedCommunityStatisticsPageComponent } from './community-statistics-page/themed-community-statistics-page.component'; @@ -29,7 +26,7 @@ export const ROUTES: Route[] = [ component: ThemedSiteStatisticsPageComponent, }, ], - canActivate: mapToCanActivate([StatisticsAdministratorGuard]), + canActivate: [statisticsAdministratorGuard], }, { path: `items/:id`, @@ -42,7 +39,7 @@ export const ROUTES: Route[] = [ breadcrumbKey: 'statistics', }, component: ThemedItemStatisticsPageComponent, - canActivate: mapToCanActivate([StatisticsAdministratorGuard]), + canActivate: [statisticsAdministratorGuard], }, { path: `collections/:id`, @@ -55,7 +52,7 @@ export const ROUTES: Route[] = [ breadcrumbKey: 'statistics', }, component: ThemedCollectionStatisticsPageComponent, - canActivate: mapToCanActivate([StatisticsAdministratorGuard]), + canActivate: [statisticsAdministratorGuard], }, { path: `communities/:id`, @@ -68,6 +65,6 @@ export const ROUTES: Route[] = [ breadcrumbKey: 'statistics', }, component: ThemedCommunityStatisticsPageComponent, - canActivate: mapToCanActivate([StatisticsAdministratorGuard]), + canActivate: [statisticsAdministratorGuard], }, ]; diff --git a/src/app/system-wide-alert/system-wide-alert-routes.ts b/src/app/system-wide-alert/system-wide-alert-routes.ts index 2f3596825bf..a71007b6b3c 100644 --- a/src/app/system-wide-alert/system-wide-alert-routes.ts +++ b/src/app/system-wide-alert/system-wide-alert-routes.ts @@ -1,15 +1,12 @@ -import { - mapToCanActivate, - Route, -} from '@angular/router'; +import { Route } from '@angular/router'; -import { SiteAdministratorGuard } from '../core/data/feature-authorization/feature-authorization-guard/site-administrator.guard'; +import { siteAdministratorGuard } from '../core/data/feature-authorization/feature-authorization-guard/site-administrator.guard'; import { SystemWideAlertFormComponent } from './alert-form/system-wide-alert-form.component'; export const ROUTES: Route[] = [ { path: '', - canActivate: mapToCanActivate([SiteAdministratorGuard]), + canActivate: [siteAdministratorGuard], component: SystemWideAlertFormComponent, }, From b83b2dd404e0fe54ff01189c2b5b1c19180952d9 Mon Sep 17 00:00:00 2001 From: Zahraa Chreim Date: Wed, 8 May 2024 16:29:15 +0300 Subject: [PATCH 11/17] 114858: Create dsoPageSingleFeatureGuards for Edit Item and Edit Bitstream Routes --- src/app/bitstream-page/bitstream-page-routes.ts | 2 ++ src/app/item-page/edit-item-page/edit-item-page-routes.ts | 8 ++++++++ 2 files changed, 10 insertions(+) diff --git a/src/app/bitstream-page/bitstream-page-routes.ts b/src/app/bitstream-page/bitstream-page-routes.ts index 73848a7f4e8..9312e01de7d 100644 --- a/src/app/bitstream-page/bitstream-page-routes.ts +++ b/src/app/bitstream-page/bitstream-page-routes.ts @@ -12,6 +12,7 @@ import { BitstreamDownloadPageComponent } from './bitstream-download-page/bitstr import { bitstreamPageResolver } from './bitstream-page.resolver'; import { ThemedEditBitstreamPageComponent } from './edit-bitstream-page/themed-edit-bitstream-page.component'; import { legacyBitstreamUrlResolver } from './legacy-bitstream-url.resolver'; +import { bitstreamPageAuthorizationsGuard } from './bitstream-page-authorizations.guard'; const EDIT_BITSTREAM_PATH = ':id/edit'; const EDIT_BITSTREAM_AUTHORIZATIONS_PATH = ':id/authorizations'; @@ -55,6 +56,7 @@ export const ROUTES: Route[] = [ }, { path: EDIT_BITSTREAM_AUTHORIZATIONS_PATH, + canActivate: [bitstreamPageAuthorizationsGuard], children: [ { path: 'create', diff --git a/src/app/item-page/edit-item-page/edit-item-page-routes.ts b/src/app/item-page/edit-item-page/edit-item-page-routes.ts index a1d76ded969..fd6edb7ed0b 100644 --- a/src/app/item-page/edit-item-page/edit-item-page-routes.ts +++ b/src/app/item-page/edit-item-page/edit-item-page-routes.ts @@ -43,6 +43,10 @@ import { ItemRelationshipsComponent } from './item-relationships/item-relationsh import { ThemedItemStatusComponent } from './item-status/themed-item-status.component'; import { ItemVersionHistoryComponent } from './item-version-history/item-version-history.component'; import { ItemWithdrawComponent } from './item-withdraw/item-withdraw.component'; +import { itemPageEditAuthorizationsGuard } from './item-page-edit-authorizations.guard'; +import { itemPagePrivateGuard } from './item-page-private.guard'; +import { itemPageMoveGuard } from './item-page-move.guard'; +import { itemPageDeleteGuard } from './item-page-delete.guard'; /** * Routing module that handles the routing for the Edit Item page administrator functionality @@ -144,6 +148,7 @@ export const ROUTES: Route[] = [ { path: ITEM_EDIT_PRIVATE_PATH, component: ItemPrivateComponent, + canActivate: [itemPagePrivateGuard], }, { path: ITEM_EDIT_PUBLIC_PATH, @@ -152,11 +157,13 @@ export const ROUTES: Route[] = [ { path: ITEM_EDIT_DELETE_PATH, component: ItemDeleteComponent, + canActivate: [itemPageDeleteGuard], }, { path: ITEM_EDIT_MOVE_PATH, component: ItemMoveComponent, data: { title: 'item.edit.move.title' }, + canActivate: [itemPageMoveGuard], }, { path: ITEM_EDIT_REGISTER_DOI_PATH, @@ -189,6 +196,7 @@ export const ROUTES: Route[] = [ data: { title: 'item.edit.authorizations.title' }, }, ], + canActivate: [itemPageEditAuthorizationsGuard], }, ], }, From 16c75beebd7ef8430ec29e845cec8f19869ab995 Mon Sep 17 00:00:00 2001 From: Zahraa Chreim Date: Thu, 9 May 2024 15:28:50 +0300 Subject: [PATCH 12/17] 114858: Add Tests for new Guards --- ...tem-page-edit-authorizations.guard.spec.ts | 83 +++++++++++++++++++ 1 file changed, 83 insertions(+) create mode 100644 src/app/item-page/edit-item-page/item-page-edit-authorizations.guard.spec.ts diff --git a/src/app/item-page/edit-item-page/item-page-edit-authorizations.guard.spec.ts b/src/app/item-page/edit-item-page/item-page-edit-authorizations.guard.spec.ts new file mode 100644 index 00000000000..dfee0c1150d --- /dev/null +++ b/src/app/item-page/edit-item-page/item-page-edit-authorizations.guard.spec.ts @@ -0,0 +1,83 @@ +import { TestBed } from '@angular/core/testing'; +import { UrlTree, Router } from '@angular/router'; +import { Observable, of as observableOf } from 'rxjs'; +import { AuthService } from 'src/app/core/auth/auth.service'; +import { AuthorizationDataService } from 'src/app/core/data/feature-authorization/authorization-data.service'; +import { FeatureID } from 'src/app/core/data/feature-authorization/feature-id'; +import { + Store, +} from '@ngrx/store'; + +import { itemPageEditAuthorizationsGuard } from './item-page-edit-authorizations.guard'; +import { APP_DATA_SERVICES_MAP } from '../../../config/app-config.interface'; +import { TranslateService } from '@ngx-translate/core'; +import { getMockTranslateService } from '../../shared/mocks/translate.service.mock'; +import { + dsoPageSingleFeatureGuard +} from '../../core/data/feature-authorization/feature-authorization-guard/dso-page-single-feature.guard'; + +describe('itemPageEditAuthorizationsGuard', () => { + let authorizationService: AuthorizationDataService; + let authService: AuthService; + let router: Router; + let route; + let parentRoute; + let store: Store; + + beforeEach(() => { + + store = jasmine.createSpyObj('store', { + dispatch: {}, + pipe: observableOf(true), + }); + authorizationService = jasmine.createSpyObj('authorizationService', { + isAuthorized: observableOf(true), + }); + router = jasmine.createSpyObj('router', { + parseUrl: {}, + }); + authService = jasmine.createSpyObj('authService', { + isAuthenticated: observableOf(true), + }); + + parentRoute = { + params: { + id: '3e1a5327-dabb-41ff-af93-e6cab9d032f0', + }, + }; + route = { + params: { + }, + parent: parentRoute, + }; + + TestBed.configureTestingModule({ + providers: [ + { provide: AuthorizationDataService, useValue: authorizationService }, + { provide: Router, useValue: router }, + { provide: AuthService, useValue: authService }, + { provide: Store, useValue: store }, + { provide: APP_DATA_SERVICES_MAP, useValue: {} }, + { provide: TranslateService, useValue: getMockTranslateService() }, + ], + }); + }); + + it('should call authorizationService.isAuthorized with the appropriate arguments', (done) => { + const result$ = TestBed.runInInjectionContext(() => { + return itemPageEditAuthorizationsGuard(route, { url: 'current-url' } as any); + }) as Observable; + + console.log('will subscribe'); + result$.subscribe((result) => { + console.log('result inside subscribe:', result); + expect(authorizationService.isAuthorized).toHaveBeenCalledWith( + FeatureID.CanManagePolicies, + 'fake-object-url', + 'fake-eperson-uuid', + ); + done(); + }); + + }); +}); From 0eb61dd1de13ac0dd902f26b8876eee6ce2bdc31 Mon Sep 17 00:00:00 2001 From: lotte Date: Fri, 10 May 2024 12:46:18 +0200 Subject: [PATCH 13/17] 115016: Fixed tests for 114858 --- ...tem-page-edit-authorizations.guard.spec.ts | 29 ++++++++++++------- 1 file changed, 18 insertions(+), 11 deletions(-) diff --git a/src/app/item-page/edit-item-page/item-page-edit-authorizations.guard.spec.ts b/src/app/item-page/edit-item-page/item-page-edit-authorizations.guard.spec.ts index dfee0c1150d..c85a17813ff 100644 --- a/src/app/item-page/edit-item-page/item-page-edit-authorizations.guard.spec.ts +++ b/src/app/item-page/edit-item-page/item-page-edit-authorizations.guard.spec.ts @@ -1,20 +1,18 @@ import { TestBed } from '@angular/core/testing'; -import { UrlTree, Router } from '@angular/router'; +import { Router, UrlTree } from '@angular/router'; import { Observable, of as observableOf } from 'rxjs'; import { AuthService } from 'src/app/core/auth/auth.service'; import { AuthorizationDataService } from 'src/app/core/data/feature-authorization/authorization-data.service'; import { FeatureID } from 'src/app/core/data/feature-authorization/feature-id'; -import { - Store, -} from '@ngrx/store'; +import { Store, } from '@ngrx/store'; import { itemPageEditAuthorizationsGuard } from './item-page-edit-authorizations.guard'; import { APP_DATA_SERVICES_MAP } from '../../../config/app-config.interface'; import { TranslateService } from '@ngx-translate/core'; import { getMockTranslateService } from '../../shared/mocks/translate.service.mock'; -import { - dsoPageSingleFeatureGuard -} from '../../core/data/feature-authorization/feature-authorization-guard/dso-page-single-feature.guard'; +import { ItemDataService } from '../../core/data/item-data.service'; +import { createSuccessfulRemoteDataObject$ } from '../../shared/remote-data.utils'; +import { Item } from '../../core/shared/item.model'; describe('itemPageEditAuthorizationsGuard', () => { let authorizationService: AuthorizationDataService; @@ -23,6 +21,10 @@ describe('itemPageEditAuthorizationsGuard', () => { let route; let parentRoute; let store: Store; + let itemService: ItemDataService; + let item: Item; + let uuid = '1234-abcdef-54321-fedcba'; + let itemSelfLink = 'test.url/1234-abcdef-54321-fedcba'; beforeEach(() => { @@ -35,6 +37,7 @@ describe('itemPageEditAuthorizationsGuard', () => { }); router = jasmine.createSpyObj('router', { parseUrl: {}, + navigateByUrl: undefined, }); authService = jasmine.createSpyObj('authService', { isAuthenticated: observableOf(true), @@ -46,10 +49,13 @@ describe('itemPageEditAuthorizationsGuard', () => { }, }; route = { - params: { - }, + params: {}, parent: parentRoute, }; + item = new Item(); + item.uuid = uuid; + item._links = { self: { href: itemSelfLink } } as any; + itemService = jasmine.createSpyObj('itemService', { findById: createSuccessfulRemoteDataObject$(item) }) TestBed.configureTestingModule({ providers: [ @@ -59,6 +65,7 @@ describe('itemPageEditAuthorizationsGuard', () => { { provide: Store, useValue: store }, { provide: APP_DATA_SERVICES_MAP, useValue: {} }, { provide: TranslateService, useValue: getMockTranslateService() }, + { provide: ItemDataService, useValue: itemService }, ], }); }); @@ -73,8 +80,8 @@ describe('itemPageEditAuthorizationsGuard', () => { console.log('result inside subscribe:', result); expect(authorizationService.isAuthorized).toHaveBeenCalledWith( FeatureID.CanManagePolicies, - 'fake-object-url', - 'fake-eperson-uuid', + itemSelfLink, // This value is retrieved from the itemDataService.findById's return item's self link + undefined, // dsoPageSingleFeatureGuard never provides a function to retrieve a person ID ); done(); }); From f1cac6e385e991c6e17bcadef2a824c32aa4f16b Mon Sep 17 00:00:00 2001 From: Zahraa Chreim Date: Fri, 10 May 2024 16:00:27 +0300 Subject: [PATCH 14/17] 114858: Add Tests for new Guards - Done --- ...itstream-page-authorizations.guard.spec.ts | 82 ++++++++++++++++ .../item-page-delete.guard.spec.ts | 94 +++++++++++++++++++ ...tem-page-edit-authorizations.guard.spec.ts | 24 +++-- .../item-page-move.guard.spec.ts | 94 +++++++++++++++++++ .../item-page-private.guard.spec.ts | 94 +++++++++++++++++++ 5 files changed, 378 insertions(+), 10 deletions(-) create mode 100644 src/app/bitstream-page/bitstream-page-authorizations.guard.spec.ts create mode 100644 src/app/item-page/edit-item-page/item-page-delete.guard.spec.ts create mode 100644 src/app/item-page/edit-item-page/item-page-move.guard.spec.ts create mode 100644 src/app/item-page/edit-item-page/item-page-private.guard.spec.ts diff --git a/src/app/bitstream-page/bitstream-page-authorizations.guard.spec.ts b/src/app/bitstream-page/bitstream-page-authorizations.guard.spec.ts new file mode 100644 index 00000000000..36760db787a --- /dev/null +++ b/src/app/bitstream-page/bitstream-page-authorizations.guard.spec.ts @@ -0,0 +1,82 @@ +import { TestBed } from '@angular/core/testing'; +import { + Router, + UrlTree, +} from '@angular/router'; +import { Store } from '@ngrx/store'; +import { TranslateService } from '@ngx-translate/core'; +import { + Observable, + of as observableOf, +} from 'rxjs'; +import { AuthService } from 'src/app/core/auth/auth.service'; +import { AuthorizationDataService } from 'src/app/core/data/feature-authorization/authorization-data.service'; +import { FeatureID } from 'src/app/core/data/feature-authorization/feature-id'; +import { bitstreamPageAuthorizationsGuard } from './bitstream-page-authorizations.guard'; +import { BitstreamDataService } from '../core/data/bitstream-data.service'; +import { Bitstream } from '../core/shared/bitstream.model'; +import { createSuccessfulRemoteDataObject$ } from '../shared/remote-data.utils'; + +describe('bitstreamPageAuthorizationsGuard', () => { + let authorizationService: AuthorizationDataService; + let authService: AuthService; + let router: Router; + let route; + let parentRoute; + let bitstreamService: BitstreamDataService; + let bitstream: Bitstream; + let uuid = '1234-abcdef-54321-fedcba'; + let bitstreamSelfLink = 'test.url/1234-abcdef-54321-fedcba'; + + beforeEach(() => { + authorizationService = jasmine.createSpyObj('authorizationService', { + isAuthorized: observableOf(true), + }); + router = jasmine.createSpyObj('router', { + parseUrl: {}, + navigateByUrl: undefined, + }); + authService = jasmine.createSpyObj('authService', { + isAuthenticated: observableOf(true), + }); + + parentRoute = { + params: { + id: '3e1a5327-dabb-41ff-af93-e6cab9d032f0', + }, + }; + route = { + params: {}, + parent: parentRoute, + }; + bitstream = new Bitstream(); + bitstream.uuid = uuid; + bitstream._links = { self: { href: bitstreamSelfLink } } as any; + bitstreamService = jasmine.createSpyObj('bitstreamService', { findById: createSuccessfulRemoteDataObject$(bitstream) }); + + TestBed.configureTestingModule({ + providers: [ + { provide: AuthorizationDataService, useValue: authorizationService }, + { provide: Router, useValue: router }, + { provide: AuthService, useValue: authService }, + { provide: BitstreamDataService, useValue: bitstreamService }, + ], + }); + }); + + it('should call authorizationService.isAuthorized with the appropriate arguments', (done) => { + const result$ = TestBed.runInInjectionContext(() => { + return bitstreamPageAuthorizationsGuard(route, { url: 'current-url' } as any); + }) as Observable; + + result$.subscribe((result) => { + expect(authorizationService.isAuthorized).toHaveBeenCalledWith( + FeatureID.CanManagePolicies, + bitstreamSelfLink, + undefined, + ); + done(); + }); + + }); +}); diff --git a/src/app/item-page/edit-item-page/item-page-delete.guard.spec.ts b/src/app/item-page/edit-item-page/item-page-delete.guard.spec.ts new file mode 100644 index 00000000000..d2d7954c6f5 --- /dev/null +++ b/src/app/item-page/edit-item-page/item-page-delete.guard.spec.ts @@ -0,0 +1,94 @@ +import { TestBed } from '@angular/core/testing'; +import { + Router, + UrlTree, +} from '@angular/router'; +import { Store } from '@ngrx/store'; +import { TranslateService } from '@ngx-translate/core'; +import { + Observable, + of as observableOf, +} from 'rxjs'; +import { AuthService } from 'src/app/core/auth/auth.service'; +import { AuthorizationDataService } from 'src/app/core/data/feature-authorization/authorization-data.service'; +import { FeatureID } from 'src/app/core/data/feature-authorization/feature-id'; + +import { APP_DATA_SERVICES_MAP } from '../../../config/app-config.interface'; +import { ItemDataService } from '../../core/data/item-data.service'; +import { Item } from '../../core/shared/item.model'; +import { getMockTranslateService } from '../../shared/mocks/translate.service.mock'; +import { createSuccessfulRemoteDataObject$ } from '../../shared/remote-data.utils'; +import { itemPageDeleteGuard } from './item-page-delete.guard'; + +describe('itemPageDeleteGuard', () => { + let authorizationService: AuthorizationDataService; + let authService: AuthService; + let router: Router; + let route; + let parentRoute; + let store: Store; + let itemService: ItemDataService; + let item: Item; + let uuid = '1234-abcdef-54321-fedcba'; + let itemSelfLink = 'test.url/1234-abcdef-54321-fedcba'; + + beforeEach(() => { + + store = jasmine.createSpyObj('store', { + dispatch: {}, + pipe: observableOf(true), + }); + authorizationService = jasmine.createSpyObj('authorizationService', { + isAuthorized: observableOf(true), + }); + router = jasmine.createSpyObj('router', { + parseUrl: {}, + navigateByUrl: undefined, + }); + authService = jasmine.createSpyObj('authService', { + isAuthenticated: observableOf(true), + }); + + parentRoute = { + params: { + id: '3e1a5327-dabb-41ff-af93-e6cab9d032f0', + }, + }; + route = { + params: {}, + parent: parentRoute, + }; + item = new Item(); + item.uuid = uuid; + item._links = { self: { href: itemSelfLink } } as any; + itemService = jasmine.createSpyObj('itemService', { findById: createSuccessfulRemoteDataObject$(item) }); + + TestBed.configureTestingModule({ + providers: [ + { provide: AuthorizationDataService, useValue: authorizationService }, + { provide: Router, useValue: router }, + { provide: AuthService, useValue: authService }, + { provide: Store, useValue: store }, + { provide: APP_DATA_SERVICES_MAP, useValue: {} }, + { provide: TranslateService, useValue: getMockTranslateService() }, + { provide: ItemDataService, useValue: itemService }, + ], + }); + }); + + it('should call authorizationService.isAuthorized with the appropriate arguments', (done) => { + const result$ = TestBed.runInInjectionContext(() => { + return itemPageDeleteGuard(route, { url: 'current-url' } as any); + }) as Observable; + + result$.subscribe((result) => { + expect(authorizationService.isAuthorized).toHaveBeenCalledWith( + FeatureID.CanDelete, + itemSelfLink, // This value is retrieved from the itemDataService.findById's return item's self link + undefined, // dsoPageSingleFeatureGuard never provides a function to retrieve a person ID + ); + done(); + }); + + }); +}); diff --git a/src/app/item-page/edit-item-page/item-page-edit-authorizations.guard.spec.ts b/src/app/item-page/edit-item-page/item-page-edit-authorizations.guard.spec.ts index c85a17813ff..f03e79ad364 100644 --- a/src/app/item-page/edit-item-page/item-page-edit-authorizations.guard.spec.ts +++ b/src/app/item-page/edit-item-page/item-page-edit-authorizations.guard.spec.ts @@ -1,18 +1,24 @@ import { TestBed } from '@angular/core/testing'; -import { Router, UrlTree } from '@angular/router'; -import { Observable, of as observableOf } from 'rxjs'; +import { + Router, + UrlTree, +} from '@angular/router'; +import { Store } from '@ngrx/store'; +import { TranslateService } from '@ngx-translate/core'; +import { + Observable, + of as observableOf, +} from 'rxjs'; import { AuthService } from 'src/app/core/auth/auth.service'; import { AuthorizationDataService } from 'src/app/core/data/feature-authorization/authorization-data.service'; import { FeatureID } from 'src/app/core/data/feature-authorization/feature-id'; -import { Store, } from '@ngrx/store'; -import { itemPageEditAuthorizationsGuard } from './item-page-edit-authorizations.guard'; import { APP_DATA_SERVICES_MAP } from '../../../config/app-config.interface'; -import { TranslateService } from '@ngx-translate/core'; -import { getMockTranslateService } from '../../shared/mocks/translate.service.mock'; import { ItemDataService } from '../../core/data/item-data.service'; -import { createSuccessfulRemoteDataObject$ } from '../../shared/remote-data.utils'; import { Item } from '../../core/shared/item.model'; +import { getMockTranslateService } from '../../shared/mocks/translate.service.mock'; +import { createSuccessfulRemoteDataObject$ } from '../../shared/remote-data.utils'; +import { itemPageEditAuthorizationsGuard } from './item-page-edit-authorizations.guard'; describe('itemPageEditAuthorizationsGuard', () => { let authorizationService: AuthorizationDataService; @@ -55,7 +61,7 @@ describe('itemPageEditAuthorizationsGuard', () => { item = new Item(); item.uuid = uuid; item._links = { self: { href: itemSelfLink } } as any; - itemService = jasmine.createSpyObj('itemService', { findById: createSuccessfulRemoteDataObject$(item) }) + itemService = jasmine.createSpyObj('itemService', { findById: createSuccessfulRemoteDataObject$(item) }); TestBed.configureTestingModule({ providers: [ @@ -75,9 +81,7 @@ describe('itemPageEditAuthorizationsGuard', () => { return itemPageEditAuthorizationsGuard(route, { url: 'current-url' } as any); }) as Observable; - console.log('will subscribe'); result$.subscribe((result) => { - console.log('result inside subscribe:', result); expect(authorizationService.isAuthorized).toHaveBeenCalledWith( FeatureID.CanManagePolicies, itemSelfLink, // This value is retrieved from the itemDataService.findById's return item's self link diff --git a/src/app/item-page/edit-item-page/item-page-move.guard.spec.ts b/src/app/item-page/edit-item-page/item-page-move.guard.spec.ts new file mode 100644 index 00000000000..d30f23817ac --- /dev/null +++ b/src/app/item-page/edit-item-page/item-page-move.guard.spec.ts @@ -0,0 +1,94 @@ +import { TestBed } from '@angular/core/testing'; +import { + Router, + UrlTree, +} from '@angular/router'; +import { Store } from '@ngrx/store'; +import { TranslateService } from '@ngx-translate/core'; +import { + Observable, + of as observableOf, +} from 'rxjs'; +import { AuthService } from 'src/app/core/auth/auth.service'; +import { AuthorizationDataService } from 'src/app/core/data/feature-authorization/authorization-data.service'; +import { FeatureID } from 'src/app/core/data/feature-authorization/feature-id'; + +import { APP_DATA_SERVICES_MAP } from '../../../config/app-config.interface'; +import { ItemDataService } from '../../core/data/item-data.service'; +import { Item } from '../../core/shared/item.model'; +import { getMockTranslateService } from '../../shared/mocks/translate.service.mock'; +import { createSuccessfulRemoteDataObject$ } from '../../shared/remote-data.utils'; +import { itemPageMoveGuard } from './item-page-move.guard'; + +describe('itemPageMoveGuard', () => { + let authorizationService: AuthorizationDataService; + let authService: AuthService; + let router: Router; + let route; + let parentRoute; + let store: Store; + let itemService: ItemDataService; + let item: Item; + let uuid = '1234-abcdef-54321-fedcba'; + let itemSelfLink = 'test.url/1234-abcdef-54321-fedcba'; + + beforeEach(() => { + + store = jasmine.createSpyObj('store', { + dispatch: {}, + pipe: observableOf(true), + }); + authorizationService = jasmine.createSpyObj('authorizationService', { + isAuthorized: observableOf(true), + }); + router = jasmine.createSpyObj('router', { + parseUrl: {}, + navigateByUrl: undefined, + }); + authService = jasmine.createSpyObj('authService', { + isAuthenticated: observableOf(true), + }); + + parentRoute = { + params: { + id: '3e1a5327-dabb-41ff-af93-e6cab9d032f0', + }, + }; + route = { + params: {}, + parent: parentRoute, + }; + item = new Item(); + item.uuid = uuid; + item._links = { self: { href: itemSelfLink } } as any; + itemService = jasmine.createSpyObj('itemService', { findById: createSuccessfulRemoteDataObject$(item) }); + + TestBed.configureTestingModule({ + providers: [ + { provide: AuthorizationDataService, useValue: authorizationService }, + { provide: Router, useValue: router }, + { provide: AuthService, useValue: authService }, + { provide: Store, useValue: store }, + { provide: APP_DATA_SERVICES_MAP, useValue: {} }, + { provide: TranslateService, useValue: getMockTranslateService() }, + { provide: ItemDataService, useValue: itemService }, + ], + }); + }); + + it('should call authorizationService.isAuthorized with the appropriate arguments', (done) => { + const result$ = TestBed.runInInjectionContext(() => { + return itemPageMoveGuard(route, { url: 'current-url' } as any); + }) as Observable; + + result$.subscribe((result) => { + expect(authorizationService.isAuthorized).toHaveBeenCalledWith( + FeatureID.CanMove, + itemSelfLink, // This value is retrieved from the itemDataService.findById's return item's self link + undefined, // dsoPageSingleFeatureGuard never provides a function to retrieve a person ID + ); + done(); + }); + + }); +}); diff --git a/src/app/item-page/edit-item-page/item-page-private.guard.spec.ts b/src/app/item-page/edit-item-page/item-page-private.guard.spec.ts new file mode 100644 index 00000000000..3e573923d83 --- /dev/null +++ b/src/app/item-page/edit-item-page/item-page-private.guard.spec.ts @@ -0,0 +1,94 @@ +import { TestBed } from '@angular/core/testing'; +import { + Router, + UrlTree, +} from '@angular/router'; +import { Store } from '@ngrx/store'; +import { TranslateService } from '@ngx-translate/core'; +import { + Observable, + of as observableOf, +} from 'rxjs'; +import { AuthService } from 'src/app/core/auth/auth.service'; +import { AuthorizationDataService } from 'src/app/core/data/feature-authorization/authorization-data.service'; +import { FeatureID } from 'src/app/core/data/feature-authorization/feature-id'; + +import { APP_DATA_SERVICES_MAP } from '../../../config/app-config.interface'; +import { ItemDataService } from '../../core/data/item-data.service'; +import { Item } from '../../core/shared/item.model'; +import { getMockTranslateService } from '../../shared/mocks/translate.service.mock'; +import { createSuccessfulRemoteDataObject$ } from '../../shared/remote-data.utils'; +import { itemPagePrivateGuard } from './item-page-private.guard'; + +describe('itemPagePrivateGuard', () => { + let authorizationService: AuthorizationDataService; + let authService: AuthService; + let router: Router; + let route; + let parentRoute; + let store: Store; + let itemService: ItemDataService; + let item: Item; + let uuid = '1234-abcdef-54321-fedcba'; + let itemSelfLink = 'test.url/1234-abcdef-54321-fedcba'; + + beforeEach(() => { + + store = jasmine.createSpyObj('store', { + dispatch: {}, + pipe: observableOf(true), + }); + authorizationService = jasmine.createSpyObj('authorizationService', { + isAuthorized: observableOf(true), + }); + router = jasmine.createSpyObj('router', { + parseUrl: {}, + navigateByUrl: undefined, + }); + authService = jasmine.createSpyObj('authService', { + isAuthenticated: observableOf(true), + }); + + parentRoute = { + params: { + id: '3e1a5327-dabb-41ff-af93-e6cab9d032f0', + }, + }; + route = { + params: {}, + parent: parentRoute, + }; + item = new Item(); + item.uuid = uuid; + item._links = { self: { href: itemSelfLink } } as any; + itemService = jasmine.createSpyObj('itemService', { findById: createSuccessfulRemoteDataObject$(item) }); + + TestBed.configureTestingModule({ + providers: [ + { provide: AuthorizationDataService, useValue: authorizationService }, + { provide: Router, useValue: router }, + { provide: AuthService, useValue: authService }, + { provide: Store, useValue: store }, + { provide: APP_DATA_SERVICES_MAP, useValue: {} }, + { provide: TranslateService, useValue: getMockTranslateService() }, + { provide: ItemDataService, useValue: itemService }, + ], + }); + }); + + it('should call authorizationService.isAuthorized with the appropriate arguments', (done) => { + const result$ = TestBed.runInInjectionContext(() => { + return itemPagePrivateGuard(route, { url: 'current-url' } as any); + }) as Observable; + + result$.subscribe((result) => { + expect(authorizationService.isAuthorized).toHaveBeenCalledWith( + FeatureID.CanMakePrivate, + itemSelfLink, // This value is retrieved from the itemDataService.findById's return item's self link + undefined, // dsoPageSingleFeatureGuard never provides a function to retrieve a person ID + ); + done(); + }); + + }); +}); From ea928378f8819fb70dea60b9fbc2fa2c03d5f177 Mon Sep 17 00:00:00 2001 From: Zahraa Chreim Date: Fri, 10 May 2024 17:13:34 +0300 Subject: [PATCH 15/17] 114858: Fix Lint Errors --- src/app/access-control/access-control-routes.ts | 9 ++------- .../admin-notify-dashboard-routes.ts | 9 ++------- src/app/app-routes.ts | 8 ++------ .../bitstream-page-authorizations.guard.spec.ts | 5 ++--- src/app/bitstream-page/bitstream-page-routes.ts | 2 +- .../item-page/edit-item-page/edit-item-page-routes.ts | 8 ++++---- 6 files changed, 13 insertions(+), 28 deletions(-) diff --git a/src/app/access-control/access-control-routes.ts b/src/app/access-control/access-control-routes.ts index ef36a19052c..cb8c4521171 100644 --- a/src/app/access-control/access-control-routes.ts +++ b/src/app/access-control/access-control-routes.ts @@ -1,14 +1,12 @@ import { AbstractControl } from '@angular/forms'; -import { - mapToCanActivate, - Route, -} from '@angular/router'; +import { Route } from '@angular/router'; import { DYNAMIC_ERROR_MESSAGES_MATCHER, DynamicErrorMessagesMatcher, } from '@ng-dynamic-forms/core'; import { i18nBreadcrumbResolver } from '../core/breadcrumbs/i18n-breadcrumb.resolver'; +import { siteAdministratorGuard } from '../core/data/feature-authorization/feature-authorization-guard/site-administrator.guard'; import { EPERSON_PATH, GROUP_PATH, @@ -19,9 +17,6 @@ import { EPersonFormComponent } from './epeople-registry/eperson-form/eperson-fo import { EPersonResolver } from './epeople-registry/eperson-resolver.service'; import { GroupFormComponent } from './group-registry/group-form/group-form.component'; import { GroupsRegistryComponent } from './group-registry/groups-registry.component'; -import { - siteAdministratorGuard -} from '../core/data/feature-authorization/feature-authorization-guard/site-administrator.guard'; /** * Condition for displaying error messages on email form field diff --git a/src/app/admin/admin-notify-dashboard/admin-notify-dashboard-routes.ts b/src/app/admin/admin-notify-dashboard/admin-notify-dashboard-routes.ts index b1fd827ba80..0316913cf62 100644 --- a/src/app/admin/admin-notify-dashboard/admin-notify-dashboard-routes.ts +++ b/src/app/admin/admin-notify-dashboard/admin-notify-dashboard-routes.ts @@ -1,16 +1,11 @@ -import { - mapToCanActivate, - Route, -} from '@angular/router'; +import { Route } from '@angular/router'; import { i18nBreadcrumbResolver } from '../../core/breadcrumbs/i18n-breadcrumb.resolver'; import { notifyInfoGuard } from '../../core/coar-notify/notify-info/notify-info.guard'; +import { siteAdministratorGuard } from '../../core/data/feature-authorization/feature-authorization-guard/site-administrator.guard'; import { AdminNotifyDashboardComponent } from './admin-notify-dashboard.component'; import { AdminNotifyIncomingComponent } from './admin-notify-logs/admin-notify-incoming/admin-notify-incoming.component'; import { AdminNotifyOutgoingComponent } from './admin-notify-logs/admin-notify-outgoing/admin-notify-outgoing.component'; -import { - siteAdministratorGuard -} from '../../core/data/feature-authorization/feature-authorization-guard/site-administrator.guard'; export const ROUTES: Route[] = [ { diff --git a/src/app/app-routes.ts b/src/app/app-routes.ts index f54d01301bd..9f9dae2b6bc 100644 --- a/src/app/app-routes.ts +++ b/src/app/app-routes.ts @@ -26,6 +26,8 @@ import { COLLECTION_MODULE_PATH } from './collection-page/collection-page-routin import { COMMUNITY_MODULE_PATH } from './community-page/community-page-routing-paths'; import { authBlockingGuard } from './core/auth/auth-blocking.guard'; import { authenticatedGuard } from './core/auth/authenticated.guard'; +import { groupAdministratorGuard } from './core/data/feature-authorization/feature-authorization-guard/group-administrator.guard'; +import { siteAdministratorGuard } from './core/data/feature-authorization/feature-authorization-guard/site-administrator.guard'; import { siteRegisterGuard } from './core/data/feature-authorization/feature-authorization-guard/site-register.guard'; import { EndUserAgreementCurrentUserGuard } from './core/end-user-agreement/end-user-agreement-current-user.guard'; import { reloadGuard } from './core/reload/reload.guard'; @@ -41,12 +43,6 @@ import { ThemedPageNotFoundComponent } from './pagenotfound/themed-pagenotfound. import { PROCESS_MODULE_PATH } from './process-page/process-page-routing.paths'; import { provideSubmissionState } from './submission/provide-submission-state'; import { SUGGESTION_MODULE_PATH } from './suggestions-page/suggestions-page-routing-paths'; -import { - siteAdministratorGuard -} from './core/data/feature-authorization/feature-authorization-guard/site-administrator.guard'; -import { - groupAdministratorGuard -} from './core/data/feature-authorization/feature-authorization-guard/group-administrator.guard'; export const APP_ROUTES: Route[] = [ { path: INTERNAL_SERVER_ERROR, component: ThemedPageInternalServerErrorComponent }, diff --git a/src/app/bitstream-page/bitstream-page-authorizations.guard.spec.ts b/src/app/bitstream-page/bitstream-page-authorizations.guard.spec.ts index 36760db787a..9b4d019058e 100644 --- a/src/app/bitstream-page/bitstream-page-authorizations.guard.spec.ts +++ b/src/app/bitstream-page/bitstream-page-authorizations.guard.spec.ts @@ -3,8 +3,6 @@ import { Router, UrlTree, } from '@angular/router'; -import { Store } from '@ngrx/store'; -import { TranslateService } from '@ngx-translate/core'; import { Observable, of as observableOf, @@ -12,10 +10,11 @@ import { import { AuthService } from 'src/app/core/auth/auth.service'; import { AuthorizationDataService } from 'src/app/core/data/feature-authorization/authorization-data.service'; import { FeatureID } from 'src/app/core/data/feature-authorization/feature-id'; -import { bitstreamPageAuthorizationsGuard } from './bitstream-page-authorizations.guard'; + import { BitstreamDataService } from '../core/data/bitstream-data.service'; import { Bitstream } from '../core/shared/bitstream.model'; import { createSuccessfulRemoteDataObject$ } from '../shared/remote-data.utils'; +import { bitstreamPageAuthorizationsGuard } from './bitstream-page-authorizations.guard'; describe('bitstreamPageAuthorizationsGuard', () => { let authorizationService: AuthorizationDataService; diff --git a/src/app/bitstream-page/bitstream-page-routes.ts b/src/app/bitstream-page/bitstream-page-routes.ts index 9312e01de7d..8b0d9d3322c 100644 --- a/src/app/bitstream-page/bitstream-page-routes.ts +++ b/src/app/bitstream-page/bitstream-page-routes.ts @@ -10,9 +10,9 @@ import { resourcePolicyTargetResolver } from '../shared/resource-policies/resolv import { BitstreamAuthorizationsComponent } from './bitstream-authorizations/bitstream-authorizations.component'; import { BitstreamDownloadPageComponent } from './bitstream-download-page/bitstream-download-page.component'; import { bitstreamPageResolver } from './bitstream-page.resolver'; +import { bitstreamPageAuthorizationsGuard } from './bitstream-page-authorizations.guard'; import { ThemedEditBitstreamPageComponent } from './edit-bitstream-page/themed-edit-bitstream-page.component'; import { legacyBitstreamUrlResolver } from './legacy-bitstream-url.resolver'; -import { bitstreamPageAuthorizationsGuard } from './bitstream-page-authorizations.guard'; const EDIT_BITSTREAM_PATH = ':id/edit'; const EDIT_BITSTREAM_AUTHORIZATIONS_PATH = ':id/authorizations'; diff --git a/src/app/item-page/edit-item-page/edit-item-page-routes.ts b/src/app/item-page/edit-item-page/edit-item-page-routes.ts index fd6edb7ed0b..a7189f98881 100644 --- a/src/app/item-page/edit-item-page/edit-item-page-routes.ts +++ b/src/app/item-page/edit-item-page/edit-item-page-routes.ts @@ -28,7 +28,11 @@ import { itemPageAccessControlGuard } from './item-page-access-control.guard'; import { itemPageBitstreamsGuard } from './item-page-bitstreams.guard'; import { itemPageCollectionMapperGuard } from './item-page-collection-mapper.guard'; import { itemPageCurateGuard } from './item-page-curate.guard'; +import { itemPageDeleteGuard } from './item-page-delete.guard'; +import { itemPageEditAuthorizationsGuard } from './item-page-edit-authorizations.guard'; import { itemPageMetadataGuard } from './item-page-metadata.guard'; +import { itemPageMoveGuard } from './item-page-move.guard'; +import { itemPagePrivateGuard } from './item-page-private.guard'; import { itemPageRegisterDoiGuard } from './item-page-register-doi.guard'; import { itemPageReinstateGuard } from './item-page-reinstate.guard'; import { itemPageRelationshipsGuard } from './item-page-relationships.guard'; @@ -43,10 +47,6 @@ import { ItemRelationshipsComponent } from './item-relationships/item-relationsh import { ThemedItemStatusComponent } from './item-status/themed-item-status.component'; import { ItemVersionHistoryComponent } from './item-version-history/item-version-history.component'; import { ItemWithdrawComponent } from './item-withdraw/item-withdraw.component'; -import { itemPageEditAuthorizationsGuard } from './item-page-edit-authorizations.guard'; -import { itemPagePrivateGuard } from './item-page-private.guard'; -import { itemPageMoveGuard } from './item-page-move.guard'; -import { itemPageDeleteGuard } from './item-page-delete.guard'; /** * Routing module that handles the routing for the Edit Item page administrator functionality From ffe30628ec0ff3915dc145060d714a23075bb041 Mon Sep 17 00:00:00 2001 From: Zahraa Chreim Date: Thu, 23 May 2024 08:58:37 +0300 Subject: [PATCH 16/17] 114858: Fix Com/Coll admins' management functionality for roles/groups --- src/app/access-control/access-control-routes.ts | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/src/app/access-control/access-control-routes.ts b/src/app/access-control/access-control-routes.ts index cb8c4521171..07b6f6c4ff4 100644 --- a/src/app/access-control/access-control-routes.ts +++ b/src/app/access-control/access-control-routes.ts @@ -6,6 +6,7 @@ import { } from '@ng-dynamic-forms/core'; import { i18nBreadcrumbResolver } from '../core/breadcrumbs/i18n-breadcrumb.resolver'; +import { groupAdministratorGuard } from '../core/data/feature-authorization/feature-authorization-guard/group-administrator.guard'; import { siteAdministratorGuard } from '../core/data/feature-authorization/feature-authorization-guard/site-administrator.guard'; import { EPERSON_PATH, @@ -16,6 +17,7 @@ import { EPeopleRegistryComponent } from './epeople-registry/epeople-registry.co import { EPersonFormComponent } from './epeople-registry/eperson-form/eperson-form.component'; import { EPersonResolver } from './epeople-registry/eperson-resolver.service'; import { GroupFormComponent } from './group-registry/group-form/group-form.component'; +import { groupPageGuard } from './group-registry/group-page.guard'; import { GroupsRegistryComponent } from './group-registry/groups-registry.component'; /** @@ -23,7 +25,7 @@ import { GroupsRegistryComponent } from './group-registry/groups-registry.compon */ export const ValidateEmailErrorStateMatcher: DynamicErrorMessagesMatcher = (control: AbstractControl, model: any, hasFocus: boolean) => { - return (control.touched && !hasFocus) || (control.errors?.emailTaken && hasFocus); + return ( control.touched && !hasFocus ) || ( control.errors?.emailTaken && hasFocus ); }; const providers = [ @@ -72,7 +74,7 @@ export const ROUTES: Route[] = [ }, providers, data: { title: 'admin.access-control.groups.title', breadcrumbKey: 'admin.access-control.groups' }, - canActivate: [siteAdministratorGuard], + canActivate: [groupAdministratorGuard], }, { path: `${GROUP_PATH}/create`, @@ -85,7 +87,7 @@ export const ROUTES: Route[] = [ title: 'admin.access-control.groups.title.addGroup', breadcrumbKey: 'admin.access-control.groups.addGroup', }, - canActivate: [siteAdministratorGuard], + canActivate: [groupAdministratorGuard], }, { path: `${GROUP_PATH}/:groupId/edit`, @@ -98,7 +100,7 @@ export const ROUTES: Route[] = [ title: 'admin.access-control.groups.title.singleGroup', breadcrumbKey: 'admin.access-control.groups.singleGroup', }, - canActivate: [siteAdministratorGuard], + canActivate: [groupPageGuard], }, { path: 'bulk-access', From 553bada77d4a47415f70071f35624938f932a21d Mon Sep 17 00:00:00 2001 From: lotte Date: Thu, 23 May 2024 15:30:15 +0200 Subject: [PATCH 17/17] 115279: Replaced EndUserAgreeMent guards with functional guards --- src/app/app-routes.ts | 55 +++++++++---------- .../abstract-end-user-agreement.guard.ts | 45 --------------- .../end-user-agreement-cookie.guard.spec.ts | 27 ++++++--- .../end-user-agreement-cookie.guard.ts | 38 +++++-------- ...-user-agreement-current-user.guard.spec.ts | 46 ++++++++++++---- .../end-user-agreement-current-user.guard.ts | 45 ++++++--------- .../end-user-agreement.guard.ts | 34 ++++++++++++ src/app/register-page/register-page-routes.ts | 9 +-- 8 files changed, 152 insertions(+), 147 deletions(-) delete mode 100644 src/app/core/end-user-agreement/abstract-end-user-agreement.guard.ts create mode 100644 src/app/core/end-user-agreement/end-user-agreement.guard.ts diff --git a/src/app/app-routes.ts b/src/app/app-routes.ts index 9f9dae2b6bc..29a78364b53 100644 --- a/src/app/app-routes.ts +++ b/src/app/app-routes.ts @@ -1,6 +1,5 @@ import { InMemoryScrollingOptions, - mapToCanActivate, Route, RouterConfigOptions, } from '@angular/router'; @@ -29,7 +28,7 @@ import { authenticatedGuard } from './core/auth/authenticated.guard'; import { groupAdministratorGuard } from './core/data/feature-authorization/feature-authorization-guard/group-administrator.guard'; import { siteAdministratorGuard } from './core/data/feature-authorization/feature-authorization-guard/site-administrator.guard'; import { siteRegisterGuard } from './core/data/feature-authorization/feature-authorization-guard/site-register.guard'; -import { EndUserAgreementCurrentUserGuard } from './core/end-user-agreement/end-user-agreement-current-user.guard'; +import { endUserAgreementCurrentUserGuard } from './core/end-user-agreement/end-user-agreement-current-user.guard'; import { reloadGuard } from './core/reload/reload.guard'; import { forgotPasswordCheckGuard } from './core/rest-property/forgot-password-check-guard.guard'; import { ServerCheckGuard } from './core/server-check/server-check.guard'; @@ -66,25 +65,25 @@ export const APP_ROUTES: Route[] = [ .then((m) => m.ROUTES), data: { showBreadcrumbs: false }, providers: [provideSuggestionNotificationsState()], - canActivate: mapToCanActivate([EndUserAgreementCurrentUserGuard]), + canActivate: [endUserAgreementCurrentUserGuard], }, { path: 'community-list', loadChildren: () => import('./community-list-page/community-list-page-routes') .then((m) => m.ROUTES), - canActivate: mapToCanActivate([EndUserAgreementCurrentUserGuard]), + canActivate: [endUserAgreementCurrentUserGuard], }, { path: 'id', loadChildren: () => import('./lookup-by-id/lookup-by-id-routes') .then((m) => m.ROUTES), - canActivate: mapToCanActivate([EndUserAgreementCurrentUserGuard]), + canActivate: [endUserAgreementCurrentUserGuard], }, { path: 'handle', loadChildren: () => import('./lookup-by-id/lookup-by-id-routes') .then((m) => m.ROUTES), - canActivate: mapToCanActivate([EndUserAgreementCurrentUserGuard]), + canActivate: [endUserAgreementCurrentUserGuard], }, { path: REGISTER_PATH, @@ -96,75 +95,75 @@ export const APP_ROUTES: Route[] = [ path: FORGOT_PASSWORD_PATH, loadChildren: () => import('./forgot-password/forgot-password-routes') .then((m) => m.ROUTES), - canActivate: [EndUserAgreementCurrentUserGuard, forgotPasswordCheckGuard], + canActivate: [endUserAgreementCurrentUserGuard, forgotPasswordCheckGuard], }, { path: COMMUNITY_MODULE_PATH, loadChildren: () => import('./community-page/community-page-routes') .then((m) => m.ROUTES), - canActivate: mapToCanActivate([EndUserAgreementCurrentUserGuard]), + canActivate: [endUserAgreementCurrentUserGuard], }, { path: COLLECTION_MODULE_PATH, loadChildren: () => import('./collection-page/collection-page-routes') .then((m) => m.ROUTES), - canActivate: mapToCanActivate([EndUserAgreementCurrentUserGuard]), + canActivate: [endUserAgreementCurrentUserGuard], }, { path: ITEM_MODULE_PATH, loadChildren: () => import('./item-page/item-page-routes') .then((m) => m.ROUTES), - canActivate: mapToCanActivate([EndUserAgreementCurrentUserGuard]), + canActivate: [endUserAgreementCurrentUserGuard], }, { path: 'entities/:entity-type', loadChildren: () => import('./item-page/item-page-routes') .then((m) => m.ROUTES), - canActivate: mapToCanActivate([EndUserAgreementCurrentUserGuard]), + canActivate: [endUserAgreementCurrentUserGuard], }, { path: LEGACY_BITSTREAM_MODULE_PATH, loadChildren: () => import('./bitstream-page/bitstream-page-routes') .then((m) => m.ROUTES), - canActivate: mapToCanActivate([EndUserAgreementCurrentUserGuard]), + canActivate: [endUserAgreementCurrentUserGuard], }, { path: BITSTREAM_MODULE_PATH, loadChildren: () => import('./bitstream-page/bitstream-page-routes') .then((m) => m.ROUTES), - canActivate: mapToCanActivate([EndUserAgreementCurrentUserGuard]), + canActivate: [endUserAgreementCurrentUserGuard], }, { path: 'mydspace', loadChildren: () => import('./my-dspace-page/my-dspace-page-routes') .then((m) => m.ROUTES), providers: [provideSuggestionNotificationsState()], - canActivate: [authenticatedGuard, ...mapToCanActivate([EndUserAgreementCurrentUserGuard])], + canActivate: [authenticatedGuard, endUserAgreementCurrentUserGuard], }, { path: 'search', loadChildren: () => import('./search-page/search-page-routes') .then((m) => m.ROUTES), - canActivate: mapToCanActivate([EndUserAgreementCurrentUserGuard]), + canActivate: [endUserAgreementCurrentUserGuard], }, { path: 'browse', loadChildren: () => import('./browse-by/browse-by-page-routes') .then((m) => m.ROUTES), - canActivate: mapToCanActivate([EndUserAgreementCurrentUserGuard]), + canActivate: [endUserAgreementCurrentUserGuard], }, { path: ADMIN_MODULE_PATH, loadChildren: () => import('./admin/admin-routes') .then((m) => m.ROUTES), - canActivate: [siteAdministratorGuard, EndUserAgreementCurrentUserGuard], + canActivate: [siteAdministratorGuard, endUserAgreementCurrentUserGuard], }, { path: NOTIFICATIONS_MODULE_PATH, loadChildren: () => import('./quality-assurance-notifications-pages/notifications-pages-routes') .then((m) => m.ROUTES), providers: [provideSuggestionNotificationsState()], - canActivate: [authenticatedGuard, ...mapToCanActivate([EndUserAgreementCurrentUserGuard])], + canActivate: [authenticatedGuard, endUserAgreementCurrentUserGuard], }, { path: 'login', @@ -181,47 +180,47 @@ export const APP_ROUTES: Route[] = [ loadChildren: () => import('./submit-page/submit-page-routes') .then((m) => m.ROUTES), providers: [provideSubmissionState()], - canActivate: mapToCanActivate([EndUserAgreementCurrentUserGuard]), + canActivate: [endUserAgreementCurrentUserGuard], }, { path: 'import-external', loadChildren: () => import('./import-external-page/import-external-page-routes') .then((m) => m.ROUTES), - canActivate: mapToCanActivate([EndUserAgreementCurrentUserGuard]), + canActivate: [endUserAgreementCurrentUserGuard], }, { path: 'workspaceitems', loadChildren: () => import('./workspaceitems-edit-page/workspaceitems-edit-page-routes') .then((m) => m.ROUTES), providers: [provideSubmissionState()], - canActivate: mapToCanActivate([EndUserAgreementCurrentUserGuard]), + canActivate: [endUserAgreementCurrentUserGuard], }, { path: WORKFLOW_ITEM_MODULE_PATH, providers: [provideSubmissionState()], loadChildren: () => import('./workflowitems-edit-page/workflowitems-edit-page-routes') .then((m) => m.ROUTES), - canActivate: mapToCanActivate([EndUserAgreementCurrentUserGuard]), + canActivate: [endUserAgreementCurrentUserGuard], }, { path: PROFILE_MODULE_PATH, loadChildren: () => import('./profile-page/profile-page-routes') .then((m) => m.ROUTES), providers: [provideSuggestionNotificationsState()], - canActivate: [authenticatedGuard, ...mapToCanActivate([EndUserAgreementCurrentUserGuard])], + canActivate: [authenticatedGuard, endUserAgreementCurrentUserGuard], }, { path: PROCESS_MODULE_PATH, loadChildren: () => import('./process-page/process-page-routes') .then((m) => m.ROUTES), - canActivate: [authenticatedGuard, ...mapToCanActivate([EndUserAgreementCurrentUserGuard])], + canActivate: [authenticatedGuard, endUserAgreementCurrentUserGuard], }, { path: SUGGESTION_MODULE_PATH, loadChildren: () => import('./suggestions-page/suggestions-page-routes') .then((m) => m.ROUTES), providers: [provideSuggestionNotificationsState()], - canActivate: [authenticatedGuard, ...mapToCanActivate([EndUserAgreementCurrentUserGuard])], + canActivate: [authenticatedGuard, endUserAgreementCurrentUserGuard], }, { path: INFO_MODULE_PATH, @@ -230,7 +229,7 @@ export const APP_ROUTES: Route[] = [ { path: REQUEST_COPY_MODULE_PATH, loadChildren: () => import('./request-copy/request-copy-routes').then((m) => m.ROUTES), - canActivate: mapToCanActivate([EndUserAgreementCurrentUserGuard]), + canActivate: [endUserAgreementCurrentUserGuard], }, { path: FORBIDDEN_PATH, @@ -240,7 +239,7 @@ export const APP_ROUTES: Route[] = [ path: 'statistics', loadChildren: () => import('./statistics-page/statistics-page-routes') .then((m) => m.ROUTES), - canActivate: mapToCanActivate([EndUserAgreementCurrentUserGuard]), + canActivate: [endUserAgreementCurrentUserGuard], }, { path: HEALTH_PAGE_PATH, @@ -250,7 +249,7 @@ export const APP_ROUTES: Route[] = [ { path: ACCESS_CONTROL_MODULE_PATH, loadChildren: () => import('./access-control/access-control-routes').then((m) => m.ROUTES), - canActivate: [groupAdministratorGuard, EndUserAgreementCurrentUserGuard], + canActivate: [groupAdministratorGuard, endUserAgreementCurrentUserGuard], }, { path: 'subscriptions', diff --git a/src/app/core/end-user-agreement/abstract-end-user-agreement.guard.ts b/src/app/core/end-user-agreement/abstract-end-user-agreement.guard.ts deleted file mode 100644 index 2937011a380..00000000000 --- a/src/app/core/end-user-agreement/abstract-end-user-agreement.guard.ts +++ /dev/null @@ -1,45 +0,0 @@ -import { - ActivatedRouteSnapshot, - Router, - RouterStateSnapshot, - UrlTree, -} from '@angular/router'; -import { - Observable, - of as observableOf, -} from 'rxjs'; - -import { environment } from '../../../environments/environment'; -import { returnEndUserAgreementUrlTreeOnFalse } from '../shared/authorized.operators'; - -/** - * An abstract guard for redirecting users to the user agreement page if a certain condition is met - * That condition is defined by abstract method hasAccepted - */ -export abstract class AbstractEndUserAgreementGuard { - - constructor(protected router: Router) { - } - - /** - * True when the user agreement has been accepted - * The user will be redirected to the End User Agreement page if they haven't accepted it before - * A redirect URL will be provided with the navigation so the component can redirect the user back to the blocked route - * when they're finished accepting the agreement - */ - canActivate(route: ActivatedRouteSnapshot, state: RouterStateSnapshot): Observable { - if (!environment.info.enableEndUserAgreement) { - return observableOf(true); - } - return this.hasAccepted().pipe( - returnEndUserAgreementUrlTreeOnFalse(this.router, state.url), - ); - } - - /** - * This abstract method determines how the User Agreement has to be accepted before the user is allowed to visit - * the desired route - */ - abstract hasAccepted(): Observable; - -} diff --git a/src/app/core/end-user-agreement/end-user-agreement-cookie.guard.spec.ts b/src/app/core/end-user-agreement/end-user-agreement-cookie.guard.spec.ts index a801fb7a6fa..640859c7a42 100644 --- a/src/app/core/end-user-agreement/end-user-agreement-cookie.guard.spec.ts +++ b/src/app/core/end-user-agreement/end-user-agreement-cookie.guard.spec.ts @@ -1,13 +1,14 @@ +import { TestBed } from '@angular/core/testing'; import { Router, UrlTree, } from '@angular/router'; +import { Observable } from 'rxjs'; import { EndUserAgreementService } from './end-user-agreement.service'; -import { EndUserAgreementCookieGuard } from './end-user-agreement-cookie.guard'; +import { endUserAgreementCookieGuard } from './end-user-agreement-cookie.guard'; -describe('EndUserAgreementCookieGuard', () => { - let guard: EndUserAgreementCookieGuard; +describe('endUserAgreementCookieGuard', () => { let endUserAgreementService: EndUserAgreementService; let router: Router; @@ -21,14 +22,22 @@ describe('EndUserAgreementCookieGuard', () => { parseUrl: new UrlTree(), createUrlTree: new UrlTree(), }); - - guard = new EndUserAgreementCookieGuard(endUserAgreementService, router); + TestBed.configureTestingModule({ + providers: [ + { provide: Router, useValue: router }, + { provide: EndUserAgreementService, useValue: endUserAgreementService }, + ], + }); }); describe('canActivate', () => { describe('when the cookie has been accepted', () => { it('should return true', (done) => { - guard.canActivate(undefined, { url: Object.assign({ url: 'redirect' }) } as any).subscribe((result) => { + const result$ = TestBed.runInInjectionContext(() => { + return endUserAgreementCookieGuard(undefined, { url: Object.assign({ url: 'redirect' }) } as any); + }) as Observable; + + result$.subscribe((result) => { expect(result).toEqual(true); done(); }); @@ -41,7 +50,11 @@ describe('EndUserAgreementCookieGuard', () => { }); it('should return a UrlTree', (done) => { - guard.canActivate(undefined, Object.assign({ url: 'redirect' })).subscribe((result) => { + const result$ = TestBed.runInInjectionContext(() => { + return endUserAgreementCookieGuard(undefined, { url: Object.assign({ url: 'redirect' }) } as any); + }) as Observable; + + result$.subscribe((result) => { expect(result).toEqual(jasmine.any(UrlTree)); done(); }); diff --git a/src/app/core/end-user-agreement/end-user-agreement-cookie.guard.ts b/src/app/core/end-user-agreement/end-user-agreement-cookie.guard.ts index 3eccae3ca9e..2da38fb22dc 100644 --- a/src/app/core/end-user-agreement/end-user-agreement-cookie.guard.ts +++ b/src/app/core/end-user-agreement/end-user-agreement-cookie.guard.ts @@ -1,29 +1,19 @@ -import { Injectable } from '@angular/core'; -import { Router } from '@angular/router'; -import { - Observable, - of as observableOf, -} from 'rxjs'; +import { inject } from '@angular/core'; +import { CanActivateFn } from '@angular/router'; +import { of as observableOf } from 'rxjs'; -import { AbstractEndUserAgreementGuard } from './abstract-end-user-agreement.guard'; +import { endUserAgreementGuard } from './end-user-agreement.guard'; import { EndUserAgreementService } from './end-user-agreement.service'; + /** - * A guard redirecting users to the end agreement page when the user agreement cookie hasn't been accepted + * Guard for preventing unauthorized access to certain pages + * requiring the end user agreement to have been accepted in a cookie */ -@Injectable({ providedIn: 'root' }) -export class EndUserAgreementCookieGuard extends AbstractEndUserAgreementGuard { - - constructor(protected endUserAgreementService: EndUserAgreementService, - protected router: Router) { - super(router); - } - - /** - * True when the user agreement cookie has been accepted - */ - hasAccepted(): Observable { - return observableOf(this.endUserAgreementService.isCookieAccepted()); - } - -} +export const endUserAgreementCookieGuard: CanActivateFn = + endUserAgreementGuard( + () => { + const endUserAgreementService = inject(EndUserAgreementService); + return observableOf(endUserAgreementService.isCookieAccepted()); + }, + ); diff --git a/src/app/core/end-user-agreement/end-user-agreement-current-user.guard.spec.ts b/src/app/core/end-user-agreement/end-user-agreement-current-user.guard.spec.ts index 88722bd799e..ae1f63884f2 100644 --- a/src/app/core/end-user-agreement/end-user-agreement-current-user.guard.spec.ts +++ b/src/app/core/end-user-agreement/end-user-agreement-current-user.guard.spec.ts @@ -1,16 +1,18 @@ +import { TestBed } from '@angular/core/testing'; import { Router, UrlTree, } from '@angular/router'; -import { of as observableOf } from 'rxjs'; +import { + Observable, + of as observableOf, +} from 'rxjs'; import { environment } from '../../../environments/environment.test'; import { EndUserAgreementService } from './end-user-agreement.service'; -import { EndUserAgreementCurrentUserGuard } from './end-user-agreement-current-user.guard'; - -describe('EndUserAgreementGuard', () => { - let guard: EndUserAgreementCurrentUserGuard; +import { endUserAgreementCurrentUserGuard } from './end-user-agreement-current-user.guard'; +describe('endUserAgreementGuard', () => { let endUserAgreementService: EndUserAgreementService; let router: Router; @@ -18,19 +20,30 @@ describe('EndUserAgreementGuard', () => { endUserAgreementService = jasmine.createSpyObj('endUserAgreementService', { hasCurrentUserAcceptedAgreement: observableOf(true), }); + router = jasmine.createSpyObj('router', { navigateByUrl: {}, parseUrl: new UrlTree(), createUrlTree: new UrlTree(), }); - guard = new EndUserAgreementCurrentUserGuard(endUserAgreementService, router); + TestBed.configureTestingModule({ + providers: [ + { provide: Router, useValue: router }, + { provide: EndUserAgreementService, useValue: endUserAgreementService }, + ], + }); + }); describe('canActivate', () => { describe('when the user has accepted the agreement', () => { it('should return true', (done) => { - guard.canActivate(undefined, Object.assign({ url: 'redirect' })).subscribe((result) => { + const result$ = TestBed.runInInjectionContext(() => { + return endUserAgreementCurrentUserGuard(undefined, Object.assign({ url: 'redirect' })); + }) as Observable; + + result$.subscribe((result) => { expect(result).toEqual(true); done(); }); @@ -43,7 +56,11 @@ describe('EndUserAgreementGuard', () => { }); it('should return a UrlTree', (done) => { - guard.canActivate(undefined, Object.assign({ url: 'redirect' })).subscribe((result) => { + const result$ = TestBed.runInInjectionContext(() => { + return endUserAgreementCurrentUserGuard(undefined, Object.assign({ url: 'redirect' })); + }) as Observable; + + result$.subscribe((result) => { expect(result).toEqual(jasmine.any(UrlTree)); done(); }); @@ -53,7 +70,12 @@ describe('EndUserAgreementGuard', () => { describe('when the end user agreement is disabled', () => { it('should return true', (done) => { environment.info.enableEndUserAgreement = false; - guard.canActivate(undefined, Object.assign({ url: 'redirect' })).subscribe((result) => { + + const result$ = TestBed.runInInjectionContext(() => { + return endUserAgreementCurrentUserGuard(undefined, Object.assign({ url: 'redirect' })); + }) as Observable; + + result$.subscribe((result) => { expect(result).toEqual(true); done(); }); @@ -61,7 +83,11 @@ describe('EndUserAgreementGuard', () => { it('should not resolve to the end user agreement page', (done) => { environment.info.enableEndUserAgreement = false; - guard.canActivate(undefined, Object.assign({ url: 'redirect' })).subscribe((result) => { + const result$ = TestBed.runInInjectionContext(() => { + return endUserAgreementCurrentUserGuard(undefined, Object.assign({ url: 'redirect' })); + }) as Observable; + + result$.subscribe((result) => { expect(router.navigateByUrl).not.toHaveBeenCalled(); done(); }); diff --git a/src/app/core/end-user-agreement/end-user-agreement-current-user.guard.ts b/src/app/core/end-user-agreement/end-user-agreement-current-user.guard.ts index 566f0494503..7c190a08b3c 100644 --- a/src/app/core/end-user-agreement/end-user-agreement-current-user.guard.ts +++ b/src/app/core/end-user-agreement/end-user-agreement-current-user.guard.ts @@ -1,34 +1,25 @@ -import { Injectable } from '@angular/core'; -import { Router } from '@angular/router'; -import { - Observable, - of as observableOf, -} from 'rxjs'; +import { inject } from '@angular/core'; +import { CanActivateFn } from '@angular/router'; +import { of as observableOf } from 'rxjs'; import { environment } from '../../../environments/environment'; -import { AbstractEndUserAgreementGuard } from './abstract-end-user-agreement.guard'; +import { endUserAgreementGuard } from './end-user-agreement.guard'; import { EndUserAgreementService } from './end-user-agreement.service'; -/** - * A guard redirecting logged in users to the end agreement page when they haven't accepted the latest user agreement - */ -@Injectable({ providedIn: 'root' }) -export class EndUserAgreementCurrentUserGuard extends AbstractEndUserAgreementGuard { - - constructor(protected endUserAgreementService: EndUserAgreementService, - protected router: Router) { - super(router); - } - /** - * True when the currently logged in user has accepted the agreements or when the user is not currently authenticated - */ - hasAccepted(): Observable { - if (!environment.info.enableEndUserAgreement) { - return observableOf(true); - } +/** + * Guard for preventing unauthorized access to certain pages + * requiring the end user agreement to have been accepted by the current user - return this.endUserAgreementService.hasCurrentUserAcceptedAgreement(true); - } + */ +export const endUserAgreementCurrentUserGuard: CanActivateFn = + endUserAgreementGuard( + () => { + const endUserAgreementService = inject(EndUserAgreementService); + if (!environment.info.enableEndUserAgreement) { + return observableOf(true); + } -} + return endUserAgreementService.hasCurrentUserAcceptedAgreement(true); + }, + ); diff --git a/src/app/core/end-user-agreement/end-user-agreement.guard.ts b/src/app/core/end-user-agreement/end-user-agreement.guard.ts new file mode 100644 index 00000000000..911dd54e259 --- /dev/null +++ b/src/app/core/end-user-agreement/end-user-agreement.guard.ts @@ -0,0 +1,34 @@ +import { inject } from '@angular/core'; +import { + ActivatedRouteSnapshot, + CanActivateFn, + Router, + RouterStateSnapshot, + UrlTree, +} from '@angular/router'; +import { + Observable, + of as observableOf, +} from 'rxjs'; + +import { environment } from '../../../environments/environment'; +import { returnEndUserAgreementUrlTreeOnFalse } from '../shared/authorized.operators'; + +export declare type HasAcceptedGuardParamFn = () => Observable; +/** + * Guard for preventing activating when the user has not accepted the EndUserAgreement + * @param hasAccepted Function determining if the EndUserAgreement has been accepted + */ +export const endUserAgreementGuard = ( + hasAccepted: HasAcceptedGuardParamFn, +): CanActivateFn => { + return (route: ActivatedRouteSnapshot, state: RouterStateSnapshot): Observable => { + const router = inject(Router); + if (!environment.info.enableEndUserAgreement) { + return observableOf(true); + } + return hasAccepted().pipe( + returnEndUserAgreementUrlTreeOnFalse(router, state.url), + ); + }; +}; diff --git a/src/app/register-page/register-page-routes.ts b/src/app/register-page/register-page-routes.ts index 109ed24c6d7..e7ca386aac8 100644 --- a/src/app/register-page/register-page-routes.ts +++ b/src/app/register-page/register-page-routes.ts @@ -1,9 +1,6 @@ -import { - mapToCanActivate, - Route, -} from '@angular/router'; +import { Route } from '@angular/router'; -import { EndUserAgreementCookieGuard } from '../core/end-user-agreement/end-user-agreement-cookie.guard'; +import { endUserAgreementCookieGuard } from '../core/end-user-agreement/end-user-agreement-cookie.guard'; import { ThemedCreateProfileComponent } from './create-profile/themed-create-profile.component'; import { ThemedRegisterEmailComponent } from './register-email/themed-register-email.component'; import { registrationGuard } from './registration.guard'; @@ -20,7 +17,7 @@ export const ROUTES: Route[] = [ component: ThemedCreateProfileComponent, canActivate: [ registrationGuard, - ...mapToCanActivate([EndUserAgreementCookieGuard]), + endUserAgreementCookieGuard, ], }, ];