diff --git a/src/app/+admin/admin-sidebar/admin-sidebar.component.ts b/src/app/+admin/admin-sidebar/admin-sidebar.component.ts
index 9d7b35eb68..2748254dbb 100644
--- a/src/app/+admin/admin-sidebar/admin-sidebar.component.ts
+++ b/src/app/+admin/admin-sidebar/admin-sidebar.component.ts
@@ -1,6 +1,6 @@
import { Component, Injector, OnInit } from '@angular/core';
import { NgbModal } from '@ng-bootstrap/ng-bootstrap';
-import { combineLatest as observableCombineLatest, Observable } from 'rxjs';
+import { combineLatest, combineLatest as observableCombineLatest, Observable } from 'rxjs';
import { first, map, take } from 'rxjs/operators';
import { AuthService } from '../../core/auth/auth.service';
import { ScriptDataService } from '../../core/data/processes/script-data.service';
@@ -103,192 +103,197 @@ export class AdminSidebarComponent extends MenuComponent implements OnInit {
* Initialize all menu sections and items for this menu
*/
createMenu() {
- const menuList = [
- /* News */
- {
- id: 'new',
- active: false,
- visible: true,
- model: {
- type: MenuItemType.TEXT,
- text: 'menu.section.new'
- } as TextMenuItemModel,
- icon: 'plus-circle',
- index: 0
- },
- {
- id: 'new_community',
- parentID: 'new',
- active: false,
- visible: true,
- model: {
- type: MenuItemType.ONCLICK,
- text: 'menu.section.new_community',
- function: () => {
- this.modalService.open(CreateCommunityParentSelectorComponent);
- }
- } as OnClickMenuItemModel,
- },
- {
- id: 'new_collection',
- parentID: 'new',
- active: false,
- visible: true,
- model: {
- type: MenuItemType.ONCLICK,
- text: 'menu.section.new_collection',
- function: () => {
- this.modalService.open(CreateCollectionParentSelectorComponent);
- }
- } as OnClickMenuItemModel,
- },
- {
- id: 'new_item',
- parentID: 'new',
- active: false,
- visible: true,
- model: {
- type: MenuItemType.ONCLICK,
- text: 'menu.section.new_item',
- function: () => {
- this.modalService.open(CreateItemParentSelectorComponent);
- }
- } as OnClickMenuItemModel,
- },
- {
- id: 'new_process',
- parentID: 'new',
- active: false,
- visible: true,
- model: {
- type: MenuItemType.LINK,
- text: 'menu.section.new_process',
- link: '/processes/new'
- } as LinkMenuItemModel,
- },
- {
- id: 'new_item_version',
- parentID: 'new',
- active: false,
- visible: true,
- model: {
- type: MenuItemType.LINK,
- text: 'menu.section.new_item_version',
- link: ''
- } as LinkMenuItemModel,
- },
+ combineLatest([
+ this.authorizationService.isAuthorized(FeatureID.IsCollectionAdmin),
+ this.authorizationService.isAuthorized(FeatureID.IsCommunityAdmin),
+ ]).subscribe(([isCollectionAdmin, isCommunityAdmin]) => {
+ const menuList = [
+ /* News */
+ {
+ id: 'new',
+ active: false,
+ visible: true,
+ model: {
+ type: MenuItemType.TEXT,
+ text: 'menu.section.new'
+ } as TextMenuItemModel,
+ icon: 'plus-circle',
+ index: 0
+ },
+ {
+ id: 'new_community',
+ parentID: 'new',
+ active: false,
+ visible: true,
+ model: {
+ type: MenuItemType.ONCLICK,
+ text: 'menu.section.new_community',
+ function: () => {
+ this.modalService.open(CreateCommunityParentSelectorComponent);
+ }
+ } as OnClickMenuItemModel,
+ },
+ {
+ id: 'new_collection',
+ parentID: 'new',
+ active: false,
+ visible: true,
+ model: {
+ type: MenuItemType.ONCLICK,
+ text: 'menu.section.new_collection',
+ function: () => {
+ this.modalService.open(CreateCollectionParentSelectorComponent);
+ }
+ } as OnClickMenuItemModel,
+ },
+ {
+ id: 'new_item',
+ parentID: 'new',
+ active: false,
+ visible: true,
+ model: {
+ type: MenuItemType.ONCLICK,
+ text: 'menu.section.new_item',
+ function: () => {
+ this.modalService.open(CreateItemParentSelectorComponent);
+ }
+ } as OnClickMenuItemModel,
+ },
+ {
+ id: 'new_process',
+ parentID: 'new',
+ active: false,
+ visible: true,
+ model: {
+ type: MenuItemType.LINK,
+ text: 'menu.section.new_process',
+ link: '/processes/new'
+ } as LinkMenuItemModel,
+ },
+ {
+ id: 'new_item_version',
+ parentID: 'new',
+ active: false,
+ visible: true,
+ model: {
+ type: MenuItemType.LINK,
+ text: 'menu.section.new_item_version',
+ link: ''
+ } as LinkMenuItemModel,
+ },
- /* Edit */
- {
- id: 'edit',
- active: false,
- visible: true,
- model: {
- type: MenuItemType.TEXT,
- text: 'menu.section.edit'
- } as TextMenuItemModel,
- icon: 'pencil-alt',
- index: 1
- },
- {
- id: 'edit_community',
- parentID: 'edit',
- active: false,
- visible: true,
- model: {
- type: MenuItemType.ONCLICK,
- text: 'menu.section.edit_community',
- function: () => {
- this.modalService.open(EditCommunitySelectorComponent);
- }
- } as OnClickMenuItemModel,
- },
- {
- id: 'edit_collection',
- parentID: 'edit',
- active: false,
- visible: true,
- model: {
- type: MenuItemType.ONCLICK,
- text: 'menu.section.edit_collection',
- function: () => {
- this.modalService.open(EditCollectionSelectorComponent);
- }
- } as OnClickMenuItemModel,
- },
- {
- id: 'edit_item',
- parentID: 'edit',
- active: false,
- visible: true,
- model: {
- type: MenuItemType.ONCLICK,
- text: 'menu.section.edit_item',
- function: () => {
- this.modalService.open(EditItemSelectorComponent);
- }
- } as OnClickMenuItemModel,
- },
+ /* Edit */
+ {
+ id: 'edit',
+ active: false,
+ visible: true,
+ model: {
+ type: MenuItemType.TEXT,
+ text: 'menu.section.edit'
+ } as TextMenuItemModel,
+ icon: 'pencil-alt',
+ index: 1
+ },
+ {
+ id: 'edit_community',
+ parentID: 'edit',
+ active: false,
+ visible: isCommunityAdmin,
+ model: {
+ type: MenuItemType.ONCLICK,
+ text: 'menu.section.edit_community',
+ function: () => {
+ this.modalService.open(EditCommunitySelectorComponent);
+ }
+ } as OnClickMenuItemModel,
+ },
+ {
+ id: 'edit_collection',
+ parentID: 'edit',
+ active: false,
+ visible: isCollectionAdmin,
+ model: {
+ type: MenuItemType.ONCLICK,
+ text: 'menu.section.edit_collection',
+ function: () => {
+ this.modalService.open(EditCollectionSelectorComponent);
+ }
+ } as OnClickMenuItemModel,
+ },
+ {
+ id: 'edit_item',
+ parentID: 'edit',
+ active: false,
+ visible: true,
+ model: {
+ type: MenuItemType.ONCLICK,
+ text: 'menu.section.edit_item',
+ function: () => {
+ this.modalService.open(EditItemSelectorComponent);
+ }
+ } as OnClickMenuItemModel,
+ },
- /* Curation tasks */
- {
- id: 'curation_tasks',
- active: false,
- visible: true,
- model: {
- type: MenuItemType.LINK,
- text: 'menu.section.curation_task',
- link: ''
- } as LinkMenuItemModel,
- icon: 'filter',
- index: 7
- },
+ /* Curation tasks */
+ {
+ id: 'curation_tasks',
+ active: false,
+ visible: true,
+ model: {
+ type: MenuItemType.LINK,
+ text: 'menu.section.curation_task',
+ link: ''
+ } as LinkMenuItemModel,
+ icon: 'filter',
+ index: 7
+ },
- /* Statistics */
- {
- id: 'statistics_task',
- active: false,
- visible: true,
- model: {
- type: MenuItemType.LINK,
- text: 'menu.section.statistics_task',
- link: ''
- } as LinkMenuItemModel,
- icon: 'chart-bar',
- index: 8
- },
+ /* Statistics */
+ {
+ id: 'statistics_task',
+ active: false,
+ visible: true,
+ model: {
+ type: MenuItemType.LINK,
+ text: 'menu.section.statistics_task',
+ link: ''
+ } as LinkMenuItemModel,
+ icon: 'chart-bar',
+ index: 8
+ },
- /* Control Panel */
- {
- id: 'control_panel',
- active: false,
- visible: true,
- model: {
- type: MenuItemType.LINK,
- text: 'menu.section.control_panel',
- link: ''
- } as LinkMenuItemModel,
- icon: 'cogs',
- index: 9
- },
+ /* Control Panel */
+ {
+ id: 'control_panel',
+ active: false,
+ visible: true,
+ model: {
+ type: MenuItemType.LINK,
+ text: 'menu.section.control_panel',
+ link: ''
+ } as LinkMenuItemModel,
+ icon: 'cogs',
+ index: 9
+ },
- /* Processes */
- {
- id: 'processes',
- active: false,
- visible: true,
- model: {
- type: MenuItemType.LINK,
- text: 'menu.section.processes',
- link: '/processes'
- } as LinkMenuItemModel,
- icon: 'terminal',
- index: 10
- },
- ];
- menuList.forEach((menuSection) => this.menuService.addSection(this.menuID, Object.assign(menuSection, {
- shouldPersistOnRouteChange: true
- })));
+ /* Processes */
+ {
+ id: 'processes',
+ active: false,
+ visible: true,
+ model: {
+ type: MenuItemType.LINK,
+ text: 'menu.section.processes',
+ link: '/processes'
+ } as LinkMenuItemModel,
+ icon: 'terminal',
+ index: 10
+ },
+ ];
+ menuList.forEach((menuSection) => this.menuService.addSection(this.menuID, Object.assign(menuSection, {
+ shouldPersistOnRouteChange: true
+ })));
+ })
}
/**
diff --git a/src/app/+collection-page/collection-page.component.html b/src/app/+collection-page/collection-page.component.html
index beb7413415..a9dd99068e 100644
--- a/src/app/+collection-page/collection-page.component.html
+++ b/src/app/+collection-page/collection-page.component.html
@@ -35,7 +35,7 @@
-
+
diff --git a/src/app/+collection-page/collection-page.component.ts b/src/app/+collection-page/collection-page.component.ts
index 8065480604..72e217a7ec 100644
--- a/src/app/+collection-page/collection-page.component.ts
+++ b/src/app/+collection-page/collection-page.component.ts
@@ -22,6 +22,8 @@ import { hasValue, isNotEmpty } from '../shared/empty.util';
import { PaginationComponentOptions } from '../shared/pagination/pagination-component-options.model';
import { AuthService } from '../core/auth/auth.service';
import {PaginationChangeEvent} from '../shared/pagination/paginationChangeEvent.interface';
+import { AuthorizationDataService } from '../core/data/feature-authorization/authorization-data.service';
+import { FeatureID } from '../core/data/feature-authorization/feature-id';
@Component({
selector: 'ds-collection-page',
@@ -44,6 +46,11 @@ export class CollectionPageComponent implements OnInit {
sortConfig: SortOptions
}>;
+ /**
+ * Whether the current user is a Community admin
+ */
+ isCollectionAdmin$: Observable;
+
constructor(
private collectionDataService: CollectionDataService,
private searchService: SearchService,
@@ -51,6 +58,7 @@ export class CollectionPageComponent implements OnInit {
private route: ActivatedRoute,
private router: Router,
private authService: AuthService,
+ private authorizationDataService: AuthorizationDataService,
) {
this.paginationConfig = new PaginationComponentOptions();
this.paginationConfig.id = 'collection-page-pagination';
@@ -70,6 +78,7 @@ export class CollectionPageComponent implements OnInit {
filter((collection: Collection) => hasValue(collection)),
mergeMap((collection: Collection) => collection.logo)
);
+ this.isCollectionAdmin$ = this.authorizationDataService.isAuthorized(FeatureID.IsCollectionAdmin);
this.paginationChanges$ = new BehaviorSubject({
paginationConfig: this.paginationConfig,
diff --git a/src/app/+collection-page/edit-collection-page/edit-collection-page.routing.module.ts b/src/app/+collection-page/edit-collection-page/edit-collection-page.routing.module.ts
index e41f0ebda4..e4dea5e63c 100644
--- a/src/app/+collection-page/edit-collection-page/edit-collection-page.routing.module.ts
+++ b/src/app/+collection-page/edit-collection-page/edit-collection-page.routing.module.ts
@@ -12,6 +12,7 @@ import { ResourcePolicyTargetResolver } from '../../shared/resource-policies/res
import { ResourcePolicyCreateComponent } from '../../shared/resource-policies/create/resource-policy-create.component';
import { ResourcePolicyResolver } from '../../shared/resource-policies/resolvers/resource-policy.resolver';
import { ResourcePolicyEditComponent } from '../../shared/resource-policies/edit/resource-policy-edit.component';
+import { IsCollectionAdminGuard } from '../../access-control/guards/is-collection-admin.guard';
/**
* Routing module that handles the routing for the Edit Collection page administrator functionality
@@ -26,6 +27,7 @@ import { ResourcePolicyEditComponent } from '../../shared/resource-policies/edit
},
data: { breadcrumbKey: 'collection.edit' },
component: EditCollectionPageComponent,
+ canActivate: [IsCollectionAdminGuard],
children: [
{
path: '',
diff --git a/src/app/+community-page/community-page.component.html b/src/app/+community-page/community-page.component.html
index 418e69ed10..c1d88c9ab8 100644
--- a/src/app/+community-page/community-page.component.html
+++ b/src/app/+community-page/community-page.component.html
@@ -21,7 +21,7 @@
-
+
diff --git a/src/app/+community-page/community-page.component.ts b/src/app/+community-page/community-page.component.ts
index e4812e6514..32886b3f01 100644
--- a/src/app/+community-page/community-page.component.ts
+++ b/src/app/+community-page/community-page.component.ts
@@ -15,6 +15,8 @@ import { fadeInOut } from '../shared/animations/fade';
import { hasValue } from '../shared/empty.util';
import { redirectOn4xx } from '../core/shared/operators';
import { AuthService } from '../core/auth/auth.service';
+import { AuthorizationDataService } from '../core/data/feature-authorization/authorization-data.service';
+import { FeatureID } from '../core/data/feature-authorization/feature-id';
@Component({
selector: 'ds-community-page',
@@ -32,6 +34,11 @@ export class CommunityPageComponent implements OnInit {
*/
communityRD$: Observable>;
+ /**
+ * Whether the current user is a Community admin
+ */
+ isCommunityAdmin$: Observable;
+
/**
* The logo of this community
*/
@@ -42,6 +49,7 @@ export class CommunityPageComponent implements OnInit {
private route: ActivatedRoute,
private router: Router,
private authService: AuthService,
+ private authorizationDataService: AuthorizationDataService
) {
}
@@ -54,7 +62,8 @@ export class CommunityPageComponent implements OnInit {
this.logoRD$ = this.communityRD$.pipe(
map((rd: RemoteData) => rd.payload),
filter((community: Community) => hasValue(community)),
- mergeMap((community: Community) => community.logo));
+ mergeMap((community: Community) => community.logo)
+ );
+ this.isCommunityAdmin$ = this.authorizationDataService.isAuthorized(FeatureID.IsCommunityAdmin);
}
-
}
diff --git a/src/app/+community-page/edit-community-page/edit-community-page.routing.module.ts b/src/app/+community-page/edit-community-page/edit-community-page.routing.module.ts
index 440fa01a30..b5aa20f252 100644
--- a/src/app/+community-page/edit-community-page/edit-community-page.routing.module.ts
+++ b/src/app/+community-page/edit-community-page/edit-community-page.routing.module.ts
@@ -10,6 +10,7 @@ import { ResourcePolicyTargetResolver } from '../../shared/resource-policies/res
import { ResourcePolicyCreateComponent } from '../../shared/resource-policies/create/resource-policy-create.component';
import { ResourcePolicyResolver } from '../../shared/resource-policies/resolvers/resource-policy.resolver';
import { ResourcePolicyEditComponent } from '../../shared/resource-policies/edit/resource-policy-edit.component';
+import { IsCommunityAdminGuard } from '../../access-control/guards/is-community-admin.guard';
/**
* Routing module that handles the routing for the Edit Community page administrator functionality
@@ -24,6 +25,7 @@ import { ResourcePolicyEditComponent } from '../../shared/resource-policies/edit
},
data: { breadcrumbKey: 'community.edit' },
component: EditCommunityPageComponent,
+ canActivate: [IsCommunityAdminGuard],
children: [
{
path: '',
diff --git a/src/app/access-control/guards/is-collection-admin.guard.spec.ts b/src/app/access-control/guards/is-collection-admin.guard.spec.ts
new file mode 100644
index 0000000000..9a60980c01
--- /dev/null
+++ b/src/app/access-control/guards/is-collection-admin.guard.spec.ts
@@ -0,0 +1,16 @@
+import { TestBed } from '@angular/core/testing';
+
+import { IsCollectionAdminGuard } from './is-collection-admin.guard';
+
+describe('IsCollectionAdminGuard', () => {
+ let guard: IsCollectionAdminGuard;
+
+ beforeEach(() => {
+ TestBed.configureTestingModule({});
+ guard = TestBed.inject(IsCollectionAdminGuard);
+ });
+
+ it('should be created', () => {
+ expect(guard).toBeTruthy();
+ });
+});
diff --git a/src/app/access-control/guards/is-collection-admin.guard.ts b/src/app/access-control/guards/is-collection-admin.guard.ts
new file mode 100644
index 0000000000..140ea46e68
--- /dev/null
+++ b/src/app/access-control/guards/is-collection-admin.guard.ts
@@ -0,0 +1,20 @@
+import { Injectable } from '@angular/core';
+import { CanActivate, ActivatedRouteSnapshot, RouterStateSnapshot } from '@angular/router';
+import { Observable } from 'rxjs';
+import { FeatureID } from '../../core/data/feature-authorization/feature-id';
+import { AuthorizationDataService } from '../../core/data/feature-authorization/authorization-data.service';
+
+/**
+ * Guard for preventing unauthorized editing of Communities
+ */
+@Injectable({
+ providedIn: 'root'
+})
+export class IsCollectionAdminGuard implements CanActivate {
+ constructor(private authorizationService: AuthorizationDataService) {
+ }
+
+ canActivate(route: ActivatedRouteSnapshot, state: RouterStateSnapshot): Observable {
+ return this.authorizationService.isAuthorized(FeatureID.IsCollectionAdmin);
+ }
+}
diff --git a/src/app/access-control/guards/is-community-admin.guard.spec.ts b/src/app/access-control/guards/is-community-admin.guard.spec.ts
new file mode 100644
index 0000000000..52dc7ec33c
--- /dev/null
+++ b/src/app/access-control/guards/is-community-admin.guard.spec.ts
@@ -0,0 +1,16 @@
+import { TestBed } from '@angular/core/testing';
+
+import { IsCommunityAdminGuard } from './is-community-admin.guard';
+
+describe('IsCommunityAdminGuard', () => {
+ let guard: IsCommunityAdminGuard;
+
+ beforeEach(() => {
+ TestBed.configureTestingModule({});
+ guard = TestBed.inject(IsCommunityAdminGuard);
+ });
+
+ it('should be created', () => {
+ expect(guard).toBeTruthy();
+ });
+});
diff --git a/src/app/access-control/guards/is-community-admin.guard.ts b/src/app/access-control/guards/is-community-admin.guard.ts
new file mode 100644
index 0000000000..fcc4f6520a
--- /dev/null
+++ b/src/app/access-control/guards/is-community-admin.guard.ts
@@ -0,0 +1,20 @@
+import { Injectable } from '@angular/core';
+import { CanActivate, ActivatedRouteSnapshot, RouterStateSnapshot } from '@angular/router';
+import { Observable } from 'rxjs';
+import { FeatureID } from '../../core/data/feature-authorization/feature-id';
+import { AuthorizationDataService } from '../../core/data/feature-authorization/authorization-data.service';
+
+/**
+ * Guard for preventing unauthorized editing of Communities
+ */
+@Injectable({
+ providedIn: 'root'
+})
+export class IsCommunityAdminGuard implements CanActivate {
+ constructor(private authorizationService: AuthorizationDataService) {
+ }
+
+ canActivate(route: ActivatedRouteSnapshot, state: RouterStateSnapshot): Observable {
+ return this.authorizationService.isAuthorized(FeatureID.IsCommunityAdmin);
+ }
+}
diff --git a/src/app/core/data/feature-authorization/feature-id.ts b/src/app/core/data/feature-authorization/feature-id.ts
index 0af2399b7c..e3473a895e 100644
--- a/src/app/core/data/feature-authorization/feature-id.ts
+++ b/src/app/core/data/feature-authorization/feature-id.ts
@@ -10,4 +10,6 @@ export enum FeatureID {
ReinstateItem = 'reinstateItem',
EPersonRegistration = 'epersonRegistration',
CanManageGroups = 'canManageGroups',
+ IsCollectionAdmin = 'isCollectionAdmin',
+ IsCommunityAdmin = 'isCommunityAdmin',
}