Merge pull request #3049 from atmire/w2p-114858_refactor_guards_as_functions-8.0

Fix: Item edit pages not hidden/redirect for anon or non-authorised users
This commit is contained in:
Tim Donohue
2024-05-31 12:16:47 -05:00
committed by GitHub
62 changed files with 1282 additions and 1285 deletions

View File

@@ -1,16 +1,13 @@
import { AbstractControl } from '@angular/forms'; import { AbstractControl } from '@angular/forms';
import { import { Route } from '@angular/router';
mapToCanActivate,
Route,
} from '@angular/router';
import { import {
DYNAMIC_ERROR_MESSAGES_MATCHER, DYNAMIC_ERROR_MESSAGES_MATCHER,
DynamicErrorMessagesMatcher, DynamicErrorMessagesMatcher,
} from '@ng-dynamic-forms/core'; } from '@ng-dynamic-forms/core';
import { i18nBreadcrumbResolver } from '../core/breadcrumbs/i18n-breadcrumb.resolver'; import { i18nBreadcrumbResolver } from '../core/breadcrumbs/i18n-breadcrumb.resolver';
import { GroupAdministratorGuard } from '../core/data/feature-authorization/feature-authorization-guard/group-administrator.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 { siteAdministratorGuard } from '../core/data/feature-authorization/feature-authorization-guard/site-administrator.guard';
import { import {
EPERSON_PATH, EPERSON_PATH,
GROUP_PATH, GROUP_PATH,
@@ -20,7 +17,7 @@ import { EPeopleRegistryComponent } from './epeople-registry/epeople-registry.co
import { EPersonFormComponent } from './epeople-registry/eperson-form/eperson-form.component'; import { EPersonFormComponent } from './epeople-registry/eperson-form/eperson-form.component';
import { EPersonResolver } from './epeople-registry/eperson-resolver.service'; import { EPersonResolver } from './epeople-registry/eperson-resolver.service';
import { GroupFormComponent } from './group-registry/group-form/group-form.component'; import { GroupFormComponent } from './group-registry/group-form/group-form.component';
import { GroupPageGuard } from './group-registry/group-page.guard'; import { groupPageGuard } from './group-registry/group-page.guard';
import { GroupsRegistryComponent } from './group-registry/groups-registry.component'; import { GroupsRegistryComponent } from './group-registry/groups-registry.component';
/** /**
@@ -28,7 +25,7 @@ import { GroupsRegistryComponent } from './group-registry/groups-registry.compon
*/ */
export const ValidateEmailErrorStateMatcher: DynamicErrorMessagesMatcher = export const ValidateEmailErrorStateMatcher: DynamicErrorMessagesMatcher =
(control: AbstractControl, model: any, hasFocus: boolean) => { (control: AbstractControl, model: any, hasFocus: boolean) => {
return (control.touched && !hasFocus) || (control.errors?.emailTaken && hasFocus); return ( control.touched && !hasFocus ) || ( control.errors?.emailTaken && hasFocus );
}; };
const providers = [ const providers = [
@@ -46,7 +43,7 @@ export const ROUTES: Route[] = [
}, },
providers, providers,
data: { title: 'admin.access-control.epeople.title', breadcrumbKey: 'admin.access-control.epeople' }, data: { title: 'admin.access-control.epeople.title', breadcrumbKey: 'admin.access-control.epeople' },
canActivate: mapToCanActivate([SiteAdministratorGuard]), canActivate: [siteAdministratorGuard],
}, },
{ {
path: `${EPERSON_PATH}/create`, path: `${EPERSON_PATH}/create`,
@@ -56,7 +53,7 @@ export const ROUTES: Route[] = [
}, },
providers, providers,
data: { title: 'admin.access-control.epeople.add.title', breadcrumbKey: 'admin.access-control.epeople.add' }, data: { title: 'admin.access-control.epeople.add.title', breadcrumbKey: 'admin.access-control.epeople.add' },
canActivate: mapToCanActivate([SiteAdministratorGuard]), canActivate: [siteAdministratorGuard],
}, },
{ {
path: `${EPERSON_PATH}/:id/edit`, path: `${EPERSON_PATH}/:id/edit`,
@@ -67,7 +64,7 @@ export const ROUTES: Route[] = [
}, },
providers, providers,
data: { title: 'admin.access-control.epeople.edit.title', breadcrumbKey: 'admin.access-control.epeople.edit' }, data: { title: 'admin.access-control.epeople.edit.title', breadcrumbKey: 'admin.access-control.epeople.edit' },
canActivate: mapToCanActivate([SiteAdministratorGuard]), canActivate: [siteAdministratorGuard],
}, },
{ {
path: GROUP_PATH, path: GROUP_PATH,
@@ -77,7 +74,7 @@ export const ROUTES: Route[] = [
}, },
providers, providers,
data: { title: 'admin.access-control.groups.title', breadcrumbKey: 'admin.access-control.groups' }, data: { title: 'admin.access-control.groups.title', breadcrumbKey: 'admin.access-control.groups' },
canActivate: mapToCanActivate([GroupAdministratorGuard]), canActivate: [groupAdministratorGuard],
}, },
{ {
path: `${GROUP_PATH}/create`, path: `${GROUP_PATH}/create`,
@@ -90,7 +87,7 @@ export const ROUTES: Route[] = [
title: 'admin.access-control.groups.title.addGroup', title: 'admin.access-control.groups.title.addGroup',
breadcrumbKey: 'admin.access-control.groups.addGroup', breadcrumbKey: 'admin.access-control.groups.addGroup',
}, },
canActivate: mapToCanActivate([GroupAdministratorGuard]), canActivate: [groupAdministratorGuard],
}, },
{ {
path: `${GROUP_PATH}/:groupId/edit`, path: `${GROUP_PATH}/:groupId/edit`,
@@ -103,7 +100,7 @@ export const ROUTES: Route[] = [
title: 'admin.access-control.groups.title.singleGroup', title: 'admin.access-control.groups.title.singleGroup',
breadcrumbKey: 'admin.access-control.groups.singleGroup', breadcrumbKey: 'admin.access-control.groups.singleGroup',
}, },
canActivate: mapToCanActivate([GroupPageGuard]), canActivate: [groupPageGuard],
}, },
{ {
path: 'bulk-access', path: 'bulk-access',
@@ -112,6 +109,6 @@ export const ROUTES: Route[] = [
breadcrumb: i18nBreadcrumbResolver, breadcrumb: i18nBreadcrumbResolver,
}, },
data: { title: 'admin.access-control.bulk-access.title', breadcrumbKey: 'admin.access-control.bulk-access' }, data: { title: 'admin.access-control.bulk-access.title', breadcrumbKey: 'admin.access-control.bulk-access' },
canActivate: mapToCanActivate([SiteAdministratorGuard]), canActivate: [siteAdministratorGuard],
}, },
]; ];

View File

