Skip to content

Commit 8253a7d

Browse files
committed
Merged dspace-cris-2023_02_x into task/dspace-cris-2023_02_x/DSC-1917
2 parents b537a60 + ae3b249 commit 8253a7d

22 files changed

Lines changed: 224 additions & 65 deletions

File tree

src/app/admin/admin-routing.module.ts

Lines changed: 24 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,12 @@ import { AdminEditUserAgreementComponent } from './admin-edit-user-agreement/adm
1010
import { NOTIFICATIONS_MODULE_PATH, REGISTRIES_MODULE_PATH } from './admin-routing-paths';
1111
import { EditCmsMetadataComponent } from './edit-cms-metadata/edit-cms-metadata.component';
1212
import { BatchImportPageComponent } from './admin-import-batch-page/batch-import-page.component';
13+
import {
14+
SiteAdministratorGuard
15+
} from '../core/data/feature-authorization/feature-authorization-guard/site-administrator.guard';
16+
import {
17+
GenericAdministratorGuard
18+
} from '../core/data/feature-authorization/feature-authorization-guard/generic-administrator-guard';
1319

1420
@NgModule({
1521
imports: [
@@ -18,59 +24,69 @@ import { BatchImportPageComponent } from './admin-import-batch-page/batch-import
1824
path: NOTIFICATIONS_MODULE_PATH,
1925
loadChildren: () => import('./admin-notifications/admin-notifications.module')
2026
.then((m) => m.AdminNotificationsModule),
27+
canActivate: [SiteAdministratorGuard]
2128
},
2229
{
2330
path: REGISTRIES_MODULE_PATH,
2431
loadChildren: () => import('./admin-registries/admin-registries.module')
2532
.then((m) => m.AdminRegistriesModule),
33+
canActivate: [SiteAdministratorGuard]
2634
},
2735
{
2836
path: 'search',
2937
resolve: { breadcrumb: I18nBreadcrumbResolver },
3038
component: AdminSearchPageComponent,
31-
data: { title: 'admin.search.title', breadcrumbKey: 'admin.search' }
39+
data: { title: 'admin.search.title', breadcrumbKey: 'admin.search' },
40+
canActivate: [GenericAdministratorGuard]
3241
},
3342
{
3443
path: 'workflow',
3544
resolve: { breadcrumb: I18nBreadcrumbResolver },
3645
component: AdminWorkflowPageComponent,
37-
data: { title: 'admin.workflow.title', breadcrumbKey: 'admin.workflow' }
46+
data: { title: 'admin.workflow.title', breadcrumbKey: 'admin.workflow' },
47+
canActivate: [SiteAdministratorGuard]
3848
},
3949
{
4050
path: 'curation-tasks',
4151
resolve: { breadcrumb: I18nBreadcrumbResolver },
4252
component: AdminCurationTasksComponent,
43-
data: { title: 'admin.curation-tasks.title', breadcrumbKey: 'admin.curation-tasks' }
53+
data: { title: 'admin.curation-tasks.title', breadcrumbKey: 'admin.curation-tasks' },
54+
canActivate: [SiteAdministratorGuard]
4455
},
4556
{
4657
path: 'metadata-import',
4758
resolve: { breadcrumb: I18nBreadcrumbResolver },
4859
component: MetadataImportPageComponent,
49-
data: { title: 'admin.metadata-import.title', breadcrumbKey: 'admin.metadata-import' }
60+
data: { title: 'admin.metadata-import.title', breadcrumbKey: 'admin.metadata-import' },
61+
canActivate: [GenericAdministratorGuard]
5062
},
5163
{
5264
path: 'edit-user-agreement',
5365
resolve: { breadcrumb: I18nBreadcrumbResolver },
5466
component: AdminEditUserAgreementComponent,
55-
data: { title: 'admin.edit-user-agreement.title', breadcrumbKey: 'admin.edit-user-agreement' }
67+
data: { title: 'admin.edit-user-agreement.title', breadcrumbKey: 'admin.edit-user-agreement' },
68+
canActivate: [SiteAdministratorGuard]
5669
},
5770
{
5871
path: 'edit-cms-metadata',
5972
resolve: { breadcrumb: I18nBreadcrumbResolver },
6073
component: EditCmsMetadataComponent,
61-
data: { title: 'admin.edit-cms-metadata.title', breadcrumbKey: 'admin.edit-cms-metadata' }
74+
data: { title: 'admin.edit-cms-metadata.title', breadcrumbKey: 'admin.edit-cms-metadata' },
75+
canActivate: [SiteAdministratorGuard]
6276
},
6377
{
6478
path: 'batch-import',
6579
resolve: { breadcrumb: I18nBreadcrumbResolver },
6680
component: BatchImportPageComponent,
67-
data: { title: 'admin.batch-import.title', breadcrumbKey: 'admin.batch-import' }
81+
data: { title: 'admin.batch-import.title', breadcrumbKey: 'admin.batch-import' },
82+
canActivate: [GenericAdministratorGuard]
6883
},
6984
{
7085
path: 'system-wide-alert',
7186
resolve: { breadcrumb: I18nBreadcrumbResolver },
7287
loadChildren: () => import('../system-wide-alert/system-wide-alert.module').then((m) => m.SystemWideAlertModule),
73-
data: {title: 'admin.system-wide-alert.title', breadcrumbKey: 'admin.system-wide-alert'}
88+
data: {title: 'admin.system-wide-alert.title', breadcrumbKey: 'admin.system-wide-alert'},
89+
canActivate: [SiteAdministratorGuard]
7490
},
7591
])
7692
],