@@ -1,14 +1,24 @@
import {
TestBed,
waitForAsync,
} from '@angular/core/testing';
import { import {
ActivatedRouteSnapshot, ActivatedRouteSnapshot,
Router, Router,
UrlTree,
} from '@angular/router'; } from '@angular/router';
import { of as observableOf } from 'rxjs'; import {
Observable,
of as observableOf,
} from 'rxjs';
import { AuthService } from '../../core/auth/auth.service'; import { AuthService } from '../../core/auth/auth.service';
import { AuthorizationDataService } from '../../core/data/feature-authorization/authorization-data.service'; import { AuthorizationDataService } from '../../core/data/feature-authorization/authorization-data.service';
import { FeatureID } from '../../core/data/feature-authorization/feature-id'; import { FeatureID } from '../../core/data/feature-authorization/feature-id';
import { HALEndpointService } from '../../core/shared/hal-endpoint.service'; import { HALEndpointService } from '../../core/shared/hal-endpoint.service';
import { GroupPageGuard } from './group-page.guard'; import { groupPageGuard } from './group-page.guard';
jasmine.DEFAULT_TIMEOUT_INTERVAL = 10000; // Increase timeout to 10 seconds
describe('GroupPageGuard', () => { describe('GroupPageGuard', () => {
const groupsEndpointUrl = 'https://test.org/api/eperson/groups'; const groupsEndpointUrl = 'https://test.org/api/eperson/groups';
@@ -20,42 +30,54 @@ describe('GroupPageGuard', () => {
}, },
} as unknown as ActivatedRouteSnapshot; } as unknown as ActivatedRouteSnapshot;
let guard: GroupPageGuard;
let halEndpointService: HALEndpointService; let halEndpointService: HALEndpointService;
let authorizationService: AuthorizationDataService; let authorizationService: AuthorizationDataService;
let router: Router; let router: Router;
let authService: AuthService; let authService: AuthService;
beforeEach(() => { function init() {
halEndpointService = jasmine.createSpyObj(['getEndpoint']); halEndpointService = jasmine.createSpyObj(['getEndpoint']);
(halEndpointService as any).getEndpoint.and.returnValue(observableOf(groupsEndpointUrl)); ( halEndpointService as any ).getEndpoint.and.returnValue(observableOf(groupsEndpointUrl));
authorizationService = jasmine.createSpyObj(['isAuthorized']); authorizationService = jasmine.createSpyObj(['isAuthorized']);
// NOTE: value is set in beforeEach // NOTE: value is set in beforeEach
router = jasmine.createSpyObj(['parseUrl']); router = jasmine.createSpyObj(['parseUrl']);
(router as any).parseUrl.and.returnValue = {}; ( router as any ).parseUrl.and.returnValue = {};
authService = jasmine.createSpyObj(['isAuthenticated']); authService = jasmine.createSpyObj(['isAuthenticated']);
(authService as any).isAuthenticated.and.returnValue(observableOf(true)); ( authService as any ).isAuthenticated.and.returnValue(observableOf(true));
guard = new GroupPageGuard(halEndpointService, authorizationService, router, authService); 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', () => { it('should be created', () => {
expect(guard).toBeTruthy(); expect(groupPageGuard).toBeTruthy();
}); });
describe('canActivate', () => { describe('canActivate', () => {
describe('when the current user can manage the group', () => { describe('when the current user can manage the group', () => {
beforeEach(() => { beforeEach(() => {
(authorizationService as any).isAuthorized.and.returnValue(observableOf(true)); ( authorizationService as any ).isAuthorized.and.returnValue(observableOf(true));
}); });
it('should return true', (done) => { it('should return true', (done) => {
guard.canActivate( const result$ = TestBed.runInInjectionContext(() => {
routeSnapshotWithGroupId, { url: 'current-url' } as any, return groupPageGuard()(routeSnapshotWithGroupId, { url: 'current-url' } as any);
).subscribe((result) => { }) as Observable<boolean | UrlTree>;
result$.subscribe((result) => {
expect(authorizationService.isAuthorized).toHaveBeenCalledWith( expect(authorizationService.isAuthorized).toHaveBeenCalledWith(
FeatureID.CanManageGroup, groupEndpointUrl, undefined, FeatureID.CanManageGroup, groupEndpointUrl, undefined,
); );
@@ -71,15 +93,18 @@ describe('GroupPageGuard', () => {
}); });
it('should not return true', (done) => { it('should not return true', (done) => {
guard.canActivate( const result$ = TestBed.runInInjectionContext(() => {
routeSnapshotWithGroupId, { url: 'current-url' } as any, return groupPageGuard()(routeSnapshotWithGroupId, { url: 'current-url' } as any);
).subscribe((result) => { }) as Observable<boolean | UrlTree>;
result$.subscribe((result) => {
expect(authorizationService.isAuthorized).toHaveBeenCalledWith( expect(authorizationService.isAuthorized).toHaveBeenCalledWith(
FeatureID.CanManageGroup, groupEndpointUrl, undefined, FeatureID.CanManageGroup, groupEndpointUrl, undefined,
); );
expect(result).not.toBeTrue(); expect(result).not.toBeTrue();
done(); done();
}); });
}); });
}); });
}); });

View File

@@ -1,7 +1,7 @@
import { Injectable } from '@angular/core'; import { inject } from '@angular/core';
import { import {
ActivatedRouteSnapshot, ActivatedRouteSnapshot,
Router, CanActivateFn,
RouterStateSnapshot, RouterStateSnapshot,
} from '@angular/router'; } from '@angular/router';
import { import {
@@ -10,34 +10,29 @@ import {
} from 'rxjs'; } from 'rxjs';
import { map } from 'rxjs/operators'; import { map } from 'rxjs/operators';
import { AuthService } from '../../core/auth/auth.service'; import {
import { AuthorizationDataService } from '../../core/data/feature-authorization/authorization-data.service'; someFeatureAuthorizationGuard,
import { SomeFeatureAuthorizationGuard } from '../../core/data/feature-authorization/feature-authorization-guard/some-feature-authorization.guard'; StringGuardParamFn,
} from '../../core/data/feature-authorization/feature-authorization-guard/some-feature-authorization.guard';
import { FeatureID } from '../../core/data/feature-authorization/feature-id'; import { FeatureID } from '../../core/data/feature-authorization/feature-id';
import { HALEndpointService } from '../../core/shared/hal-endpoint.service'; import { HALEndpointService } from '../../core/shared/hal-endpoint.service';
@Injectable({ const defaultGroupPageGetObjectUrl: StringGuardParamFn = (
providedIn: 'root', route: ActivatedRouteSnapshot,
}) state: RouterStateSnapshot,
export class GroupPageGuard extends SomeFeatureAuthorizationGuard { ): Observable<string> => {
const halEndpointService = inject(HALEndpointService);
const groupsEndpoint = 'groups';
protected groupsEndpoint = 'groups'; return halEndpointService.getEndpoint(groupsEndpoint).pipe(
map(groupsUrl => `${groupsUrl}/${route?.params?.groupId}`),
);
};
constructor(protected halEndpointService: HALEndpointService, export const groupPageGuard = (
protected authorizationService: AuthorizationDataService, getObjectUrl = defaultGroupPageGetObjectUrl,
protected router: Router, getEPersonUuid?: StringGuardParamFn,
protected authService: AuthService) { ): CanActivateFn => someFeatureAuthorizationGuard(
super(authorizationService, router, authService); () => observableOf([FeatureID.CanManageGroup]),
} getObjectUrl,
getEPersonUuid);
getFeatureIDs(route: ActivatedRouteSnapshot, state: RouterStateSnapshot): Observable<FeatureID[]> {
return observableOf([FeatureID.CanManageGroup]);
}
getObjectUrl(route: ActivatedRouteSnapshot, state: RouterStateSnapshot): Observable<string> {
return this.halEndpointService.getEndpoint(this.groupsEndpoint).pipe(
map(groupsUrl => `${groupsUrl}/${route?.params?.groupId}`),
);
}
}

View File

@@ -1,18 +1,15 @@
import { import { Route } from '@angular/router';
mapToCanActivate,
Route,
} from '@angular/router';
import { i18nBreadcrumbResolver } from '../../core/breadcrumbs/i18n-breadcrumb.resolver'; import { i18nBreadcrumbResolver } from '../../core/breadcrumbs/i18n-breadcrumb.resolver';
import { notifyInfoGuard } from '../../core/coar-notify/notify-info/notify-info.guard'; 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 { siteAdministratorGuard } from '../../core/data/feature-authorization/feature-authorization-guard/site-administrator.guard';
import { AdminNotifyDashboardComponent } from './admin-notify-dashboard.component'; import { AdminNotifyDashboardComponent } from './admin-notify-dashboard.component';
import { AdminNotifyIncomingComponent } from './admin-notify-logs/admin-notify-incoming/admin-notify-incoming.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 { AdminNotifyOutgoingComponent } from './admin-notify-logs/admin-notify-outgoing/admin-notify-outgoing.component';
export const ROUTES: Route[] = [ export const ROUTES: Route[] = [
{ {
canActivate: [...mapToCanActivate([SiteAdministratorGuard]), notifyInfoGuard], canActivate: [siteAdministratorGuard, notifyInfoGuard],
path: '', path: '',
resolve: { resolve: {
breadcrumb: i18nBreadcrumbResolver, breadcrumb: i18nBreadcrumbResolver,
@@ -30,7 +27,7 @@ export const ROUTES: Route[] = [
breadcrumb: i18nBreadcrumbResolver, breadcrumb: i18nBreadcrumbResolver,
}, },
component: AdminNotifyIncomingComponent, component: AdminNotifyIncomingComponent,
canActivate: [...mapToCanActivate([SiteAdministratorGuard]), notifyInfoGuard], canActivate: [siteAdministratorGuard, notifyInfoGuard],
data: { data: {
title: 'admin.notify.dashboard.page.title', title: 'admin.notify.dashboard.page.title',
breadcrumbKey: 'admin.notify.dashboard', breadcrumbKey: 'admin.notify.dashboard',
@@ -42,7 +39,7 @@ export const ROUTES: Route[] = [
breadcrumb: i18nBreadcrumbResolver, breadcrumb: i18nBreadcrumbResolver,
}, },
component: AdminNotifyOutgoingComponent, component: AdminNotifyOutgoingComponent,
canActivate: [...mapToCanActivate([SiteAdministratorGuard]), notifyInfoGuard], canActivate: [siteAdministratorGuard, notifyInfoGuard],
data: { data: {
title: 'admin.notify.dashboard.page.title', title: 'admin.notify.dashboard.page.title',
breadcrumbKey: 'admin.notify.dashboard', breadcrumbKey: 'admin.notify.dashboard',

View File

@@ -1,6 +1,5 @@
import { import {
InMemoryScrollingOptions, InMemoryScrollingOptions,
mapToCanActivate,
Route, Route,
RouterConfigOptions, RouterConfigOptions,
} from '@angular/router'; } from '@angular/router';
@@ -26,12 +25,12 @@ import { COLLECTION_MODULE_PATH } from './collection-page/collection-page-routin
import { COMMUNITY_MODULE_PATH } from './community-page/community-page-routing-paths'; import { COMMUNITY_MODULE_PATH } from './community-page/community-page-routing-paths';
import { authBlockingGuard } from './core/auth/auth-blocking.guard'; import { authBlockingGuard } from './core/auth/auth-blocking.guard';
import { authenticatedGuard } from './core/auth/authenticated.guard'; import { authenticatedGuard } from './core/auth/authenticated.guard';
import { GroupAdministratorGuard } from './core/data/feature-authorization/feature-authorization-guard/group-administrator.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 { 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 { endUserAgreementCurrentUserGuard } from './core/end-user-agreement/end-user-agreement-current-user.guard';
import { reloadGuard } from './core/reload/reload.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 { ServerCheckGuard } from './core/server-check/server-check.guard';
import { ThemedForbiddenComponent } from './forbidden/themed-forbidden.component'; import { ThemedForbiddenComponent } from './forbidden/themed-forbidden.component';
import { ITEM_MODULE_PATH } from './item-page/item-page-routing-paths'; import { ITEM_MODULE_PATH } from './item-page/item-page-routing-paths';
@@ -66,105 +65,105 @@ export const APP_ROUTES: Route[] = [
.then((m) => m.ROUTES), .then((m) => m.ROUTES),
data: { showBreadcrumbs: false }, data: { showBreadcrumbs: false },
providers: [provideSuggestionNotificationsState()], providers: [provideSuggestionNotificationsState()],
canActivate: mapToCanActivate([EndUserAgreementCurrentUserGuard]), canActivate: [endUserAgreementCurrentUserGuard],
}, },
{ {
path: 'community-list', path: 'community-list',
loadChildren: () => import('./community-list-page/community-list-page-routes') loadChildren: () => import('./community-list-page/community-list-page-routes')
.then((m) => m.ROUTES), .then((m) => m.ROUTES),
canActivate: mapToCanActivate([EndUserAgreementCurrentUserGuard]), canActivate: [endUserAgreementCurrentUserGuard],
}, },
{ {
path: 'id', path: 'id',
loadChildren: () => import('./lookup-by-id/lookup-by-id-routes') loadChildren: () => import('./lookup-by-id/lookup-by-id-routes')
.then((m) => m.ROUTES), .then((m) => m.ROUTES),
canActivate: mapToCanActivate([EndUserAgreementCurrentUserGuard]), canActivate: [endUserAgreementCurrentUserGuard],
}, },
{ {
path: 'handle', path: 'handle',
loadChildren: () => import('./lookup-by-id/lookup-by-id-routes') loadChildren: () => import('./lookup-by-id/lookup-by-id-routes')
.then((m) => m.ROUTES), .then((m) => m.ROUTES),
canActivate: mapToCanActivate([EndUserAgreementCurrentUserGuard]), canActivate: [endUserAgreementCurrentUserGuard],
}, },
{ {
path: REGISTER_PATH, path: REGISTER_PATH,
loadChildren: () => import('./register-page/register-page-routes') loadChildren: () => import('./register-page/register-page-routes')
.then((m) => m.ROUTES), .then((m) => m.ROUTES),
canActivate: mapToCanActivate([SiteRegisterGuard]), canActivate: [siteRegisterGuard],
}, },
{ {
path: FORGOT_PASSWORD_PATH, path: FORGOT_PASSWORD_PATH,
loadChildren: () => import('./forgot-password/forgot-password-routes') loadChildren: () => import('./forgot-password/forgot-password-routes')
.then((m) => m.ROUTES), .then((m) => m.ROUTES),
canActivate: mapToCanActivate([EndUserAgreementCurrentUserGuard, ForgotPasswordCheckGuard]), canActivate: [endUserAgreementCurrentUserGuard, forgotPasswordCheckGuard],
}, },
{ {
path: COMMUNITY_MODULE_PATH, path: COMMUNITY_MODULE_PATH,
loadChildren: () => import('./community-page/community-page-routes') loadChildren: () => import('./community-page/community-page-routes')
.then((m) => m.ROUTES), .then((m) => m.ROUTES),
canActivate: mapToCanActivate([EndUserAgreementCurrentUserGuard]), canActivate: [endUserAgreementCurrentUserGuard],
}, },
{ {
path: COLLECTION_MODULE_PATH, path: COLLECTION_MODULE_PATH,
loadChildren: () => import('./collection-page/collection-page-routes') loadChildren: () => import('./collection-page/collection-page-routes')
.then((m) => m.ROUTES), .then((m) => m.ROUTES),
canActivate: mapToCanActivate([EndUserAgreementCurrentUserGuard]), canActivate: [endUserAgreementCurrentUserGuard],
}, },
{ {
path: ITEM_MODULE_PATH, path: ITEM_MODULE_PATH,
loadChildren: () => import('./item-page/item-page-routes') loadChildren: () => import('./item-page/item-page-routes')
.then((m) => m.ROUTES), .then((m) => m.ROUTES),
canActivate: mapToCanActivate([EndUserAgreementCurrentUserGuard]), canActivate: [endUserAgreementCurrentUserGuard],
}, },
{ {
path: 'entities/:entity-type', path: 'entities/:entity-type',
loadChildren: () => import('./item-page/item-page-routes') loadChildren: () => import('./item-page/item-page-routes')
.then((m) => m.ROUTES), .then((m) => m.ROUTES),
canActivate: mapToCanActivate([EndUserAgreementCurrentUserGuard]), canActivate: [endUserAgreementCurrentUserGuard],
}, },
{ {
path: LEGACY_BITSTREAM_MODULE_PATH, path: LEGACY_BITSTREAM_MODULE_PATH,
loadChildren: () => import('./bitstream-page/bitstream-page-routes') loadChildren: () => import('./bitstream-page/bitstream-page-routes')
.then((m) => m.ROUTES), .then((m) => m.ROUTES),
canActivate: mapToCanActivate([EndUserAgreementCurrentUserGuard]), canActivate: [endUserAgreementCurrentUserGuard],
}, },
{ {
path: BITSTREAM_MODULE_PATH, path: BITSTREAM_MODULE_PATH,
loadChildren: () => import('./bitstream-page/bitstream-page-routes') loadChildren: () => import('./bitstream-page/bitstream-page-routes')
.then((m) => m.ROUTES), .then((m) => m.ROUTES),
canActivate: mapToCanActivate([EndUserAgreementCurrentUserGuard]), canActivate: [endUserAgreementCurrentUserGuard],
}, },
{ {
path: 'mydspace', path: 'mydspace',
loadChildren: () => import('./my-dspace-page/my-dspace-page-routes') loadChildren: () => import('./my-dspace-page/my-dspace-page-routes')
.then((m) => m.ROUTES), .then((m) => m.ROUTES),
providers: [provideSuggestionNotificationsState()], providers: [provideSuggestionNotificationsState()],
canActivate: [authenticatedGuard, ...mapToCanActivate([EndUserAgreementCurrentUserGuard])], canActivate: [authenticatedGuard, endUserAgreementCurrentUserGuard],
}, },
{ {
path: 'search', path: 'search',
loadChildren: () => import('./search-page/search-page-routes') loadChildren: () => import('./search-page/search-page-routes')
.then((m) => m.ROUTES), .then((m) => m.ROUTES),
canActivate: mapToCanActivate([EndUserAgreementCurrentUserGuard]), canActivate: [endUserAgreementCurrentUserGuard],
}, },
{ {
path: 'browse', path: 'browse',
loadChildren: () => import('./browse-by/browse-by-page-routes') loadChildren: () => import('./browse-by/browse-by-page-routes')
.then((m) => m.ROUTES), .then((m) => m.ROUTES),
canActivate: mapToCanActivate([EndUserAgreementCurrentUserGuard]), canActivate: [endUserAgreementCurrentUserGuard],
}, },
{ {
path: ADMIN_MODULE_PATH, path: ADMIN_MODULE_PATH,
loadChildren: () => import('./admin/admin-routes') loadChildren: () => import('./admin/admin-routes')
.then((m) => m.ROUTES), .then((m) => m.ROUTES),
canActivate: mapToCanActivate([SiteAdministratorGuard, EndUserAgreementCurrentUserGuard]), canActivate: [siteAdministratorGuard, endUserAgreementCurrentUserGuard],
}, },
{ {
path: NOTIFICATIONS_MODULE_PATH, path: NOTIFICATIONS_MODULE_PATH,
loadChildren: () => import('./quality-assurance-notifications-pages/notifications-pages-routes') loadChildren: () => import('./quality-assurance-notifications-pages/notifications-pages-routes')
.then((m) => m.ROUTES), .then((m) => m.ROUTES),
providers: [provideSuggestionNotificationsState()], providers: [provideSuggestionNotificationsState()],
canActivate: [authenticatedGuard, ...mapToCanActivate([EndUserAgreementCurrentUserGuard])], canActivate: [authenticatedGuard, endUserAgreementCurrentUserGuard],
}, },
{ {
path: 'login', path: 'login',
@@ -181,47 +180,47 @@ export const APP_ROUTES: Route[] = [
loadChildren: () => import('./submit-page/submit-page-routes') loadChildren: () => import('./submit-page/submit-page-routes')
.then((m) => m.ROUTES), .then((m) => m.ROUTES),
providers: [provideSubmissionState()], providers: [provideSubmissionState()],
canActivate: mapToCanActivate([EndUserAgreementCurrentUserGuard]), canActivate: [endUserAgreementCurrentUserGuard],
}, },
{ {
path: 'import-external', path: 'import-external',
loadChildren: () => import('./import-external-page/import-external-page-routes') loadChildren: () => import('./import-external-page/import-external-page-routes')
.then((m) => m.ROUTES), .then((m) => m.ROUTES),
canActivate: mapToCanActivate([EndUserAgreementCurrentUserGuard]), canActivate: [endUserAgreementCurrentUserGuard],
}, },
{ {
path: 'workspaceitems', path: 'workspaceitems',
loadChildren: () => import('./workspaceitems-edit-page/workspaceitems-edit-page-routes') loadChildren: () => import('./workspaceitems-edit-page/workspaceitems-edit-page-routes')
.then((m) => m.ROUTES), .then((m) => m.ROUTES),
providers: [provideSubmissionState()], providers: [provideSubmissionState()],
canActivate: mapToCanActivate([EndUserAgreementCurrentUserGuard]), canActivate: [endUserAgreementCurrentUserGuard],
}, },
{ {
path: WORKFLOW_ITEM_MODULE_PATH, path: WORKFLOW_ITEM_MODULE_PATH,
providers: [provideSubmissionState()], providers: [provideSubmissionState()],
loadChildren: () => import('./workflowitems-edit-page/workflowitems-edit-page-routes') loadChildren: () => import('./workflowitems-edit-page/workflowitems-edit-page-routes')
.then((m) => m.ROUTES), .then((m) => m.ROUTES),
canActivate: mapToCanActivate([EndUserAgreementCurrentUserGuard]), canActivate: [endUserAgreementCurrentUserGuard],
}, },
{ {
path: PROFILE_MODULE_PATH, path: PROFILE_MODULE_PATH,
loadChildren: () => import('./profile-page/profile-page-routes') loadChildren: () => import('./profile-page/profile-page-routes')
.then((m) => m.ROUTES), .then((m) => m.ROUTES),
providers: [provideSuggestionNotificationsState()], providers: [provideSuggestionNotificationsState()],
canActivate: [authenticatedGuard, ...mapToCanActivate([EndUserAgreementCurrentUserGuard])], canActivate: [authenticatedGuard, endUserAgreementCurrentUserGuard],
}, },
{ {
path: PROCESS_MODULE_PATH, path: PROCESS_MODULE_PATH,
loadChildren: () => import('./process-page/process-page-routes') loadChildren: () => import('./process-page/process-page-routes')
.then((m) => m.ROUTES), .then((m) => m.ROUTES),
canActivate: [authenticatedGuard, ...mapToCanActivate([EndUserAgreementCurrentUserGuard])], canActivate: [authenticatedGuard, endUserAgreementCurrentUserGuard],
}, },
{ {
path: SUGGESTION_MODULE_PATH, path: SUGGESTION_MODULE_PATH,
loadChildren: () => import('./suggestions-page/suggestions-page-routes') loadChildren: () => import('./suggestions-page/suggestions-page-routes')
.then((m) => m.ROUTES), .then((m) => m.ROUTES),
providers: [provideSuggestionNotificationsState()], providers: [provideSuggestionNotificationsState()],
canActivate: [authenticatedGuard, ...mapToCanActivate([EndUserAgreementCurrentUserGuard])], canActivate: [authenticatedGuard, endUserAgreementCurrentUserGuard],
}, },
{ {
path: INFO_MODULE_PATH, path: INFO_MODULE_PATH,
@@ -230,7 +229,7 @@ export const APP_ROUTES: Route[] = [
{ {
path: REQUEST_COPY_MODULE_PATH, path: REQUEST_COPY_MODULE_PATH,
loadChildren: () => import('./request-copy/request-copy-routes').then((m) => m.ROUTES), loadChildren: () => import('./request-copy/request-copy-routes').then((m) => m.ROUTES),
canActivate: mapToCanActivate([EndUserAgreementCurrentUserGuard]), canActivate: [endUserAgreementCurrentUserGuard],
}, },
{ {
path: FORBIDDEN_PATH, path: FORBIDDEN_PATH,
@@ -240,7 +239,7 @@ export const APP_ROUTES: Route[] = [
path: 'statistics', path: 'statistics',
loadChildren: () => import('./statistics-page/statistics-page-routes') loadChildren: () => import('./statistics-page/statistics-page-routes')
.then((m) => m.ROUTES), .then((m) => m.ROUTES),
canActivate: mapToCanActivate([EndUserAgreementCurrentUserGuard]), canActivate: [endUserAgreementCurrentUserGuard],
}, },
{ {
path: HEALTH_PAGE_PATH, path: HEALTH_PAGE_PATH,
@@ -250,7 +249,7 @@ export const APP_ROUTES: Route[] = [
{ {
path: ACCESS_CONTROL_MODULE_PATH, path: ACCESS_CONTROL_MODULE_PATH,
loadChildren: () => import('./access-control/access-control-routes').then((m) => m.ROUTES), loadChildren: () => import('./access-control/access-control-routes').then((m) => m.ROUTES),
canActivate: mapToCanActivate([GroupAdministratorGuard, EndUserAgreementCurrentUserGuard]), canActivate: [groupAdministratorGuard, endUserAgreementCurrentUserGuard],
}, },
{ {
path: 'subscriptions', path: 'subscriptions',

View File

@@ -0,0 +1,81 @@
import { TestBed } from '@angular/core/testing';
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 { 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;
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<boolean | UrlTree>;
result$.subscribe((result) => {
expect(authorizationService.isAuthorized).toHaveBeenCalledWith(
FeatureID.CanManagePolicies,
bitstreamSelfLink,
undefined,
);
done();
});
});
});

View File

@@ -0,0 +1,16 @@
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 { bitstreamPageResolver } from './bitstream-page.resolver';
/**
* Guard for preventing unauthorized access to certain {@link Bitstream} pages requiring specific authorizations.
* Checks authorization rights for managing policies.
*/
export const bitstreamPageAuthorizationsGuard: CanActivateFn =
dsoPageSingleFeatureGuard(
() => bitstreamPageResolver,
() => observableOf(FeatureID.CanManagePolicies),
);

View File

@@ -10,6 +10,7 @@ import { resourcePolicyTargetResolver } from '../shared/resource-policies/resolv
import { BitstreamAuthorizationsComponent } from './bitstream-authorizations/bitstream-authorizations.component'; import { BitstreamAuthorizationsComponent } from './bitstream-authorizations/bitstream-authorizations.component';
import { BitstreamDownloadPageComponent } from './bitstream-download-page/bitstream-download-page.component'; import { BitstreamDownloadPageComponent } from './bitstream-download-page/bitstream-download-page.component';
import { bitstreamPageResolver } from './bitstream-page.resolver'; 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 { ThemedEditBitstreamPageComponent } from './edit-bitstream-page/themed-edit-bitstream-page.component';
import { legacyBitstreamURLRedirectGuard } from './legacy-bitstream-url-redirect.guard'; import { legacyBitstreamURLRedirectGuard } from './legacy-bitstream-url-redirect.guard';
@@ -49,6 +50,7 @@ export const ROUTES: Route[] = [
}, },
{ {
path: EDIT_BITSTREAM_AUTHORIZATIONS_PATH, path: EDIT_BITSTREAM_AUTHORIZATIONS_PATH,
canActivate: [bitstreamPageAuthorizationsGuard],
children: [ children: [
{ {
path: 'create', path: 'create',

View File

@@ -1,43 +1,16 @@
import { Injectable } from '@angular/core'; import { CanActivateFn } from '@angular/router';
import { import { of as observableOf } from 'rxjs';
ActivatedRouteSnapshot,
ResolveFn,
Router,
RouterStateSnapshot,
} from '@angular/router';
import {
Observable,
of as observableOf,
} from 'rxjs';
import { AuthService } from '../core/auth/auth.service'; import { dsoPageSingleFeatureGuard } from '../core/data/feature-authorization/feature-authorization-guard/dso-page-single-feature.guard';
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 { FeatureID } from '../core/data/feature-authorization/feature-id';
import { RemoteData } from '../core/data/remote-data';
import { Collection } from '../core/shared/collection.model';
import { collectionPageResolver } from './collection-page.resolver'; import { collectionPageResolver } from './collection-page.resolver';
@Injectable({
providedIn: 'root',
})
/** /**
* Guard for preventing unauthorized access to certain {@link Collection} pages requiring administrator rights * Guard for preventing unauthorized access to certain {@link Collection} pages requiring administrator rights
* Check administrator authorization rights
*/ */
export class CollectionPageAdministratorGuard extends DsoPageSingleFeatureGuard<Collection> { export const collectionPageAdministratorGuard: CanActivateFn =
dsoPageSingleFeatureGuard(
protected resolver: ResolveFn<RemoteData<Collection>> = collectionPageResolver; () => collectionPageResolver,
() => observableOf(FeatureID.AdministratorOf),
constructor(protected authorizationService: AuthorizationDataService, );
protected router: Router,
protected authService: AuthService) {
super(authorizationService, router, authService);
}
/**
* Check administrator authorization rights
*/
getFeatureID(next: ActivatedRouteSnapshot, state: RouterStateSnapshot): Observable<FeatureID> {
return observableOf(FeatureID.AdministratorOf);
}
}

View File

@@ -1,7 +1,4 @@
import { import { Route } from '@angular/router';
mapToCanActivate,
Route,
} from '@angular/router';
import { browseByGuard } from '../browse-by/browse-by-guard'; import { browseByGuard } from '../browse-by/browse-by-guard';
import { browseByI18nBreadcrumbResolver } from '../browse-by/browse-by-i18n-breadcrumb.resolver'; 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 { LinkMenuItemModel } from '../shared/menu/menu-item/models/link.model';
import { MenuItemType } from '../shared/menu/menu-item-type.model'; import { MenuItemType } from '../shared/menu/menu-item-type.model';
import { collectionPageResolver } from './collection-page.resolver'; import { collectionPageResolver } from './collection-page.resolver';
import { CollectionPageAdministratorGuard } from './collection-page-administrator.guard'; import { collectionPageAdministratorGuard } from './collection-page-administrator.guard';
import { import {
COLLECTION_CREATE_PATH, COLLECTION_CREATE_PATH,
COLLECTION_EDIT_PATH, COLLECTION_EDIT_PATH,
@@ -65,7 +62,7 @@ export const ROUTES: Route[] = [
path: COLLECTION_EDIT_PATH, path: COLLECTION_EDIT_PATH,
loadChildren: () => import('./edit-collection-page/edit-collection-page-routes') loadChildren: () => import('./edit-collection-page/edit-collection-page-routes')
.then((m) => m.ROUTES), .then((m) => m.ROUTES),
canActivate: mapToCanActivate([CollectionPageAdministratorGuard]), canActivate: [collectionPageAdministratorGuard],
}, },
{ {
path: 'delete', path: 'delete',

View File

@@ -1,10 +1,7 @@
import { import { Route } from '@angular/router';
mapToCanActivate,
Route,
} from '@angular/router';
import { i18nBreadcrumbResolver } from '../../core/breadcrumbs/i18n-breadcrumb.resolver'; 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 { ResourcePolicyCreateComponent } from '../../shared/resource-policies/create/resource-policy-create.component';
import { ResourcePolicyEditComponent } from '../../shared/resource-policies/edit/resource-policy-edit.component'; import { ResourcePolicyEditComponent } from '../../shared/resource-policies/edit/resource-policy-edit.component';
import { resourcePolicyResolver } from '../../shared/resource-policies/resolvers/resource-policy.resolver'; import { resourcePolicyResolver } from '../../shared/resource-policies/resolvers/resource-policy.resolver';
@@ -30,7 +27,7 @@ export const ROUTES: Route[] = [
}, },
data: { breadcrumbKey: 'collection.edit' }, data: { breadcrumbKey: 'collection.edit' },
component: EditCollectionPageComponent, component: EditCollectionPageComponent,
canActivate: mapToCanActivate([CollectionAdministratorGuard]), canActivate: [collectionAdministratorGuard],
children: [ children: [
{ {
path: '', path: '',

View File

@@ -1,43 +1,16 @@
import { Injectable } from '@angular/core'; import { CanActivateFn } from '@angular/router';
import { import { of as observableOf } from 'rxjs';
ActivatedRouteSnapshot,
ResolveFn,
Router,
RouterStateSnapshot,
} from '@angular/router';
import {
Observable,
of as observableOf,
} from 'rxjs';
import { AuthService } from '../core/auth/auth.service'; import { dsoPageSingleFeatureGuard } from '../core/data/feature-authorization/feature-authorization-guard/dso-page-single-feature.guard';
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 { 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';
@Injectable({
providedIn: 'root',
})
/** /**
* Guard for preventing unauthorized access to certain {@link Community} pages requiring administrator rights * Guard for preventing unauthorized access to certain {@link Community} pages requiring administrator rights
* Check administrator authorization rights
*/ */
export class CommunityPageAdministratorGuard extends DsoPageSingleFeatureGuard<Community> { export const communityPageAdministratorGuard: CanActivateFn =
dsoPageSingleFeatureGuard(
protected resolver: ResolveFn<RemoteData<Community>> = communityPageResolver; () => communityPageResolver,
() => observableOf(FeatureID.AdministratorOf),
constructor(protected authorizationService: AuthorizationDataService, );
protected router: Router,
protected authService: AuthService) {
super(authorizationService, router, authService);
}
/**
* Check administrator authorization rights
*/
getFeatureID(next: ActivatedRouteSnapshot, state: RouterStateSnapshot): Observable<FeatureID> {
return observableOf(FeatureID.AdministratorOf);
}
}

View File

@@ -1,7 +1,4 @@
import { import { Route } from '@angular/router';
mapToCanActivate,
Route,
} from '@angular/router';
import { browseByGuard } from '../browse-by/browse-by-guard'; import { browseByGuard } from '../browse-by/browse-by-guard';
import { browseByI18nBreadcrumbResolver } from '../browse-by/browse-by-i18n-breadcrumb.resolver'; 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 { LinkMenuItemModel } from '../shared/menu/menu-item/models/link.model';
import { MenuItemType } from '../shared/menu/menu-item-type.model'; import { MenuItemType } from '../shared/menu/menu-item-type.model';
import { communityPageResolver } from './community-page.resolver'; import { communityPageResolver } from './community-page.resolver';
import { CommunityPageAdministratorGuard } from './community-page-administrator.guard'; import { communityPageAdministratorGuard } from './community-page-administrator.guard';
import { import {
COMMUNITY_CREATE_PATH, COMMUNITY_CREATE_PATH,
COMMUNITY_EDIT_PATH, COMMUNITY_EDIT_PATH,
@@ -62,7 +59,7 @@ export const ROUTES: Route[] = [
path: COMMUNITY_EDIT_PATH, path: COMMUNITY_EDIT_PATH,
loadChildren: () => import('./edit-community-page/edit-community-page-routes') loadChildren: () => import('./edit-community-page/edit-community-page-routes')
.then((m) => m.ROUTES), .then((m) => m.ROUTES),
canActivate: mapToCanActivate([CommunityPageAdministratorGuard]), canActivate: [communityPageAdministratorGuard],
}, },
{ {
path: 'delete', path: 'delete',

View File

@@ -1,10 +1,7 @@
import { import { Route } from '@angular/router';
mapToCanActivate,
Route,
} from '@angular/router';
import { i18nBreadcrumbResolver } from '../../core/breadcrumbs/i18n-breadcrumb.resolver'; 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 { ResourcePolicyCreateComponent } from '../../shared/resource-policies/create/resource-policy-create.component';
import { ResourcePolicyEditComponent } from '../../shared/resource-policies/edit/resource-policy-edit.component'; import { ResourcePolicyEditComponent } from '../../shared/resource-policies/edit/resource-policy-edit.component';
import { resourcePolicyResolver } from '../../shared/resource-policies/resolvers/resource-policy.resolver'; import { resourcePolicyResolver } from '../../shared/resource-policies/resolvers/resource-policy.resolver';
@@ -28,7 +25,7 @@ export const ROUTES: Route[] = [
}, },
data: { breadcrumbKey: 'community.edit' }, data: { breadcrumbKey: 'community.edit' },
component: EditCommunityPageComponent, component: EditCommunityPageComponent,
canActivate: mapToCanActivate([CommunityAdministratorGuard]), canActivate: [communityAdministratorGuard],
children: [ children: [
{ {
path: '', path: '',

View File

@@ -1,35 +1,13 @@
import { Injectable } from '@angular/core'; import { CanActivateFn } from '@angular/router';
import { import { of as observableOf } from 'rxjs';
ActivatedRouteSnapshot,
Router,
RouterStateSnapshot,
} 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 { FeatureID } from '../feature-id';
import { SingleFeatureAuthorizationGuard } from './single-feature-authorization.guard'; import { singleFeatureAuthorizationGuard } from './single-feature-authorization.guard';
/** /**
* Prevent unauthorized activating and loading of routes when the current authenticated user * Prevent unauthorized activating and loading of routes when the current authenticated user
* isn't a Collection administrator * isn't a Collection administrator
* Check group management rights
*/ */
@Injectable({ export const collectionAdministratorGuard: CanActivateFn =
providedIn: 'root', singleFeatureAuthorizationGuard(() => observableOf(FeatureID.IsCollectionAdmin));
})
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<FeatureID> {
return observableOf(FeatureID.IsCollectionAdmin);
}
}

View File

@@ -1,35 +1,13 @@
import { Injectable } from '@angular/core'; import { CanActivateFn } from '@angular/router';
import { import { of as observableOf } from 'rxjs';
ActivatedRouteSnapshot,
Router,
RouterStateSnapshot,
} 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 { FeatureID } from '../feature-id';
import { SingleFeatureAuthorizationGuard } from './single-feature-authorization.guard'; import { singleFeatureAuthorizationGuard } from './single-feature-authorization.guard';
/** /**
* Prevent unauthorized activating and loading of routes when the current authenticated user * Prevent unauthorized activating and loading of routes when the current authenticated user
* isn't a Community administrator * isn't a Community administrator
* Check group management rights
*/ */
@Injectable({ export const communityAdministratorGuard: CanActivateFn =
providedIn: 'root', singleFeatureAuthorizationGuard(() => observableOf(FeatureID.IsCommunityAdmin));
})
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<FeatureID> {
return observableOf(FeatureID.IsCommunityAdmin);
}
}

View File

@@ -1,8 +1,8 @@
import { TestBed } from '@angular/core/testing';
import { import {
ActivatedRouteSnapshot,
ResolveFn, ResolveFn,
Router, Router,
RouterStateSnapshot, UrlTree,
} from '@angular/router'; } from '@angular/router';
import { import {
Observable, Observable,
@@ -12,52 +12,39 @@ import {
import { createSuccessfulRemoteDataObject$ } from '../../../../shared/remote-data.utils'; import { createSuccessfulRemoteDataObject$ } from '../../../../shared/remote-data.utils';
import { AuthService } from '../../../auth/auth.service'; import { AuthService } from '../../../auth/auth.service';
import { DSpaceObject } from '../../../shared/dspace-object.model'; import { DSpaceObject } from '../../../shared/dspace-object.model';
import { Item } from '../../../shared/item.model';
import { RemoteData } from '../../remote-data'; import { RemoteData } from '../../remote-data';
import { AuthorizationDataService } from '../authorization-data.service'; import { AuthorizationDataService } from '../authorization-data.service';
import { FeatureID } from '../feature-id'; import { FeatureID } from '../feature-id';
import { DsoPageSingleFeatureGuard } from './dso-page-single-feature.guard'; import { dsoPageSingleFeatureGuard } from './dso-page-single-feature.guard';
import {
defaultDSOGetObjectUrl,
getRouteWithDSOId,
} from './dso-page-some-feature.guard';
const object = {
self: 'test-selflink',
} as DSpaceObject;
const testResolver: ResolveFn<RemoteData<any>> = () => createSuccessfulRemoteDataObject$(object);
/**
* Test implementation of abstract class DsoPageSingleFeatureGuard
*/
class DsoPageSingleFeatureGuardImpl extends DsoPageSingleFeatureGuard<any> {
protected resolver: ResolveFn<RemoteData<Item>> = testResolver;
constructor(protected authorizationService: AuthorizationDataService,
protected router: Router,
protected authService: AuthService,
protected featureID: FeatureID) {
super(authorizationService, router, authService);
}
getFeatureID(route: ActivatedRouteSnapshot, state: RouterStateSnapshot): Observable<FeatureID> {
return observableOf(this.featureID);
}
}
describe('DsoPageSingleFeatureGuard', () => { describe('DsoPageSingleFeatureGuard', () => {
let guard: DsoPageSingleFeatureGuard<any>;
let authorizationService: AuthorizationDataService; let authorizationService: AuthorizationDataService;
let router: Router; let router: Router;
let authService: AuthService; let authService: AuthService;
let resolver: ResolveFn<RemoteData<any>>;
let object: DSpaceObject;
let route; let route;
let parentRoute; let parentRoute;
let featureId: FeatureID;
function init() { function init() {
object = {
self: 'test-selflink',
} as DSpaceObject;
authorizationService = jasmine.createSpyObj('authorizationService', { authorizationService = jasmine.createSpyObj('authorizationService', {
isAuthorized: observableOf(true), isAuthorized: observableOf(true),
}); });
router = jasmine.createSpyObj('router', { router = jasmine.createSpyObj('router', {
parseUrl: {}, parseUrl: {},
}); });
resolver = () => createSuccessfulRemoteDataObject$(object);
authService = jasmine.createSpyObj('authService', { authService = jasmine.createSpyObj('authService', {
isAuthenticated: observableOf(true), isAuthenticated: observableOf(true),
}); });
@@ -71,16 +58,25 @@ describe('DsoPageSingleFeatureGuard', () => {
}, },
parent: parentRoute, parent: parentRoute,
}; };
guard = new DsoPageSingleFeatureGuardImpl(authorizationService, router, authService, undefined);
featureId = FeatureID.LoginOnBehalfOf;
TestBed.configureTestingModule({
providers: [
{ provide: AuthorizationDataService, useValue: authorizationService },
{ provide: Router, useValue: router },
{ provide: AuthService, useValue: authService },
],
});
} }
beforeEach(() => { beforeEach(() => {
init(); init();
}); });
describe('getObjectUrl', () => { describe('defaultDSOGetObjectUrl', () => {
it('should return the resolved object\'s selflink', (done) => { it('should return the resolved object\'s selflink', (done) => {
guard.getObjectUrl(route, undefined).subscribe((selflink) => { defaultDSOGetObjectUrl(resolver)(route, undefined).subscribe((selflink) => {
expect(selflink).toEqual(object.self); expect(selflink).toEqual(object.self);
done(); done();
}); });
@@ -89,8 +85,23 @@ describe('DsoPageSingleFeatureGuard', () => {
describe('getRouteWithDSOId', () => { describe('getRouteWithDSOId', () => {
it('should return the route that has the UUID of the DSO', () => { it('should return the route that has the UUID of the DSO', () => {
const foundRoute = (guard as any).getRouteWithDSOId(route); const foundRoute = getRouteWithDSOId(route);
expect(foundRoute).toBe(parentRoute); expect(foundRoute).toBe(parentRoute);
}); });
}); });
describe('dsoPageSingleFeatureGuard', () => {
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<boolean | UrlTree>;
result$.subscribe(() => {
expect(authorizationService.isAuthorized).toHaveBeenCalledWith(featureId, object.self, undefined);
done();
});
});
});
}); });

View File

@@ -1,31 +1,27 @@
import { import {
ActivatedRouteSnapshot, ActivatedRouteSnapshot,
CanActivateFn,
ResolveFn,
RouterStateSnapshot, RouterStateSnapshot,
} from '@angular/router'; } from '@angular/router';
import { Observable } from 'rxjs'; import { Observable } from 'rxjs';
import { map } from 'rxjs/operators'; import { map } from 'rxjs/operators';
import { DSpaceObject } from '../../../shared/dspace-object.model'; import { DSpaceObject } from '../../../shared/dspace-object.model';
import { RemoteData } from '../../remote-data';
import { FeatureID } from '../feature-id'; import { FeatureID } from '../feature-id';
import { DsoPageSomeFeatureGuard } from './dso-page-some-feature.guard'; 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 * 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 * This guard utilizes a resolver to retrieve the relevant object to check authorizations for
*/ */
export abstract class DsoPageSingleFeatureGuard<T extends DSpaceObject> extends DsoPageSomeFeatureGuard<T> { export const dsoPageSingleFeatureGuard = <T extends DSpaceObject> (
/** getResolveFn: () => ResolveFn<RemoteData<T>>,
* The features to check authorization for getFeatureID: SingleFeatureGuardParamFn,
*/ ): CanActivateFn => dsoPageSomeFeatureGuard(
getFeatureIDs(route: ActivatedRouteSnapshot, state: RouterStateSnapshot): Observable<FeatureID[]> { getResolveFn,
return this.getFeatureID(route, state).pipe( (route: ActivatedRouteSnapshot, state: RouterStateSnapshot): Observable<FeatureID[]> => getFeatureID(route, state).pipe(
map((featureID) => [featureID]), map((featureID: FeatureID) => [featureID]),
); ));
}
/**
* The type of feature to check authorization for
* Override this method to define a feature
*/
abstract getFeatureID(route: ActivatedRouteSnapshot, state: RouterStateSnapshot): Observable<FeatureID>;
}

View File

@@ -1,8 +1,8 @@
import { TestBed } from '@angular/core/testing';
import { import {
ActivatedRouteSnapshot,
ResolveFn, ResolveFn,
Router, Router,
RouterStateSnapshot, UrlTree,
} from '@angular/router'; } from '@angular/router';
import { import {
Observable, Observable,
@@ -12,53 +12,39 @@ import {
import { createSuccessfulRemoteDataObject$ } from '../../../../shared/remote-data.utils'; import { createSuccessfulRemoteDataObject$ } from '../../../../shared/remote-data.utils';
import { AuthService } from '../../../auth/auth.service'; import { AuthService } from '../../../auth/auth.service';
import { DSpaceObject } from '../../../shared/dspace-object.model'; import { DSpaceObject } from '../../../shared/dspace-object.model';
import { Item } from '../../../shared/item.model';
import { RemoteData } from '../../remote-data'; import { RemoteData } from '../../remote-data';
import { AuthorizationDataService } from '../authorization-data.service'; import { AuthorizationDataService } from '../authorization-data.service';
import { FeatureID } from '../feature-id'; import { FeatureID } from '../feature-id';
import { DsoPageSomeFeatureGuard } from './dso-page-some-feature.guard'; import {
defaultDSOGetObjectUrl,
dsoPageSomeFeatureGuard,
getRouteWithDSOId,
} from './dso-page-some-feature.guard';
const object = {
self: 'test-selflink',
} as DSpaceObject;
const testResolver: ResolveFn<RemoteData<any>> = () => createSuccessfulRemoteDataObject$(object); describe('dsoPageSomeFeatureGuard and its functions', () => {
/**
* Test implementation of abstract class DsoPageSomeFeatureGuard
*/
class DsoPageSomeFeatureGuardImpl extends DsoPageSomeFeatureGuard<any> {
protected resolver: ResolveFn<RemoteData<Item>> = testResolver;
constructor(protected authorizationService: AuthorizationDataService,
protected router: Router,
protected authService: AuthService,
protected featureIDs: FeatureID[]) {
super(authorizationService, router, authService);
}
getFeatureIDs(route: ActivatedRouteSnapshot, state: RouterStateSnapshot): Observable<FeatureID[]> {
return observableOf(this.featureIDs);
}
}
describe('DsoPageSomeFeatureGuard', () => {
let guard: DsoPageSomeFeatureGuard<any>;
let authorizationService: AuthorizationDataService; let authorizationService: AuthorizationDataService;
let router: Router; let router: Router;
let authService: AuthService; let authService: AuthService;
let resolver: ResolveFn<RemoteData<any>>;
let object: DSpaceObject;
let route; let route;
let parentRoute; let parentRoute;
let featureIds: FeatureID[];
function init() { function init() {
object = {
self: 'test-selflink',
} as DSpaceObject;
featureIds = [FeatureID.LoginOnBehalfOf, FeatureID.CanDelete];
authorizationService = jasmine.createSpyObj('authorizationService', { authorizationService = jasmine.createSpyObj('authorizationService', {
isAuthorized: observableOf(true), isAuthorized: observableOf(true),
}); });
router = jasmine.createSpyObj('router', { router = jasmine.createSpyObj('router', {
parseUrl: {}, parseUrl: {},
}); });
resolver = () => createSuccessfulRemoteDataObject$(object);
authService = jasmine.createSpyObj('authService', { authService = jasmine.createSpyObj('authService', {
isAuthenticated: observableOf(true), isAuthenticated: observableOf(true),
}); });
@@ -72,16 +58,25 @@ describe('DsoPageSomeFeatureGuard', () => {
}, },
parent: parentRoute, parent: parentRoute,
}; };
guard = new DsoPageSomeFeatureGuardImpl(authorizationService, router, authService, []);
TestBed.configureTestingModule({
providers: [
{ provide: AuthorizationDataService, useValue: authorizationService },
{ provide: Router, useValue: router },
{ provide: AuthService, useValue: authService },
],
});
} }
beforeEach(() => { beforeEach(() => {
init(); init();
}); });
describe('getObjectUrl', () => {
describe('defaultDSOGetObjectUrl', () => {
it('should return the resolved object\'s selflink', (done) => { it('should return the resolved object\'s selflink', (done) => {
guard.getObjectUrl(route, undefined).subscribe((selflink) => { defaultDSOGetObjectUrl(resolver)(route, undefined).subscribe((selflink) => {
expect(selflink).toEqual(object.self); expect(selflink).toEqual(object.self);
done(); done();
}); });
@@ -90,8 +85,26 @@ describe('DsoPageSomeFeatureGuard', () => {
describe('getRouteWithDSOId', () => { describe('getRouteWithDSOId', () => {
it('should return the route that has the UUID of the DSO', () => { it('should return the route that has the UUID of the DSO', () => {
const foundRoute = (guard as any).getRouteWithDSOId(route); const foundRoute = getRouteWithDSOId(route);
expect(foundRoute).toBe(parentRoute); 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<boolean | UrlTree>;
result$.subscribe(() => {
featureIds.forEach((featureId: FeatureID) => {
expect(authorizationService.isAuthorized).toHaveBeenCalledWith(featureId, object.self, undefined);
});
done();
});
});
});
}); });

View File

@@ -1,7 +1,7 @@
import { import {
ActivatedRouteSnapshot, ActivatedRouteSnapshot,
CanActivateFn,
ResolveFn, ResolveFn,
Router,
RouterStateSnapshot, RouterStateSnapshot,
} from '@angular/router'; } from '@angular/router';
import { Observable } from 'rxjs'; import { Observable } from 'rxjs';
@@ -11,47 +11,50 @@ import {
hasNoValue, hasNoValue,
hasValue, hasValue,
} from '../../../../shared/empty.util'; } from '../../../../shared/empty.util';
import { AuthService } from '../../../auth/auth.service';
import { DSpaceObject } from '../../../shared/dspace-object.model'; import { DSpaceObject } from '../../../shared/dspace-object.model';
import { getAllSucceededRemoteDataPayload } from '../../../shared/operators'; import { getAllSucceededRemoteDataPayload } from '../../../shared/operators';
import { RemoteData } from '../../remote-data'; import { RemoteData } from '../../remote-data';
import { AuthorizationDataService } from '../authorization-data.service'; import { FeatureID } from '../feature-id';
import { SomeFeatureAuthorizationGuard } from './some-feature-authorization.guard'; import {
someFeatureAuthorizationGuard,
SomeFeatureGuardParamFn,
StringGuardParamFn,
} from './some-feature-authorization.guard';
export declare type DSOGetObjectURlFn = <T extends DSpaceObject>(resolve: ResolveFn<RemoteData<T>>) => StringGuardParamFn;
/** /**
* Abstract Guard for preventing unauthorized access to {@link DSpaceObject} pages that require rights for any specific feature in a list * Method to resolve resolve (parent) route that contains the UUID of the DSO
* This guard utilizes a resolver to retrieve the relevant object to check authorizations for * @param route The current route
*/ */
export abstract class DsoPageSomeFeatureGuard<T extends DSpaceObject> extends SomeFeatureAuthorizationGuard { export const getRouteWithDSOId = (route: ActivatedRouteSnapshot): ActivatedRouteSnapshot => {
let routeWithDSOId = route;
protected abstract resolver: ResolveFn<RemoteData<DSpaceObject>>; while (hasNoValue(routeWithDSOId.params.id) && hasValue(routeWithDSOId.parent)) {
routeWithDSOId = routeWithDSOId.parent;
constructor(protected authorizationService: AuthorizationDataService,
protected router: Router,
protected authService: AuthService) {
super(authorizationService, router, authService);
} }
return routeWithDSOId;
};
/**
* Check authorization rights for the object resolved using the provided resolver
*/ export const defaultDSOGetObjectUrl: DSOGetObjectURlFn = <T extends DSpaceObject>(resolve: ResolveFn<RemoteData<T>>): StringGuardParamFn => {
getObjectUrl(route: ActivatedRouteSnapshot, state: RouterStateSnapshot): Observable<string> { return (route: ActivatedRouteSnapshot, state: RouterStateSnapshot): Observable<string> => {
const routeWithObjectID = this.getRouteWithDSOId(route); const routeWithObjectID = getRouteWithDSOId(route);
return (this.resolver(routeWithObjectID, state) as Observable<RemoteData<T>>).pipe( return (resolve(routeWithObjectID, state) as Observable<RemoteData<T>>).pipe(
getAllSucceededRemoteDataPayload(), getAllSucceededRemoteDataPayload(),
map((dso) => dso.self), map((dso) => dso.self),
); );
} };
};
/** /**
* Method to resolve (parent) route that contains the UUID of the DSO * Guard for preventing unauthorized access to {@link DSpaceObject} pages that require rights for any specific feature in a list
* @param route The current route * This guard utilizes a resolver to retrieve the relevant object to check authorizations for
*/ */
protected getRouteWithDSOId(route: ActivatedRouteSnapshot): ActivatedRouteSnapshot { export const dsoPageSomeFeatureGuard = <T extends DSpaceObject>(
let routeWithDSOId = route; getResolveFn: () => ResolveFn<RemoteData<T>>,
while (hasNoValue(routeWithDSOId.params.id) && hasValue(routeWithDSOId.parent)) { getFeatureIDs: SomeFeatureGuardParamFn,
routeWithDSOId = routeWithDSOId.parent; getObjectUrl: DSOGetObjectURlFn = defaultDSOGetObjectUrl,
} getEPersonUuid?: StringGuardParamFn,
return routeWithDSOId; ): CanActivateFn => someFeatureAuthorizationGuard((route: ActivatedRouteSnapshot, state: RouterStateSnapshot): Observable<FeatureID[]> => getFeatureIDs(route, state), getObjectUrl(getResolveFn()), getEPersonUuid);
}
}

View File

@@ -1,35 +1,12 @@
import { Injectable } from '@angular/core'; import { CanActivateFn } from '@angular/router';
import { import { of as observableOf } from 'rxjs';
ActivatedRouteSnapshot,
Router,
RouterStateSnapshot,
} 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 { FeatureID } from '../feature-id';
import { SingleFeatureAuthorizationGuard } from './single-feature-authorization.guard'; import { singleFeatureAuthorizationGuard } from './single-feature-authorization.guard';
/** /**
* Prevent unauthorized activating and loading of routes when the current authenticated user doesn't have group * Prevent unauthorized activating and loading of routes when the current authenticated user doesn't have group
* management rights * management rights
*/ */
@Injectable({ export const groupAdministratorGuard: CanActivateFn =
providedIn: 'root', singleFeatureAuthorizationGuard(() => observableOf(FeatureID.CanManageGroups));
})
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<FeatureID> {
return observableOf(FeatureID.CanManageGroups);
}
}

View File

@@ -1,7 +1,10 @@
import { import {
ActivatedRouteSnapshot, TestBed,
waitForAsync,
} from '@angular/core/testing';
import {
Router, Router,
RouterStateSnapshot, UrlTree,
} from '@angular/router'; } from '@angular/router';
import { import {
Observable, Observable,
@@ -11,37 +14,9 @@ import {
import { AuthService } from '../../../auth/auth.service'; import { AuthService } from '../../../auth/auth.service';
import { AuthorizationDataService } from '../authorization-data.service'; import { AuthorizationDataService } from '../authorization-data.service';
import { FeatureID } from '../feature-id'; import { FeatureID } from '../feature-id';
import { SingleFeatureAuthorizationGuard } from './single-feature-authorization.guard'; import { singleFeatureAuthorizationGuard } from './single-feature-authorization.guard';
/** describe('singleFeatureAuthorizationGuard', () => {
* 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<FeatureID> {
return observableOf(this.featureId);
}
getObjectUrl(route: ActivatedRouteSnapshot, state: RouterStateSnapshot): Observable<string> {
return observableOf(this.objectUrl);
}
getEPersonUuid(route: ActivatedRouteSnapshot, state: RouterStateSnapshot): Observable<string> {
return observableOf(this.ePersonUuid);
}
}
describe('SingleFeatureAuthorizationGuard', () => {
let guard: SingleFeatureAuthorizationGuard;
let authorizationService: AuthorizationDataService; let authorizationService: AuthorizationDataService;
let router: Router; let router: Router;
let authService: AuthService; let authService: AuthService;
@@ -64,17 +39,36 @@ describe('SingleFeatureAuthorizationGuard', () => {
authService = jasmine.createSpyObj('authService', { authService = jasmine.createSpyObj('authService', {
isAuthenticated: observableOf(true), isAuthenticated: observableOf(true),
}); });
guard = new SingleFeatureAuthorizationGuardImpl(authorizationService, router, authService, featureId, objectUrl, ePersonUuid);
TestBed.configureTestingModule({
providers: [
{ provide: AuthorizationDataService, useValue: authorizationService },
{ provide: Router, useValue: router },
{ provide: AuthService, useValue: authService },
],
});
} }
beforeEach(() => { beforeEach(waitForAsync(() => {
init(); init();
}); }));
describe('canActivate', () => { describe('canActivate', () => {
it('should call authorizationService.isAuthenticated with the appropriate arguments', () => { it('should call authorizationService.isAuthenticated with the appropriate arguments', (done: DoneFn) => {
guard.canActivate(undefined, { url: 'current-url' } as any).subscribe(); const result$ = TestBed.runInInjectionContext(() => {
expect(authorizationService.isAuthorized).toHaveBeenCalledWith(featureId, objectUrl, ePersonUuid); return singleFeatureAuthorizationGuard(
() => observableOf(featureId),
() => observableOf(objectUrl),
() => observableOf(ePersonUuid),
)(undefined, { url: 'current-url' } as any);
}) as Observable<boolean | UrlTree>;
result$.subscribe(() => {
expect(authorizationService.isAuthorized).toHaveBeenCalledWith(featureId, objectUrl, ePersonUuid);
done();
});
}); });
}); });
}); });

View File

@@ -1,31 +1,35 @@
import { import {
ActivatedRouteSnapshot, ActivatedRouteSnapshot,
CanActivateFn,
RouterStateSnapshot, RouterStateSnapshot,
} from '@angular/router'; } from '@angular/router';
import { Observable } from 'rxjs'; import { Observable } from 'rxjs';
import { map } from 'rxjs/operators'; import { map } from 'rxjs/operators';
import { FeatureID } from '../feature-id'; import { FeatureID } from '../feature-id';
import { SomeFeatureAuthorizationGuard } from './some-feature-authorization.guard'; import {
someFeatureAuthorizationGuard,
StringGuardParamFn,
} from './some-feature-authorization.guard';
export declare type SingleFeatureGuardParamFn = (route: ActivatedRouteSnapshot, state: RouterStateSnapshot) => Observable<FeatureID>;
/** /**
* Abstract Guard for preventing unauthorized activating and loading of routes when a user * Guard for preventing unauthorized activating and loading of routes when a user doesn't have
* doesn't have authorized rights on a specific feature and/or object. * 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. *
* @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<FeatureID[]> {
return this.getFeatureID(route, state).pipe(
map((featureID) => [featureID]),
);
}
/** export const singleFeatureAuthorizationGuard = (
* The type of feature to check authorization for getFeatureID: SingleFeatureGuardParamFn,
* Override this method to define a feature getObjectUrl?: StringGuardParamFn,
*/ getEPersonUuid?: StringGuardParamFn,
abstract getFeatureID(route: ActivatedRouteSnapshot, state: RouterStateSnapshot): Observable<FeatureID>; ): CanActivateFn => someFeatureAuthorizationGuard(
} (route: ActivatedRouteSnapshot, state: RouterStateSnapshot): Observable<FeatureID[]> => getFeatureID(route, state).pipe(
map((featureID: FeatureID) => [featureID]),
), getObjectUrl, getEPersonUuid);

View File

@@ -1,33 +1,12 @@
import { Injectable } from '@angular/core'; import { CanActivateFn } from '@angular/router';
import { import { of as observableOf } from 'rxjs';
ActivatedRouteSnapshot,
Router,
RouterStateSnapshot,
} 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 { FeatureID } from '../feature-id';
import { SingleFeatureAuthorizationGuard } from './single-feature-authorization.guard'; import { singleFeatureAuthorizationGuard } from './single-feature-authorization.guard';
/** /**
* Prevent unauthorized activating and loading of routes when the current authenticated user doesn't have administrator * Prevent unauthorized activating and loading of routes when the current authenticated user doesn't have administrator
* rights to the {@link Site} * rights to the {@link Site}
*/ */
@Injectable({ providedIn: 'root' }) export const siteAdministratorGuard: CanActivateFn =
export class SiteAdministratorGuard extends SingleFeatureAuthorizationGuard { singleFeatureAuthorizationGuard(() => observableOf(FeatureID.AdministratorOf));
constructor(protected authorizationService: AuthorizationDataService, protected router: Router, protected authService: AuthService) {
super(authorizationService, router, authService);
}
/**
* Check administrator authorization rights
*/
getFeatureID(route: ActivatedRouteSnapshot, state: RouterStateSnapshot): Observable<FeatureID> {
return observableOf(FeatureID.AdministratorOf);
}
}

View File

@@ -1,33 +1,12 @@
import { Injectable } from '@angular/core'; import { CanActivateFn } from '@angular/router';
import { import { of as observableOf } from 'rxjs';
ActivatedRouteSnapshot,
Router,
RouterStateSnapshot,
} 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 { FeatureID } from '../feature-id';
import { SingleFeatureAuthorizationGuard } from './single-feature-authorization.guard'; import { singleFeatureAuthorizationGuard } from './single-feature-authorization.guard';
/** /**
* Prevent unauthorized activating and loading of routes when the current authenticated user doesn't have registration * Prevent unauthorized activating and loading of routes when the current authenticated user doesn't have registration
* rights to the {@link Site} * rights to the {@link Site}
*/ */
@Injectable({ providedIn: 'root' }) export const siteRegisterGuard: CanActivateFn =
export class SiteRegisterGuard extends SingleFeatureAuthorizationGuard { singleFeatureAuthorizationGuard(() => observableOf(FeatureID.EPersonRegistration));
constructor(protected authorizationService: AuthorizationDataService, protected router: Router, protected authService: AuthService) {
super(authorizationService, router, authService);
}
/**
* Check registration authorization rights
*/
getFeatureID(route: ActivatedRouteSnapshot, state: RouterStateSnapshot): Observable<FeatureID> {
return observableOf(FeatureID.EPersonRegistration);
}
}

View File

@@ -1,7 +1,10 @@
import { import {
ActivatedRouteSnapshot, TestBed,
waitForAsync,
} from '@angular/core/testing';
import {
Router, Router,
RouterStateSnapshot, UrlTree,
} from '@angular/router'; } from '@angular/router';
import { import {
Observable, Observable,
@@ -11,37 +14,9 @@ import {
import { AuthService } from '../../../auth/auth.service'; import { AuthService } from '../../../auth/auth.service';
import { AuthorizationDataService } from '../authorization-data.service'; import { AuthorizationDataService } from '../authorization-data.service';
import { FeatureID } from '../feature-id'; import { FeatureID } from '../feature-id';
import { SomeFeatureAuthorizationGuard } from './some-feature-authorization.guard'; 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<FeatureID[]> {
return observableOf(this.featureIds);
}
getObjectUrl(route: ActivatedRouteSnapshot, state: RouterStateSnapshot): Observable<string> {
return observableOf(this.objectUrl);
}
getEPersonUuid(route: ActivatedRouteSnapshot, state: RouterStateSnapshot): Observable<string> {
return observableOf(this.ePersonUuid);
}
}
describe('SomeFeatureAuthorizationGuard', () => { describe('SomeFeatureAuthorizationGuard', () => {
let guard: SomeFeatureAuthorizationGuard;
let authorizationService: AuthorizationDataService; let authorizationService: AuthorizationDataService;
let router: Router; let router: Router;
let authService: AuthService; let authService: AuthService;
@@ -62,18 +37,27 @@ describe('SomeFeatureAuthorizationGuard', () => {
return observableOf(authorizedFeatureIds.indexOf(featureId) > -1); return observableOf(authorizedFeatureIds.indexOf(featureId) > -1);
}, },
}); });
router = jasmine.createSpyObj('router', { router = jasmine.createSpyObj('router', {
parseUrl: {}, parseUrl: {},
}); });
authService = jasmine.createSpyObj('authService', { authService = jasmine.createSpyObj('authService', {
isAuthenticated: observableOf(true), isAuthenticated: observableOf(true),
}); });
guard = new SomeFeatureAuthorizationGuardImpl(authorizationService, router, authService, featureIds, objectUrl, ePersonUuid);
TestBed.configureTestingModule({
providers: [
{ provide: AuthorizationDataService, useValue: authorizationService },
{ provide: Router, useValue: router },
{ provide: AuthService, useValue: authService },
],
});
} }
beforeEach(() => { beforeEach(waitForAsync(() => {
init(); init();
}); }));
describe('canActivate', () => { describe('canActivate', () => {
describe('when the user isn\'t authorized', () => { describe('when the user isn\'t authorized', () => {
@@ -82,7 +66,16 @@ describe('SomeFeatureAuthorizationGuard', () => {
}); });
it('should not return true', (done) => { it('should not return true', (done) => {
guard.canActivate(undefined, { url: 'current-url' } as any).subscribe((result) => {
const result$ = TestBed.runInInjectionContext(() => {
return someFeatureAuthorizationGuard(
() => observableOf(featureIds),
() => observableOf(objectUrl),
() => observableOf(ePersonUuid),
)(undefined, { url: 'current-url' } as any);
}) as Observable<boolean | UrlTree>;
result$.subscribe((result) => {
expect(result).not.toEqual(true); expect(result).not.toEqual(true);
done(); done();
}); });
@@ -95,7 +88,16 @@ describe('SomeFeatureAuthorizationGuard', () => {
}); });
it('should return true', (done) => { it('should return true', (done) => {
guard.canActivate(undefined, { url: 'current-url' } as any).subscribe((result) => {
const result$ = TestBed.runInInjectionContext(() => {
return someFeatureAuthorizationGuard(
() => observableOf(featureIds),
() => observableOf(objectUrl),
() => observableOf(ePersonUuid),
)(undefined, { url: 'current-url' } as any);
}) as Observable<boolean | UrlTree>;
result$.subscribe((result) => {
expect(result).toEqual(true); expect(result).toEqual(true);
done(); done();
}); });
@@ -108,7 +110,16 @@ describe('SomeFeatureAuthorizationGuard', () => {
}); });
it('should return true', (done) => { it('should return true', (done) => {
guard.canActivate(undefined, { url: 'current-url' } as any).subscribe((result) => {
const result$ = TestBed.runInInjectionContext(() => {
return someFeatureAuthorizationGuard(
() => observableOf(featureIds),
() => observableOf(objectUrl),
() => observableOf(ePersonUuid),
)(undefined, { url: 'current-url' } as any);
}) as Observable<boolean | UrlTree>;
result$.subscribe((result) => {
expect(result).toEqual(true); expect(result).toEqual(true);
done(); done();
}); });

View File

@@ -1,5 +1,7 @@
import { inject } from '@angular/core';
import { import {
ActivatedRouteSnapshot, ActivatedRouteSnapshot,
CanActivateFn,
Router, Router,
RouterStateSnapshot, RouterStateSnapshot,
UrlTree, UrlTree,
@@ -16,49 +18,39 @@ import { returnForbiddenUrlTreeOrLoginOnAllFalse } from '../../../shared/authori
import { AuthorizationDataService } from '../authorization-data.service'; import { AuthorizationDataService } from '../authorization-data.service';
import { FeatureID } from '../feature-id'; import { FeatureID } from '../feature-id';
export declare type SomeFeatureGuardParamFn = (route: ActivatedRouteSnapshot, state: RouterStateSnapshot) => Observable<FeatureID[]>;
export declare type StringGuardParamFn = (route: ActivatedRouteSnapshot, state: RouterStateSnapshot) => Observable<string>;
export const defaultStringGuardParamFn = () => observableOf(undefined);
/** /**
* Abstract Guard for preventing unauthorized activating and loading of routes when a user * Guard for preventing unauthorized activating and loading of routes when a user doesn't have
* doesn't have authorized rights on any of the specified features and/or object. * 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.
* @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 abstract class SomeFeatureAuthorizationGuard { export const someFeatureAuthorizationGuard = (
constructor(protected authorizationService: AuthorizationDataService, getFeatureIDs: SomeFeatureGuardParamFn,
protected router: Router, getObjectUrl: StringGuardParamFn = defaultStringGuardParamFn,
protected authService: AuthService) { getEPersonUuid: StringGuardParamFn = defaultStringGuardParamFn,
} ): CanActivateFn => {
return (route: ActivatedRouteSnapshot, state: RouterStateSnapshot): Observable<boolean | UrlTree> => {
/** const authorizationService = inject(AuthorizationDataService);
* True when user has authorization rights for the feature and object provided const router = inject(Router);
* Redirect the user to the unauthorized page when they are not authorized for the given feature const authService = inject(AuthService);
*/ return observableCombineLatest([
canActivate(route: ActivatedRouteSnapshot, state: RouterStateSnapshot): Observable<boolean | UrlTree> { getFeatureIDs(route, state),
return observableCombineLatest(this.getFeatureIDs(route, state), this.getObjectUrl(route, state), this.getEPersonUuid(route, state)).pipe( getObjectUrl(route, state),
switchMap(([featureIDs, objectUrl, ePersonUuid]) => getEPersonUuid(route, state),
observableCombineLatest(...featureIDs.map((featureID) => this.authorizationService.isAuthorized(featureID, objectUrl, ePersonUuid))), ]).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<FeatureID[]>;
/**
* 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<string> {
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<string> {
return observableOf(undefined);
}
}

View File

@@ -1,35 +1,12 @@
import { Injectable } from '@angular/core'; import { CanActivateFn } from '@angular/router';
import { import { of as observableOf } from 'rxjs';
ActivatedRouteSnapshot,
Router,
RouterStateSnapshot,
} 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 { FeatureID } from '../feature-id';
import { SingleFeatureAuthorizationGuard } from './single-feature-authorization.guard'; import { singleFeatureAuthorizationGuard } from './single-feature-authorization.guard';
/** /**
* Prevent unauthorized activating and loading of routes when the current authenticated user doesn't have group * Prevent unauthorized activating and loading of routes when the current authenticated user doesn't have group
* management rights * management rights
*/ */
@Injectable({ export const statisticsAdministratorGuard: CanActivateFn =
providedIn: 'root', singleFeatureAuthorizationGuard(() => observableOf(FeatureID.CanViewUsageStatistics));
})
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<FeatureID> {
return observableOf(FeatureID.CanViewUsageStatistics);
}
}

View File

@@ -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<boolean | UrlTree> {
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<boolean>;
}

View File

@@ -1,13 +1,14 @@
import { TestBed } from '@angular/core/testing';
import { import {
Router, Router,
UrlTree, UrlTree,
} from '@angular/router'; } from '@angular/router';
import { Observable } from 'rxjs';
import { EndUserAgreementService } from './end-user-agreement.service'; 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', () => { describe('endUserAgreementCookieGuard', () => {
let guard: EndUserAgreementCookieGuard;
let endUserAgreementService: EndUserAgreementService; let endUserAgreementService: EndUserAgreementService;
let router: Router; let router: Router;
@@ -21,14 +22,22 @@ describe('EndUserAgreementCookieGuard', () => {
parseUrl: new UrlTree(), parseUrl: new UrlTree(),
createUrlTree: new UrlTree(), createUrlTree: new UrlTree(),
}); });
TestBed.configureTestingModule({
guard = new EndUserAgreementCookieGuard(endUserAgreementService, router); providers: [
{ provide: Router, useValue: router },
{ provide: EndUserAgreementService, useValue: endUserAgreementService },
],
});
}); });
describe('canActivate', () => { describe('canActivate', () => {
describe('when the cookie has been accepted', () => { describe('when the cookie has been accepted', () => {
it('should return true', (done) => { 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<boolean | UrlTree>;
result$.subscribe((result) => {
expect(result).toEqual(true); expect(result).toEqual(true);
done(); done();
}); });
@@ -41,7 +50,11 @@ describe('EndUserAgreementCookieGuard', () => {
}); });
it('should return a UrlTree', (done) => { 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<boolean | UrlTree>;
result$.subscribe((result) => {
expect(result).toEqual(jasmine.any(UrlTree)); expect(result).toEqual(jasmine.any(UrlTree));
done(); done();
}); });

View File

@@ -1,29 +1,19 @@
import { Injectable } from '@angular/core'; import { inject } from '@angular/core';
import { Router } from '@angular/router'; import { CanActivateFn } from '@angular/router';
import { import { of as observableOf } from 'rxjs';
Observable,
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'; 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 const endUserAgreementCookieGuard: CanActivateFn =
export class EndUserAgreementCookieGuard extends AbstractEndUserAgreementGuard { endUserAgreementGuard(
() => {
constructor(protected endUserAgreementService: EndUserAgreementService, const endUserAgreementService = inject(EndUserAgreementService);
protected router: Router) { return observableOf(endUserAgreementService.isCookieAccepted());
super(router); },
} );
/**
* True when the user agreement cookie has been accepted
*/
hasAccepted(): Observable<boolean> {
return observableOf(this.endUserAgreementService.isCookieAccepted());
}
}

View File

@@ -1,16 +1,18 @@
import { TestBed } from '@angular/core/testing';
import { import {
Router, Router,
UrlTree, UrlTree,
} from '@angular/router'; } from '@angular/router';
import { of as observableOf } from 'rxjs'; import {
Observable,
of as observableOf,
} from 'rxjs';
import { environment } from '../../../environments/environment.test'; import { environment } from '../../../environments/environment.test';
import { EndUserAgreementService } from './end-user-agreement.service'; import { EndUserAgreementService } from './end-user-agreement.service';
import { EndUserAgreementCurrentUserGuard } from './end-user-agreement-current-user.guard'; import { endUserAgreementCurrentUserGuard } from './end-user-agreement-current-user.guard';
describe('EndUserAgreementGuard', () => {
let guard: EndUserAgreementCurrentUserGuard;
describe('endUserAgreementGuard', () => {
let endUserAgreementService: EndUserAgreementService; let endUserAgreementService: EndUserAgreementService;
let router: Router; let router: Router;
@@ -18,19 +20,30 @@ describe('EndUserAgreementGuard', () => {
endUserAgreementService = jasmine.createSpyObj('endUserAgreementService', { endUserAgreementService = jasmine.createSpyObj('endUserAgreementService', {
hasCurrentUserAcceptedAgreement: observableOf(true), hasCurrentUserAcceptedAgreement: observableOf(true),
}); });
router = jasmine.createSpyObj('router', { router = jasmine.createSpyObj('router', {
navigateByUrl: {}, navigateByUrl: {},
parseUrl: new UrlTree(), parseUrl: new UrlTree(),
createUrlTree: new UrlTree(), createUrlTree: new UrlTree(),
}); });
guard = new EndUserAgreementCurrentUserGuard(endUserAgreementService, router); TestBed.configureTestingModule({
providers: [
{ provide: Router, useValue: router },
{ provide: EndUserAgreementService, useValue: endUserAgreementService },
],
});
}); });
describe('canActivate', () => { describe('canActivate', () => {
describe('when the user has accepted the agreement', () => { describe('when the user has accepted the agreement', () => {
it('should return true', (done) => { 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<boolean | UrlTree>;
result$.subscribe((result) => {
expect(result).toEqual(true); expect(result).toEqual(true);
done(); done();
}); });
@@ -43,7 +56,11 @@ describe('EndUserAgreementGuard', () => {
}); });
it('should return a UrlTree', (done) => { 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<boolean | UrlTree>;
result$.subscribe((result) => {
expect(result).toEqual(jasmine.any(UrlTree)); expect(result).toEqual(jasmine.any(UrlTree));
done(); done();
}); });
@@ -53,7 +70,12 @@ describe('EndUserAgreementGuard', () => {
describe('when the end user agreement is disabled', () => { describe('when the end user agreement is disabled', () => {
it('should return true', (done) => { it('should return true', (done) => {
environment.info.enableEndUserAgreement = false; 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<boolean | UrlTree>;
result$.subscribe((result) => {
expect(result).toEqual(true); expect(result).toEqual(true);
done(); done();
}); });
@@ -61,7 +83,11 @@ describe('EndUserAgreementGuard', () => {
it('should not resolve to the end user agreement page', (done) => { it('should not resolve to the end user agreement page', (done) => {
environment.info.enableEndUserAgreement = false; 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<boolean | UrlTree>;
result$.subscribe((result) => {
expect(router.navigateByUrl).not.toHaveBeenCalled(); expect(router.navigateByUrl).not.toHaveBeenCalled();
done(); done();
}); });

View File

@@ -1,34 +1,25 @@
import { Injectable } from '@angular/core'; import { inject } from '@angular/core';
import { Router } from '@angular/router'; import { CanActivateFn } from '@angular/router';
import { import { of as observableOf } from 'rxjs';
Observable,
of as observableOf,
} from 'rxjs';
import { environment } from '../../../environments/environment'; 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'; 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 * Guard for preventing unauthorized access to certain pages
* requiring the end user agreement to have been accepted by the current user
*/ */
@Injectable({ providedIn: 'root' }) export const endUserAgreementCurrentUserGuard: CanActivateFn =
export class EndUserAgreementCurrentUserGuard extends AbstractEndUserAgreementGuard { endUserAgreementGuard(
() => {
const endUserAgreementService = inject(EndUserAgreementService);
if (!environment.info.enableEndUserAgreement) {
return observableOf(true);
}
constructor(protected endUserAgreementService: EndUserAgreementService, return endUserAgreementService.hasCurrentUserAcceptedAgreement(true);
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<boolean> {
if (!environment.info.enableEndUserAgreement) {
return observableOf(true);
}
return this.endUserAgreementService.hasCurrentUserAcceptedAgreement(true);
}
}

View File

@@ -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<boolean>;
/**
* 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<boolean | UrlTree> => {
const router = inject(Router);
if (!environment.info.enableEndUserAgreement) {
return observableOf(true);
}
return hasAccepted().pipe(
returnEndUserAgreementUrlTreeOnFalse(router, state.url),
);
};
};

View File

@@ -1,37 +1,11 @@
import { Injectable } from '@angular/core'; import { CanActivateFn } from '@angular/router';
import { import { of as observableOf } from 'rxjs';
ActivatedRouteSnapshot,
Router,
RouterStateSnapshot,
} from '@angular/router';
import {
Observable,
of,
} from 'rxjs';
import { AuthService } from '../auth/auth.service'; import { singleFeatureAuthorizationGuard } from '../data/feature-authorization/feature-authorization-guard/single-feature-authorization.guard';
import { AuthorizationDataService } from '../data/feature-authorization/authorization-data.service';
import { SingleFeatureAuthorizationGuard } from '../data/feature-authorization/feature-authorization-guard/single-feature-authorization.guard';
import { FeatureID } from '../data/feature-authorization/feature-id'; import { FeatureID } from '../data/feature-authorization/feature-id';
@Injectable({
providedIn: 'root',
})
/** /**
* Guard that checks if the forgot-password feature is enabled * Guard that checks if the forgot-password feature is enabled
*/ */
export class ForgotPasswordCheckGuard extends SingleFeatureAuthorizationGuard { export const forgotPasswordCheckGuard: CanActivateFn =
singleFeatureAuthorizationGuard(() => observableOf(FeatureID.EPersonForgotPassword));
constructor(
protected readonly authorizationService: AuthorizationDataService,
protected readonly router: Router,
protected readonly authService: AuthService,
) {
super(authorizationService, router, authService);
}
getFeatureID(route: ActivatedRouteSnapshot, state: RouterStateSnapshot): Observable<FeatureID> {
return of(FeatureID.EPersonForgotPassword);
}
}

View File

@@ -1,10 +1,7 @@
import { import { Route } from '@angular/router';
mapToCanActivate,
Route,
} from '@angular/router';
import { i18nBreadcrumbResolver } from '../core/breadcrumbs/i18n-breadcrumb.resolver'; 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'; import { HealthPageComponent } from './health-page.component';
export const ROUTES: Route[] = [ export const ROUTES: Route[] = [
@@ -15,7 +12,7 @@ export const ROUTES: Route[] = [
breadcrumbKey: 'health', breadcrumbKey: 'health',
title: 'health-page.title', title: 'health-page.title',
}, },
canActivate: mapToCanActivate([SiteAdministratorGuard]), canActivate: [siteAdministratorGuard],
component: HealthPageComponent, component: HealthPageComponent,
}, },
]; ];

View File

@@ -1,7 +1,4 @@
import { import { Route } from '@angular/router';
mapToCanActivate,
Route,
} from '@angular/router';
import { i18nBreadcrumbResolver } from '../../core/breadcrumbs/i18n-breadcrumb.resolver'; import { i18nBreadcrumbResolver } from '../../core/breadcrumbs/i18n-breadcrumb.resolver';
import { ThemedDsoEditMetadataComponent } from '../../dso-shared/dso-edit-metadata/themed-dso-edit-metadata.component'; import { ThemedDsoEditMetadataComponent } from '../../dso-shared/dso-edit-metadata/themed-dso-edit-metadata.component';
@@ -27,17 +24,21 @@ import { ItemCollectionMapperComponent } from './item-collection-mapper/item-col
import { ItemCurateComponent } from './item-curate/item-curate.component'; import { ItemCurateComponent } from './item-curate/item-curate.component';
import { ItemDeleteComponent } from './item-delete/item-delete.component'; import { ItemDeleteComponent } from './item-delete/item-delete.component';
import { ItemMoveComponent } from './item-move/item-move.component'; import { ItemMoveComponent } from './item-move/item-move.component';
import { ItemPageAccessControlGuard } from './item-page-access-control.guard'; import { itemPageAccessControlGuard } from './item-page-access-control.guard';
import { ItemPageBitstreamsGuard } from './item-page-bitstreams.guard'; import { itemPageBitstreamsGuard } from './item-page-bitstreams.guard';
import { ItemPageCollectionMapperGuard } from './item-page-collection-mapper.guard'; import { itemPageCollectionMapperGuard } from './item-page-collection-mapper.guard';
import { ItemPageCurateGuard } from './item-page-curate.guard'; import { itemPageCurateGuard } from './item-page-curate.guard';
import { ItemPageMetadataGuard } from './item-page-metadata.guard'; import { itemPageDeleteGuard } from './item-page-delete.guard';
import { ItemPageRegisterDoiGuard } from './item-page-register-doi.guard'; import { itemPageEditAuthorizationsGuard } from './item-page-edit-authorizations.guard';
import { ItemPageReinstateGuard } from './item-page-reinstate.guard'; import { itemPageMetadataGuard } from './item-page-metadata.guard';
import { ItemPageRelationshipsGuard } from './item-page-relationships.guard'; import { itemPageMoveGuard } from './item-page-move.guard';
import { ItemPageStatusGuard } from './item-page-status.guard'; import { itemPagePrivateGuard } from './item-page-private.guard';
import { ItemPageVersionHistoryGuard } from './item-page-version-history.guard'; import { itemPageRegisterDoiGuard } from './item-page-register-doi.guard';
import { ItemPageWithdrawGuard } from './item-page-withdraw.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 { ItemPrivateComponent } from './item-private/item-private.component';
import { ItemPublicComponent } from './item-public/item-public.component'; import { ItemPublicComponent } from './item-public/item-public.component';
import { ItemRegisterDoiComponent } from './item-register-doi/item-register-doi.component'; import { ItemRegisterDoiComponent } from './item-register-doi/item-register-doi.component';
@@ -72,31 +73,31 @@ export const ROUTES: Route[] = [
path: 'status', path: 'status',
component: ThemedItemStatusComponent, component: ThemedItemStatusComponent,
data: { title: 'item.edit.tabs.status.title', showBreadcrumbs: true }, data: { title: 'item.edit.tabs.status.title', showBreadcrumbs: true },
canActivate: mapToCanActivate([ItemPageStatusGuard]), canActivate: [itemPageStatusGuard],
}, },
{ {
path: 'bitstreams', path: 'bitstreams',
component: ItemBitstreamsComponent, component: ItemBitstreamsComponent,
data: { title: 'item.edit.tabs.bitstreams.title', showBreadcrumbs: true }, data: { title: 'item.edit.tabs.bitstreams.title', showBreadcrumbs: true },
canActivate: mapToCanActivate([ItemPageBitstreamsGuard]), canActivate: [itemPageBitstreamsGuard],
}, },
{ {
path: 'metadata', path: 'metadata',
component: ThemedDsoEditMetadataComponent, component: ThemedDsoEditMetadataComponent,
data: { title: 'item.edit.tabs.metadata.title', showBreadcrumbs: true }, data: { title: 'item.edit.tabs.metadata.title', showBreadcrumbs: true },
canActivate: mapToCanActivate([ItemPageMetadataGuard]), canActivate: [itemPageMetadataGuard],
}, },
{ {
path: 'curate', path: 'curate',
component: ItemCurateComponent, component: ItemCurateComponent,
data: { title: 'item.edit.tabs.curate.title', showBreadcrumbs: true }, data: { title: 'item.edit.tabs.curate.title', showBreadcrumbs: true },
canActivate: mapToCanActivate([ItemPageCurateGuard]), canActivate: [itemPageCurateGuard],
}, },
{ {
path: 'relationships', path: 'relationships',
component: ItemRelationshipsComponent, component: ItemRelationshipsComponent,
data: { title: 'item.edit.tabs.relationships.title', showBreadcrumbs: true }, data: { title: 'item.edit.tabs.relationships.title', showBreadcrumbs: true },
canActivate: mapToCanActivate([ItemPageRelationshipsGuard]), canActivate: [itemPageRelationshipsGuard],
}, },
/* TODO - uncomment & fix when view page exists /* TODO - uncomment & fix when view page exists
{ {
@@ -114,19 +115,19 @@ export const ROUTES: Route[] = [
path: 'versionhistory', path: 'versionhistory',
component: ItemVersionHistoryComponent, component: ItemVersionHistoryComponent,
data: { title: 'item.edit.tabs.versionhistory.title', showBreadcrumbs: true }, data: { title: 'item.edit.tabs.versionhistory.title', showBreadcrumbs: true },
canActivate: mapToCanActivate([ItemPageVersionHistoryGuard]), canActivate: [itemPageVersionHistoryGuard],
}, },
{ {
path: 'access-control', path: 'access-control',
component: ItemAccessControlComponent, component: ItemAccessControlComponent,
data: { title: 'item.edit.tabs.access-control.title', showBreadcrumbs: true }, data: { title: 'item.edit.tabs.access-control.title', showBreadcrumbs: true },
canActivate: mapToCanActivate([ItemPageAccessControlGuard]), canActivate: [itemPageAccessControlGuard],
}, },
{ {
path: 'mapper', path: 'mapper',
component: ItemCollectionMapperComponent, component: ItemCollectionMapperComponent,
data: { title: 'item.edit.tabs.item-mapper.title', showBreadcrumbs: true }, data: { title: 'item.edit.tabs.item-mapper.title', showBreadcrumbs: true },
canActivate: mapToCanActivate([ItemPageCollectionMapperGuard]), canActivate: [itemPageCollectionMapperGuard],
}, },
], ],
}, },
@@ -137,16 +138,17 @@ export const ROUTES: Route[] = [
{ {
path: ITEM_EDIT_WITHDRAW_PATH, path: ITEM_EDIT_WITHDRAW_PATH,
component: ItemWithdrawComponent, component: ItemWithdrawComponent,
canActivate: mapToCanActivate([ItemPageWithdrawGuard]), canActivate: [itemPageWithdrawGuard],
}, },
{ {
path: ITEM_EDIT_REINSTATE_PATH, path: ITEM_EDIT_REINSTATE_PATH,
component: ItemReinstateComponent, component: ItemReinstateComponent,
canActivate: mapToCanActivate([ItemPageReinstateGuard]), canActivate: [itemPageReinstateGuard],
}, },
{ {
path: ITEM_EDIT_PRIVATE_PATH, path: ITEM_EDIT_PRIVATE_PATH,
component: ItemPrivateComponent, component: ItemPrivateComponent,
canActivate: [itemPagePrivateGuard],
}, },
{ {
path: ITEM_EDIT_PUBLIC_PATH, path: ITEM_EDIT_PUBLIC_PATH,
@@ -155,16 +157,18 @@ export const ROUTES: Route[] = [
{ {
path: ITEM_EDIT_DELETE_PATH, path: ITEM_EDIT_DELETE_PATH,
component: ItemDeleteComponent, component: ItemDeleteComponent,
canActivate: [itemPageDeleteGuard],
}, },
{ {
path: ITEM_EDIT_MOVE_PATH, path: ITEM_EDIT_MOVE_PATH,
component: ItemMoveComponent, component: ItemMoveComponent,
data: { title: 'item.edit.move.title' }, data: { title: 'item.edit.move.title' },
canActivate: [itemPageMoveGuard],
}, },
{ {
path: ITEM_EDIT_REGISTER_DOI_PATH, path: ITEM_EDIT_REGISTER_DOI_PATH,
component: ItemRegisterDoiComponent, component: ItemRegisterDoiComponent,
canActivate: mapToCanActivate([ItemPageRegisterDoiGuard]), canActivate: [itemPageRegisterDoiGuard],
data: { title: 'item.edit.register-doi.title' }, data: { title: 'item.edit.register-doi.title' },
}, },
{ {
@@ -192,6 +196,7 @@ export const ROUTES: Route[] = [
data: { title: 'item.edit.authorizations.title' }, data: { title: 'item.edit.authorizations.title' },
}, },
], ],
canActivate: [itemPageEditAuthorizationsGuard],
}, },
], ],
}, },

View File

@@ -1,43 +1,15 @@
import { Injectable } from '@angular/core'; import { CanActivateFn } from '@angular/router';
import { import { of as observableOf } from 'rxjs';
ActivatedRouteSnapshot,
ResolveFn,
Router,
RouterStateSnapshot,
} from '@angular/router';
import {
Observable,
of as observableOf,
} from 'rxjs';
import { AuthService } from '../../core/auth/auth.service'; import { dsoPageSingleFeatureGuard } from '../../core/data/feature-authorization/feature-authorization-guard/dso-page-single-feature.guard';
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 { 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'; import { itemPageResolver } from '../item-page.resolver';
@Injectable({
providedIn: 'root',
})
/** /**
* Guard for preventing unauthorized access to certain {@link Item} pages requiring administrator rights * Guard for preventing unauthorized access to certain {@link Item} pages requiring administrator rights
*/ */
export class ItemPageAccessControlGuard extends DsoPageSingleFeatureGuard<Item> { export const itemPageAccessControlGuard: CanActivateFn =
dsoPageSingleFeatureGuard(
protected resolver: ResolveFn<RemoteData<Item>> = itemPageResolver; () => itemPageResolver,
() => observableOf(FeatureID.AdministratorOf),
constructor(protected authorizationService: AuthorizationDataService, );
protected router: Router,
protected authService: AuthService) {
super(authorizationService, router, authService);
}
/**
* Check administrator authorization rights
*/
getFeatureID(next: ActivatedRouteSnapshot, state: RouterStateSnapshot): Observable<FeatureID> {
return observableOf(FeatureID.AdministratorOf);
}
}

View File

@@ -1,43 +1,16 @@
import { Injectable } from '@angular/core'; import { CanActivateFn } from '@angular/router';
import { import { of as observableOf } from 'rxjs';
ActivatedRouteSnapshot,
ResolveFn,
Router,
RouterStateSnapshot,
} from '@angular/router';
import {
Observable,
of as observableOf,
} from 'rxjs';
import { AuthService } from '../../core/auth/auth.service'; import { dsoPageSingleFeatureGuard } from '../../core/data/feature-authorization/feature-authorization-guard/dso-page-single-feature.guard';
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 { 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'; import { itemPageResolver } from '../item-page.resolver';
@Injectable({
providedIn: 'root',
})
/** /**
* Guard for preventing unauthorized access to certain {@link Item} pages requiring manage bitstreams rights * Guard for preventing unauthorized access to certain {@link Item} pages requiring manage bitstreams rights
* Check manage bitstreams authorization rights
*/ */
export class ItemPageBitstreamsGuard extends DsoPageSingleFeatureGuard<Item> { export const itemPageBitstreamsGuard: CanActivateFn =
dsoPageSingleFeatureGuard(
protected resolver: ResolveFn<RemoteData<Item>> = itemPageResolver; () => itemPageResolver,
() => observableOf(FeatureID.CanManageBitstreamBundles),
constructor(protected authorizationService: AuthorizationDataService, );
protected router: Router,
protected authService: AuthService) {
super( authorizationService, router, authService);
}
/**
* Check manage bitstreams authorization rights
*/
getFeatureID(next: ActivatedRouteSnapshot, state: RouterStateSnapshot): Observable<FeatureID> {
return observableOf(FeatureID.CanManageBitstreamBundles);
}
}

View File

@@ -1,43 +1,16 @@
import { Injectable } from '@angular/core'; import { CanActivateFn } from '@angular/router';
import { import { of as observableOf } from 'rxjs';
ActivatedRouteSnapshot,
ResolveFn,
Router,
RouterStateSnapshot,
} from '@angular/router';
import {
Observable,
of as observableOf,
} from 'rxjs';
import { AuthService } from '../../core/auth/auth.service'; import { dsoPageSingleFeatureGuard } from '../../core/data/feature-authorization/feature-authorization-guard/dso-page-single-feature.guard';
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 { 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'; import { itemPageResolver } from '../item-page.resolver';
@Injectable({
providedIn: 'root',
})
/** /**
* Guard for preventing unauthorized access to certain {@link Item} pages requiring manage mappings rights * Guard for preventing unauthorized access to certain {@link Item} pages requiring manage mappings rights
* Check manage mappings authorization rights
*/ */
export class ItemPageCollectionMapperGuard extends DsoPageSingleFeatureGuard<Item> { export const itemPageCollectionMapperGuard: CanActivateFn =
dsoPageSingleFeatureGuard(
protected resolver: ResolveFn<RemoteData<Item>> = itemPageResolver; () => itemPageResolver,
() => observableOf(FeatureID.CanManageMappings),
constructor(protected authorizationService: AuthorizationDataService, );
protected router: Router,
protected authService: AuthService) {
super(authorizationService, router, authService);
}
/**
* Check manage mappings authorization rights
*/
getFeatureID(next: ActivatedRouteSnapshot, state: RouterStateSnapshot): Observable<FeatureID> {
return observableOf(FeatureID.CanManageMappings);
}
}

View File

@@ -1,43 +1,15 @@
import { Injectable } from '@angular/core'; import { CanActivateFn } from '@angular/router';
import { import { of as observableOf } from 'rxjs';
ActivatedRouteSnapshot,
ResolveFn,
Router,
RouterStateSnapshot,
} from '@angular/router';
import {
Observable,
of as observableOf,
} from 'rxjs';
import { AuthService } from '../../core/auth/auth.service'; import { dsoPageSingleFeatureGuard } from '../../core/data/feature-authorization/feature-authorization-guard/dso-page-single-feature.guard';
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 { 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'; import { itemPageResolver } from '../item-page.resolver';
@Injectable({
providedIn: 'root',
})
/** /**
* Guard for preventing unauthorized access to certain {@link Item} pages requiring administrator rights * Guard for preventing unauthorized access to certain {@link Item} pages requiring administrator rights
*/ */
export class ItemPageCurateGuard extends DsoPageSingleFeatureGuard<Item> { export const itemPageCurateGuard: CanActivateFn =
dsoPageSingleFeatureGuard(
protected resolver: ResolveFn<RemoteData<Item>> = itemPageResolver; () => itemPageResolver,
() => observableOf(FeatureID.AdministratorOf),
constructor(protected authorizationService: AuthorizationDataService, );
protected router: Router,
protected authService: AuthService) {
super(authorizationService, router, authService);
}
/**
* Check administrator authorization rights
*/
getFeatureID(next: ActivatedRouteSnapshot, state: RouterStateSnapshot): Observable<FeatureID> {
return observableOf(FeatureID.AdministratorOf);
}
}

View File

@@ -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<boolean | UrlTree>;
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();
});
});
});

View File

@@ -0,0 +1,16 @@
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 { itemPageResolver } from '../item-page.resolver';
/**
* Guard for preventing unauthorized access to certain {@link Item} pages requiring specific authorizations.
* Checks authorization rights for deleting items.
*/
export const itemPageDeleteGuard: CanActivateFn =
dsoPageSingleFeatureGuard(
() => itemPageResolver,
() => observableOf(FeatureID.CanDelete),
);

View File

@@ -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 { itemPageEditAuthorizationsGuard } from './item-page-edit-authorizations.guard';
describe('itemPageEditAuthorizationsGuard', () => {
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 itemPageEditAuthorizationsGuard(route, { url: 'current-url' } as any);
}) as Observable<boolean | UrlTree>;
result$.subscribe((result) => {
expect(authorizationService.isAuthorized).toHaveBeenCalledWith(
FeatureID.CanManagePolicies,
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();
});
});
});

View File

@@ -0,0 +1,16 @@
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 { itemPageResolver } from '../item-page.resolver';
/**
* Guard for preventing unauthorized access to certain {@link Item} pages requiring specific authorizations.
* Checks authorization rights for managing policies.
*/
export const itemPageEditAuthorizationsGuard: CanActivateFn =
dsoPageSingleFeatureGuard(
() => itemPageResolver,
() => observableOf(FeatureID.CanManagePolicies),
);

View File

@@ -1,43 +1,16 @@
import { Injectable } from '@angular/core'; import { CanActivateFn } from '@angular/router';
import { import { of as observableOf } from 'rxjs';
ActivatedRouteSnapshot,
ResolveFn,
Router,
RouterStateSnapshot,
} from '@angular/router';
import {
Observable,
of as observableOf,
} from 'rxjs';
import { AuthService } from '../../core/auth/auth.service'; import { dsoPageSingleFeatureGuard } from '../../core/data/feature-authorization/feature-authorization-guard/dso-page-single-feature.guard';
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 { 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'; import { itemPageResolver } from '../item-page.resolver';
@Injectable({
providedIn: 'root',
})
/** /**
* Guard for preventing unauthorized access to certain {@link Item} pages requiring edit metadata rights * Guard for preventing unauthorized access to certain {@link Item} pages requiring edit metadata rights
* Check edit metadata authorization rights
*/ */
export class ItemPageMetadataGuard extends DsoPageSingleFeatureGuard<Item> { export const itemPageMetadataGuard: CanActivateFn =
dsoPageSingleFeatureGuard(
protected resolver: ResolveFn<RemoteData<Item>> = itemPageResolver; () => itemPageResolver,
() => observableOf(FeatureID.CanEditMetadata),
constructor(protected authorizationService: AuthorizationDataService, );
protected router: Router,
protected authService: AuthService) {
super(authorizationService, router, authService);
}
/**
* Check edit metadata authorization rights
*/
getFeatureID(next: ActivatedRouteSnapshot, state: RouterStateSnapshot): Observable<FeatureID> {
return observableOf(FeatureID.CanEditMetadata);
}
}

View File

@@ -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<boolean | UrlTree>;
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();
});
});
});

View File

@@ -0,0 +1,16 @@
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 { itemPageResolver } from '../item-page.resolver';
/**
* Guard for preventing unauthorized access to certain {@link Item} pages requiring specific authorizations.
* Checks authorization rights for moving items.
*/
export const itemPageMoveGuard: CanActivateFn =
dsoPageSingleFeatureGuard(
() => itemPageResolver,
() => observableOf(FeatureID.CanMove),
);

View File

@@ -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<boolean | UrlTree>;
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();
});
});
});

View File

@@ -0,0 +1,16 @@
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 { itemPageResolver } from '../item-page.resolver';
/**
* 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(
() => itemPageResolver,
() => observableOf(FeatureID.CanMakePrivate),
);

View File

@@ -1,43 +1,16 @@
import { Injectable } from '@angular/core'; import { CanActivateFn } from '@angular/router';
import { import { of as observableOf } from 'rxjs';
ActivatedRouteSnapshot,
ResolveFn,
Router,
RouterStateSnapshot,
} from '@angular/router';
import {
Observable,
of as observableOf,
} from 'rxjs';
import { AuthService } from '../../core/auth/auth.service'; import { dsoPageSingleFeatureGuard } from '../../core/data/feature-authorization/feature-authorization-guard/dso-page-single-feature.guard';
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 { 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'; import { itemPageResolver } from '../item-page.resolver';
@Injectable({
providedIn: 'root',
})
/** /**
* Guard for preventing unauthorized access to certain {@link Item} pages requiring DOI registration rights * Guard for preventing unauthorized access to certain {@link Item} pages requiring DOI registration rights
* Check DOI registration authorization rights
*/ */
export class ItemPageRegisterDoiGuard extends DsoPageSingleFeatureGuard<Item> { export const itemPageRegisterDoiGuard: CanActivateFn =
dsoPageSingleFeatureGuard(
protected resolver: ResolveFn<RemoteData<Item>> = itemPageResolver; () => itemPageResolver,
() => observableOf(FeatureID.CanRegisterDOI),
constructor(protected authorizationService: AuthorizationDataService, );
protected router: Router,
protected authService: AuthService) {
super(authorizationService, router, authService);
}
/**
* Check DOI registration authorization rights
*/
getFeatureID(next: ActivatedRouteSnapshot, state: RouterStateSnapshot): Observable<FeatureID> {
return observableOf(FeatureID.CanRegisterDOI);
}
}

View File

@@ -1,43 +1,16 @@
import { Injectable } from '@angular/core'; import { CanActivateFn } from '@angular/router';
import { import { of as observableOf } from 'rxjs';
ActivatedRouteSnapshot,
ResolveFn,
Router,
RouterStateSnapshot,
} from '@angular/router';
import {
Observable,
of as observableOf,
} from 'rxjs';
import { AuthService } from '../../core/auth/auth.service'; import { dsoPageSingleFeatureGuard } from '../../core/data/feature-authorization/feature-authorization-guard/dso-page-single-feature.guard';
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 { 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'; import { itemPageResolver } from '../item-page.resolver';
@Injectable({
providedIn: 'root',
})
/** /**
* Guard for preventing unauthorized access to certain {@link Item} pages requiring reinstate rights * Guard for preventing unauthorized access to certain {@link Item} pages requiring reinstate rights
* Check reinstate authorization rights
*/ */
export class ItemPageReinstateGuard extends DsoPageSingleFeatureGuard<Item> { export const itemPageReinstateGuard: CanActivateFn =
dsoPageSingleFeatureGuard(
protected resolver: ResolveFn<RemoteData<Item>> = itemPageResolver; () => itemPageResolver,
() => observableOf(FeatureID.ReinstateItem),
constructor(protected authorizationService: AuthorizationDataService, );
protected router: Router,
protected authService: AuthService) {
super(authorizationService, router, authService);
}
/**
* Check reinstate authorization rights
*/
getFeatureID(next: ActivatedRouteSnapshot, state: RouterStateSnapshot): Observable<FeatureID> {
return observableOf(FeatureID.ReinstateItem);
}
}

View File

@@ -1,43 +1,16 @@
import { Injectable } from '@angular/core'; import { CanActivateFn } from '@angular/router';
import { import { of as observableOf } from 'rxjs';
ActivatedRouteSnapshot,
ResolveFn,
Router,
RouterStateSnapshot,
} from '@angular/router';
import {
Observable,
of as observableOf,
} from 'rxjs';
import { AuthService } from '../../core/auth/auth.service'; import { dsoPageSingleFeatureGuard } from '../../core/data/feature-authorization/feature-authorization-guard/dso-page-single-feature.guard';
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 { 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'; import { itemPageResolver } from '../item-page.resolver';
@Injectable({
providedIn: 'root',
})
/** /**
* Guard for preventing unauthorized access to certain {@link Item} pages requiring manage relationships rights * Guard for preventing unauthorized access to certain {@link Item} pages requiring manage relationships rights
* Check manage relationships authorization rights
*/ */
export class ItemPageRelationshipsGuard extends DsoPageSingleFeatureGuard<Item> { export const itemPageRelationshipsGuard: CanActivateFn =
dsoPageSingleFeatureGuard(
protected resolver: ResolveFn<RemoteData<Item>> = itemPageResolver; () => itemPageResolver,
() => observableOf(FeatureID.CanManageRelationships),
constructor(protected authorizationService: AuthorizationDataService, );
protected router: Router,
protected authService: AuthService) {
super(authorizationService, router, authService);
}
/**
* Check manage relationships authorization rights
*/
getFeatureID(next: ActivatedRouteSnapshot, state: RouterStateSnapshot): Observable<FeatureID> {
return observableOf(FeatureID.CanManageRelationships);
}
}

View File

@@ -1,44 +1,17 @@
import { Injectable } from '@angular/core'; import { CanActivateFn } from '@angular/router';
import { import { of as observableOf } from 'rxjs';
ActivatedRouteSnapshot,
ResolveFn,
Router,
RouterStateSnapshot,
} from '@angular/router';
import {
Observable,
of as observableOf,
} from 'rxjs';
import { AuthService } from '../../core/auth/auth.service'; import { dsoPageSomeFeatureGuard } from '../../core/data/feature-authorization/feature-authorization-guard/dso-page-some-feature.guard';
import { AuthorizationDataService } from '../../core/data/feature-authorization/authorization-data.service';
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 { 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'; import { itemPageResolver } from '../item-page.resolver';
@Injectable({
providedIn: 'root',
})
/** /**
* Guard for preventing unauthorized access to certain {@link Item} pages requiring any of the rights required for * Guard for preventing unauthorized access to certain {@link Item} pages requiring any of the rights required for
* the status page * the status page
* Check authorization rights
*/ */
export class ItemPageStatusGuard extends DsoPageSomeFeatureGuard<Item> { export const itemPageStatusGuard: CanActivateFn =
dsoPageSomeFeatureGuard(
protected resolver: ResolveFn<RemoteData<Item>> = itemPageResolver; () => itemPageResolver,
() => observableOf([FeatureID.CanManageMappings, FeatureID.WithdrawItem, FeatureID.ReinstateItem, FeatureID.CanManagePolicies, FeatureID.CanMakePrivate, FeatureID.CanDelete, FeatureID.CanMove, FeatureID.CanRegisterDOI]),
constructor(protected authorizationService: AuthorizationDataService, );
protected router: Router,
protected authService: AuthService) {
super(authorizationService, router, authService);
}
/**
* Check authorization rights
*/
getFeatureIDs(next: ActivatedRouteSnapshot, state: RouterStateSnapshot): Observable<FeatureID[]> {
return observableOf([FeatureID.CanManageMappings, FeatureID.WithdrawItem, FeatureID.ReinstateItem, FeatureID.CanManagePolicies, FeatureID.CanMakePrivate, FeatureID.CanDelete, FeatureID.CanMove, FeatureID.CanRegisterDOI]);
}
}

View File

@@ -1,43 +1,16 @@
import { Injectable } from '@angular/core'; import { CanActivateFn } from '@angular/router';
import { import { of as observableOf } from 'rxjs';
ActivatedRouteSnapshot,
ResolveFn,
Router,
RouterStateSnapshot,
} from '@angular/router';
import {
Observable,
of as observableOf,
} from 'rxjs';
import { AuthService } from '../../core/auth/auth.service'; import { dsoPageSingleFeatureGuard } from '../../core/data/feature-authorization/feature-authorization-guard/dso-page-single-feature.guard';
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 { 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'; import { itemPageResolver } from '../item-page.resolver';
@Injectable({
providedIn: 'root',
})
/** /**
* Guard for preventing unauthorized access to certain {@link Item} pages requiring manage versions rights * Guard for preventing unauthorized access to certain {@link Item} pages requiring manage versions rights
* Check manage versions authorization rights
*/ */
export class ItemPageVersionHistoryGuard extends DsoPageSingleFeatureGuard<Item> { export const itemPageVersionHistoryGuard: CanActivateFn =
dsoPageSingleFeatureGuard(
protected resolver: ResolveFn<RemoteData<Item>> = itemPageResolver; () => itemPageResolver,
() => observableOf(FeatureID.CanManageVersions),
constructor(protected authorizationService: AuthorizationDataService, );
protected router: Router,
protected authService: AuthService) {
super(authorizationService, router, authService);
}
/**
* Check manage versions authorization rights
*/
getFeatureID(next: ActivatedRouteSnapshot, state: RouterStateSnapshot): Observable<FeatureID> {
return observableOf(FeatureID.CanManageVersions);
}
}

View File

@@ -1,43 +1,16 @@
import { Injectable } from '@angular/core'; import { CanActivateFn } from '@angular/router';
import { import { of as observableOf } from 'rxjs';
ActivatedRouteSnapshot,
ResolveFn,
Router,
RouterStateSnapshot,
} from '@angular/router';
import {
Observable,
of as observableOf,
} from 'rxjs';
import { AuthService } from '../../core/auth/auth.service'; import { dsoPageSingleFeatureGuard } from '../../core/data/feature-authorization/feature-authorization-guard/dso-page-single-feature.guard';
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 { 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'; import { itemPageResolver } from '../item-page.resolver';
@Injectable({
providedIn: 'root',
})
/** /**
* Guard for preventing unauthorized access to certain {@link Item} pages requiring withdraw rights * Guard for preventing unauthorized access to certain {@link Item} pages requiring withdraw rights
* Check withdraw authorization rights
*/ */
export class ItemPageWithdrawGuard extends DsoPageSingleFeatureGuard<Item> { export const itemPageWithdrawGuard: CanActivateFn =
dsoPageSingleFeatureGuard(
protected resolver: ResolveFn<RemoteData<Item>> = itemPageResolver; () => itemPageResolver,
() => observableOf(FeatureID.WithdrawItem),
constructor(protected authorizationService: AuthorizationDataService, );
protected router: Router,
protected authService: AuthService) {
super(authorizationService, router, authService);
}
/**
* Check withdraw authorization rights
*/
getFeatureID(next: ActivatedRouteSnapshot, state: RouterStateSnapshot): Observable<FeatureID> {
return observableOf(FeatureID.WithdrawItem);
}
}

View File

@@ -1,43 +1,15 @@
import { Injectable } from '@angular/core'; import { CanActivateFn } from '@angular/router';
import { import { of as observableOf } from 'rxjs';
ActivatedRouteSnapshot,
ResolveFn,
Router,
RouterStateSnapshot,
} from '@angular/router';
import {
Observable,
of as observableOf,
} from 'rxjs';
import { AuthService } from '../core/auth/auth.service'; import { dsoPageSingleFeatureGuard } from '../core/data/feature-authorization/feature-authorization-guard/dso-page-single-feature.guard';
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 { 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'; import { itemPageResolver } from './item-page.resolver';
@Injectable({
providedIn: 'root',
})
/** /**
* Guard for preventing unauthorized access to certain {@link Item} pages requiring administrator rights * Guard for preventing unauthorized access to certain {@link Item} pages requiring administrator rights
*/ */
export class ItemPageAdministratorGuard extends DsoPageSingleFeatureGuard<Item> { export const itemPageAdministratorGuard: CanActivateFn =
dsoPageSingleFeatureGuard(
protected resolver: ResolveFn<RemoteData<Item>> = itemPageResolver; () => itemPageResolver,
() => observableOf(FeatureID.AdministratorOf),
constructor(protected authorizationService: AuthorizationDataService, );
protected router: Router,
protected authService: AuthService) {
super(authorizationService, router, authService);
}
/**
* Check administrator authorization rights
*/
getFeatureID(next: ActivatedRouteSnapshot, state: RouterStateSnapshot): Observable<FeatureID> {
return observableOf(FeatureID.AdministratorOf);
}
}

View File

@@ -1,7 +1,4 @@
import { import { Route } from '@angular/router';
mapToCanActivate,
Route,
} from '@angular/router';
import { REQUEST_COPY_MODULE_PATH } from '../app-routing-paths'; import { REQUEST_COPY_MODULE_PATH } from '../app-routing-paths';
import { authenticatedGuard } from '../core/auth/authenticated.guard'; import { authenticatedGuard } from '../core/auth/authenticated.guard';
@@ -19,7 +16,7 @@ import {
UPLOAD_BITSTREAM_PATH, UPLOAD_BITSTREAM_PATH,
} from './item-page-routing-paths'; } from './item-page-routing-paths';
import { OrcidPageComponent } from './orcid-page/orcid-page.component'; 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 { ThemedItemPageComponent } from './simple/themed-item-page.component';
import { versionResolver } from './version-page/version.resolver'; import { versionResolver } from './version-page/version.resolver';
import { VersionPageComponent } from './version-page/version-page/version-page.component'; import { VersionPageComponent } from './version-page/version-page/version-page.component';
@@ -60,7 +57,7 @@ export const ROUTES: Route[] = [
{ {
path: ORCID_PATH, path: ORCID_PATH,
component: OrcidPageComponent, component: OrcidPageComponent,
canActivate: [authenticatedGuard, ...mapToCanActivate([OrcidPageGuard])], canActivate: [authenticatedGuard, orcidPageGuard],
}, },
], ],
data: { data: {

View File

@@ -1,43 +1,16 @@
import { Injectable } from '@angular/core'; import { CanActivateFn } from '@angular/router';
import { import { of as observableOf } from 'rxjs';
ActivatedRouteSnapshot,
ResolveFn,
Router,
RouterStateSnapshot,
} from '@angular/router';
import {
Observable,
of as observableOf,
} from 'rxjs';
import { AuthService } from '../../core/auth/auth.service'; import { dsoPageSingleFeatureGuard } from '../../core/data/feature-authorization/feature-authorization-guard/dso-page-single-feature.guard';
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 { 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'; import { itemPageResolver } from '../item-page.resolver';
@Injectable({
providedIn: 'root',
})
/** /**
* Guard for preventing unauthorized access to certain {@link Item} pages requiring administrator rights * Guard for preventing unauthorized access to certain {@link Item} pages requiring administrator rights
* Check administrator authorization rights
*/ */
export class OrcidPageGuard extends DsoPageSingleFeatureGuard<Item> { export const orcidPageGuard: CanActivateFn =
dsoPageSingleFeatureGuard(
protected resolver: ResolveFn<RemoteData<Item>> = itemPageResolver; () => itemPageResolver,
() => observableOf(FeatureID.CanSynchronizeWithORCID),
constructor(protected authorizationService: AuthorizationDataService, );
protected router: Router,
protected authService: AuthService) {
super(authorizationService, router, authService);
}
/**
* Check administrator authorization rights
*/
getFeatureID(next: ActivatedRouteSnapshot, state: RouterStateSnapshot): Observable<FeatureID> {
return observableOf(FeatureID.CanSynchronizeWithORCID);
}
}

View File

@@ -1,9 +1,6 @@
import { import { Route } from '@angular/router';
mapToCanActivate,
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 { ThemedCreateProfileComponent } from './create-profile/themed-create-profile.component';
import { ThemedRegisterEmailComponent } from './register-email/themed-register-email.component'; import { ThemedRegisterEmailComponent } from './register-email/themed-register-email.component';
import { registrationGuard } from './registration.guard'; import { registrationGuard } from './registration.guard';
@@ -20,7 +17,7 @@ export const ROUTES: Route[] = [
component: ThemedCreateProfileComponent, component: ThemedCreateProfileComponent,
canActivate: [ canActivate: [
registrationGuard, registrationGuard,
...mapToCanActivate([EndUserAgreementCookieGuard]), endUserAgreementCookieGuard,
], ],
}, },
]; ];

View File

@@ -1,12 +1,9 @@
import { import { Route } from '@angular/router';
mapToCanActivate,
Route,
} from '@angular/router';
import { collectionPageResolver } from '../collection-page/collection-page.resolver'; import { collectionPageResolver } from '../collection-page/collection-page.resolver';
import { communityPageResolver } from '../community-page/community-page.resolver'; import { communityPageResolver } from '../community-page/community-page.resolver';
import { i18nBreadcrumbResolver } from '../core/breadcrumbs/i18n-breadcrumb.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 { itemResolver } from '../item-page/item.resolver';
import { ThemedCollectionStatisticsPageComponent } from './collection-statistics-page/themed-collection-statistics-page.component'; import { ThemedCollectionStatisticsPageComponent } from './collection-statistics-page/themed-collection-statistics-page.component';
import { ThemedCommunityStatisticsPageComponent } from './community-statistics-page/themed-community-statistics-page.component'; import { ThemedCommunityStatisticsPageComponent } from './community-statistics-page/themed-community-statistics-page.component';
@@ -29,7 +26,7 @@ export const ROUTES: Route[] = [
component: ThemedSiteStatisticsPageComponent, component: ThemedSiteStatisticsPageComponent,
}, },
], ],
canActivate: mapToCanActivate([StatisticsAdministratorGuard]), canActivate: [statisticsAdministratorGuard],
}, },
{ {
path: `items/:id`, path: `items/:id`,
@@ -42,7 +39,7 @@ export const ROUTES: Route[] = [
breadcrumbKey: 'statistics', breadcrumbKey: 'statistics',
}, },
component: ThemedItemStatisticsPageComponent, component: ThemedItemStatisticsPageComponent,
canActivate: mapToCanActivate([StatisticsAdministratorGuard]), canActivate: [statisticsAdministratorGuard],
}, },
{ {
path: `collections/:id`, path: `collections/:id`,
@@ -55,7 +52,7 @@ export const ROUTES: Route[] = [
breadcrumbKey: 'statistics', breadcrumbKey: 'statistics',
}, },
component: ThemedCollectionStatisticsPageComponent, component: ThemedCollectionStatisticsPageComponent,
canActivate: mapToCanActivate([StatisticsAdministratorGuard]), canActivate: [statisticsAdministratorGuard],
}, },
{ {
path: `communities/:id`, path: `communities/:id`,
@@ -68,6 +65,6 @@ export const ROUTES: Route[] = [
breadcrumbKey: 'statistics', breadcrumbKey: 'statistics',
}, },
component: ThemedCommunityStatisticsPageComponent, component: ThemedCommunityStatisticsPageComponent,
canActivate: mapToCanActivate([StatisticsAdministratorGuard]), canActivate: [statisticsAdministratorGuard],
}, },
]; ];

View File

@@ -1,15 +1,12 @@
import { import { Route } from '@angular/router';
mapToCanActivate,
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'; import { SystemWideAlertFormComponent } from './alert-form/system-wide-alert-form.component';
export const ROUTES: Route[] = [ export const ROUTES: Route[] = [
{ {
path: '', path: '',
canActivate: mapToCanActivate([SiteAdministratorGuard]), canActivate: [siteAdministratorGuard],
component: SystemWideAlertFormComponent, component: SystemWideAlertFormComponent,
}, },