src/app/admin/edit-cms-metadata/edit-cms-metadata.component.ts

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -83,6 +83,9 @@ export class EditCmsMetadataComponent implements OnInit {
8383
this.notificationsService.error(this.translateService.get('admin.edit-cms-metadata.error'));
8484
}
8585
this.siteService.setStale();
86+
this.siteService.find().subscribe((site) => {
87+
this.site = site;
88+
});
8689
});
8790
}
8891

src/app/app-routing.module.ts

Lines changed: 8 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -3,9 +3,8 @@ import { NoPreloading, RouterModule } from '@angular/router';
33
import { AuthBlockingGuard } from './core/auth/auth-blocking.guard';
44

55
import { AuthenticatedGuard } from './core/auth/authenticated.guard';
6-
import {
7-
SiteAdministratorGuard
8-
} from './core/data/feature-authorization/feature-authorization-guard/site-administrator.guard';
6+
7+
98
import {
109
ACCESS_CONTROL_MODULE_PATH,
1110
ADMIN_MODULE_PATH,
@@ -46,6 +45,11 @@ import { ThemedPageErrorComponent } from './page-error/themed-page-error.compone
4645
import { ForgotPasswordCheckGuard } from './core/rest-property/forgot-password-check-guard.guard';
4746
import { SUGGESTION_MODULE_PATH } from './suggestions-page/suggestions-page-routing-paths';
4847
import { RedirectService } from './redirect/redirect.service';
48+
import {
49+
GenericAdministratorGuard
50+
} from './core/data/feature-authorization/feature-authorization-guard/generic-administrator-guard';
51+
52+
4953

5054
@NgModule({
5155
imports: [
@@ -168,7 +172,7 @@ import { RedirectService } from './redirect/redirect.service';
168172
path: ADMIN_MODULE_PATH,
169173
loadChildren: () => import('./admin/admin.module')
170174
.then((m) => m.AdminModule),
171-
canActivate: [SiteAdministratorGuard, EndUserAgreementCurrentUserGuard]
175+
canActivate: [GenericAdministratorGuard, EndUserAgreementCurrentUserGuard]
172176
},
173177
{
174178
path: 'login',

src/app/collection-page/edit-item-template-page/edit-item-template-page.component.html

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,10 @@
44
<ng-container *ngIf="itemRD?.hasSucceeded">
55
<h2 class="border-bottom">{{ 'collection.edit.template.head' | translate:{ collection: dsoNameService.getName(collection) } }}</h2>
66
<ds-themed-dso-edit-metadata [updateDataService]="itemTemplateService" [dso]="itemRD?.payload"></ds-themed-dso-edit-metadata>
7-
<button [routerLink]="getCollectionEditUrl(collection)" class="btn btn-outline-secondary">{{ 'collection.edit.template.cancel' | translate }}</button>
7+
<button [routerLink]="getCollectionEditUrl(collection)" class="btn btn-outline-secondary">
8+
<i class="fas fa-arrow-left"></i>
9+
{{ 'collection.edit.template.back' | translate }}
10+
</button>
811
</ng-container>
912
<ds-themed-loading *ngIf="itemRD?.isLoading" [message]="'collection.edit.template.loading' | translate"></ds-themed-loading>
1013
<ds-alert *ngIf="itemRD?.hasFailed" [type]="AlertTypeEnum.Error" [content]="'collection.edit.template.error' | translate"></ds-alert>
Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
import { Injectable } from '@angular/core';
2+
import { FeatureID } from '../feature-id';
3+
import { AuthorizationDataService } from '../authorization-data.service';
4+
import { ActivatedRouteSnapshot, Router, RouterStateSnapshot } from '@angular/router';
5+
import { Observable, of as observableOf } from 'rxjs';
6+
import { AuthService } from '../../../auth/auth.service';
7+
import { SomeFeatureAuthorizationGuard } from './some-feature-authorization.guard';
8+
9+
/**
10+
* Prevent unauthorized activating and loading of routes when the current authenticated user doesn't have administrator
11+
* rights to the {@link Site}, Community or Collection
12+
*/
13+
@Injectable({
14+
providedIn: 'root'
15+
})
16+
export class GenericAdministratorGuard extends SomeFeatureAuthorizationGuard {
17+
constructor(protected authorizationService: AuthorizationDataService, protected router: Router, protected authService: AuthService) {
18+
super(authorizationService, router, authService);
19+
}
20+
21+
/**
22+
* Check if user have administrator rights to Site, Community or Collection
23+
*/
24+
getFeatureIDs(route: ActivatedRouteSnapshot, state: RouterStateSnapshot): Observable<FeatureID[]> {
25+
return observableOf([
26+
FeatureID.AdministratorOf,
27+
FeatureID.IsCommunityAdmin,
28+
FeatureID.IsCollectionAdmin,
29+
]);
30+
}
31+
}

src/app/dso-shared/dso-edit-metadata/dso-edit-metadata-headers/dso-edit-metadata-headers.component.html

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@
66
<div class="ds-flex-cell ds-lang-cell"><b>{{ dsoType + '.edit.metadata.headers.language' | translate }}</b></div>
77
<div class="ds-flex-cell ds-authority-cell"><b>{{ dsoType + '.edit.metadata.headers.authority' | translate }}</b></div>
88
<div class="ds-flex-cell ds-security-cell"><b>{{'item.edit.metadata.headers.security'| translate}}</b></div>
9-
<div class="text-center ds-flex-cell ds-edit-cell"><b>{{ dsoType + '.edit.metadata.headers.edit' | translate }}</b></div>
9+
<div class="ds-flex-cell ds-edit-cell"><b>{{ dsoType + '.edit.metadata.headers.edit' | translate }}</b></div>
1010
</div>
1111
</div>
1212
</div>

src/app/item-page/alerts/item-alerts.component.html

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
<div>
1+
<div class="pb-3">
22
<div *ngIf="item && !item.isDiscoverable" class="private-warning">
33
<ds-alert [type]="AlertTypeEnum.Warning" [content]="'item.alerts.private' | translate"></ds-alert>
44
</div>
@@ -8,7 +8,7 @@
88
<span class="align-self-center">{{'item.alerts.withdrawn' | translate}}</span>
99
</div>
1010
</ds-alert>
11-
<p class="text-center">
11+
<p class="text-center" *ngIf="!(isAdministrator$ | async)">
1212
<a routerLink="/home" class="btn btn-primary">{{"410.link.home-page" | translate}}</a>
1313
</p>
1414
</div>

src/app/item-page/alerts/item-alerts.component.spec.ts

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,8 @@ import { TranslateModule } from '@ngx-translate/core';
44
import { NO_ERRORS_SCHEMA } from '@angular/core';
55
import { Item } from '../../core/shared/item.model';
66
import { By } from '@angular/platform-browser';
7+
import {AuthorizationDataService} from '../../core/data/feature-authorization/authorization-data.service';
8+
import {AuthorizationDataServiceStub} from '../../shared/testing/authorization-service.stub';
79

810
describe('ItemAlertsComponent', () => {
911
let component: ItemAlertsComponent;
@@ -14,7 +16,10 @@ describe('ItemAlertsComponent', () => {
1416
TestBed.configureTestingModule({
1517
declarations: [ItemAlertsComponent],
1618
imports: [TranslateModule.forRoot()],
17-
schemas: [NO_ERRORS_SCHEMA]
19+
schemas: [NO_ERRORS_SCHEMA],
20+
providers: [
21+
{ provide: AuthorizationDataService, useClass: AuthorizationDataServiceStub }
22+
]
1823
})
1924
.compileComponents();
2025
}));

src/app/item-page/alerts/item-alerts.component.ts

Lines changed: 14 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,9 @@
1-
import { Component, Input } from '@angular/core';
1+
import {Component, Input, OnInit} from '@angular/core';
22
import { Item } from '../../core/shared/item.model';
33
import { AlertType } from '../../shared/alert/alert-type';
4+
import {Observable} from 'rxjs';
5+
import {FeatureID} from '../../core/data/feature-authorization/feature-id';
6+
import {AuthorizationDataService} from '../../core/data/feature-authorization/authorization-data.service';
47

58
@Component({
69
selector: 'ds-item-alerts',
@@ -10,7 +13,7 @@ import { AlertType } from '../../shared/alert/alert-type';
1013
/**
1114
* Component displaying alerts for an item
1215
*/
13-
export class ItemAlertsComponent {
16+
export class ItemAlertsComponent implements OnInit {
1417
/**
1518
* The Item to display alerts for
1619
*/
@@ -21,4 +24,13 @@ export class ItemAlertsComponent {
2124
* @type {AlertType}
2225
*/
2326
public AlertTypeEnum = AlertType;
27+
28+
isAdministrator$: Observable<boolean>;
29+
30+
constructor(private authorizationService: AuthorizationDataService) {
31+
}
32+
33+
ngOnInit() {
34+
this.isAdministrator$ = this.authorizationService.isAuthorized(FeatureID.AdministratorOf);
35+
}
2436
}

src/app/menu.resolver.ts

Lines changed: 39 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
1-
import { Injectable } from '@angular/core';
1+
import { Inject, Injectable, PLATFORM_ID } from '@angular/core';
22
import { ActivatedRoute, ActivatedRouteSnapshot, Resolve, RouterStateSnapshot } from '@angular/router';
3-
import { combineLatest as observableCombineLatest, Observable } from 'rxjs';
3+
import { combineLatest as observableCombineLatest, Observable, of } from 'rxjs';
44
import { MenuID } from './shared/menu/menu-id.model';
55
import { MenuState } from './shared/menu/menu-state.model';
66
import { MenuItemType } from './shared/menu/menu-item-type.model';
@@ -52,11 +52,12 @@ import { environment } from '../environments/environment';
5252
import { SectionDataService } from './core/layout/section-data.service';
5353
import { Section } from './core/layout/models/section.model';
5454
import { NOTIFICATIONS_RECITER_SUGGESTION_PATH } from './admin/admin-notifications/admin-notifications-routing-paths';
55+
import { isPlatformBrowser } from '@angular/common';
5556
import { ConfigurationDataService } from './core/data/configuration-data.service';
5657
import { ConfigurationProperty } from './core/shared/configuration-property.model';
5758

5859
/**
59-
* Creates all of the app's menus
60+
* Creates all the app's menus
6061
*/
6162
@Injectable({
6263
providedIn: 'root'
@@ -66,6 +67,7 @@ export class MenuResolver implements Resolve<boolean> {
6667
private activatedRouteLastChild: ActivatedRoute;
6768

6869
constructor(
70+
@Inject(PLATFORM_ID) public platformId: any,
6971
protected route: ActivatedRoute,
7072
protected menuService: MenuService,
7173
protected authorizationService: AuthorizationDataService,
@@ -80,6 +82,9 @@ export class MenuResolver implements Resolve<boolean> {
8082
* Initialize all menus
8183
*/
8284
resolve(route: ActivatedRouteSnapshot, state: RouterStateSnapshot): Observable<boolean> {
85+
if (!isPlatformBrowser(this.platformId)) {
86+
return of(true);
87+
}
8388
return observableCombineLatest([
8489
this.createPublicMenu$(),
8590
this.createAdminMenu$(),
@@ -318,7 +323,7 @@ export class MenuResolver implements Resolve<boolean> {
318323
id: 'new_item',
319324
parentID: 'new',
320325
active: false,
321-
visible: isSiteAdmin,
326+
visible: isSiteAdmin || isCommunityAdmin || isCollectionAdmin,
322327
model: {
323328
type: MenuItemType.ONCLICK,
324329
text: 'menu.section.new_item',
@@ -451,12 +456,24 @@ export class MenuResolver implements Resolve<boolean> {
451456
// icon: 'cogs',
452457
// index: 9
453458
// },
454-
459+
/* Admin Search */
460+
{
461+
id: 'admin_search',
462+
active: false,
463+
visible: isSiteAdmin || isCollectionAdmin || isCommunityAdmin,
464+
model: {
465+
type: MenuItemType.LINK,
466+
text: 'menu.section.admin_search',
467+
link: '/admin/search'
468+
} as LinkMenuItemModel,
469+
index: 5,
470+
icon: 'search',
471+
},
455472
/* Processes */
456473
{
457474
id: 'processes',
458475
active: false,
459-
visible: isSiteAdmin,
476+
visible: isSiteAdmin || isCommunityAdmin || isCollectionAdmin,
460477
model: {
461478
type: MenuItemType.LINK,
462479
text: 'menu.section.processes',
@@ -532,8 +549,14 @@ export class MenuResolver implements Resolve<boolean> {
532549
];
533550
menuList.forEach((menuSection) => this.menuService.addSection(MenuID.ADMIN, menuSection));
534551

535-
this.authorizationService.isAuthorized(FeatureID.AdministratorOf).pipe(
536-
filter((authorized: boolean) => authorized),
552+
observableCombineLatest([
553+
this.authorizationService.isAuthorized(FeatureID.AdministratorOf),
554+
this.authorizationService.isAuthorized(FeatureID.IsCommunityAdmin),
555+
this.authorizationService.isAuthorized(FeatureID.IsCollectionAdmin),
556+
]).pipe(
557+
filter(([isAdmin, isCommunityAdmin, isCollectionAdmin]) =>
558+
isAdmin || isCollectionAdmin || isCommunityAdmin
559+
),
537560
take(1),
538561
switchMap(() => this.scriptDataService.scriptWithNameExistsAndCanExecute(METADATA_EXPORT_SCRIPT_NAME)),
539562
filter((metadataExportScriptExists: boolean) => metadataExportScriptExists),
@@ -659,8 +682,14 @@ export class MenuResolver implements Resolve<boolean> {
659682
const menuList = [];
660683
menuList.forEach((menuSection) => this.menuService.addSection(MenuID.ADMIN, menuSection));
661684

662-
this.authorizationService.isAuthorized(FeatureID.AdministratorOf).pipe(
663-
filter((authorized: boolean) => authorized),
685+
observableCombineLatest([
686+
this.authorizationService.isAuthorized(FeatureID.AdministratorOf),
687+
this.authorizationService.isAuthorized(FeatureID.IsCommunityAdmin),
688+
this.authorizationService.isAuthorized(FeatureID.IsCollectionAdmin),
689+
]).pipe(
690+
filter(([isAdmin, isCommunityAdmin, isCollectionAdmin]) =>
691+
isAdmin || isCollectionAdmin || isCommunityAdmin
692+
),
664693
take(1),
665694
switchMap(() => this.scriptDataService.scriptWithNameExistsAndCanExecute(METADATA_IMPORT_SCRIPT_NAME)),
666695
filter((metadataImportScriptExists: boolean) => metadataImportScriptExists),
@@ -761,19 +790,6 @@ export class MenuResolver implements Resolve<boolean> {
761790
link: '/admin/notifications/' + NOTIFICATIONS_RECITER_SUGGESTION_PATH
762791
} as LinkMenuItemModel,
763792
},
764-
/* Admin Search */
765-
{
766-
id: 'admin_search',
767-
active: false,
768-
visible: authorized,
769-
model: {
770-
type: MenuItemType.LINK,
771-
text: 'menu.section.admin_search',
772-
link: '/admin/search'
773-
} as LinkMenuItemModel,
774-
icon: 'search',
775-
index: 5
776-
},
777793
/* Registries */
778794
{
779795
id: 'registries',

0 commit comments

Comments
 (0)