diff --git a/src/app/+collection-page/collection-page-administrator.guard.ts b/src/app/+collection-page/collection-page-administrator.guard.ts index 748cca81cb..c7866515b2 100644 --- a/src/app/+collection-page/collection-page-administrator.guard.ts +++ b/src/app/+collection-page/collection-page-administrator.guard.ts @@ -4,7 +4,7 @@ import { Collection } from '../core/shared/collection.model'; import { CollectionPageResolver } from './collection-page.resolver'; import { AuthorizationDataService } from '../core/data/feature-authorization/authorization-data.service'; import { Observable, of as observableOf } from 'rxjs'; -import { DsoPageFeatureGuard } from '../core/data/feature-authorization/feature-authorization-guard/dso-page-feature.guard'; +import { DsoPageSingleFeatureGuard } from '../core/data/feature-authorization/feature-authorization-guard/dso-page-single-feature.guard'; import { FeatureID } from '../core/data/feature-authorization/feature-id'; import { AuthService } from '../core/auth/auth.service'; @@ -14,7 +14,7 @@ import { AuthService } from '../core/auth/auth.service'; /** * Guard for preventing unauthorized access to certain {@link Collection} pages requiring administrator rights */ -export class CollectionPageAdministratorGuard extends DsoPageFeatureGuard { +export class CollectionPageAdministratorGuard extends DsoPageSingleFeatureGuard { constructor(protected resolver: CollectionPageResolver, protected authorizationService: AuthorizationDataService, protected router: Router, diff --git a/src/app/+community-page/community-page-administrator.guard.ts b/src/app/+community-page/community-page-administrator.guard.ts index fad4a78f07..fd7ce5f7bf 100644 --- a/src/app/+community-page/community-page-administrator.guard.ts +++ b/src/app/+community-page/community-page-administrator.guard.ts @@ -4,7 +4,7 @@ import { Community } from '../core/shared/community.model'; import { CommunityPageResolver } from './community-page.resolver'; import { AuthorizationDataService } from '../core/data/feature-authorization/authorization-data.service'; import { Observable, of as observableOf } from 'rxjs'; -import { DsoPageFeatureGuard } from '../core/data/feature-authorization/feature-authorization-guard/dso-page-feature.guard'; +import { DsoPageSingleFeatureGuard } from '../core/data/feature-authorization/feature-authorization-guard/dso-page-single-feature.guard'; import { FeatureID } from '../core/data/feature-authorization/feature-id'; import { AuthService } from '../core/auth/auth.service'; @@ -14,7 +14,7 @@ import { AuthService } from '../core/auth/auth.service'; /** * Guard for preventing unauthorized access to certain {@link Community} pages requiring administrator rights */ -export class CommunityPageAdministratorGuard extends DsoPageFeatureGuard { +export class CommunityPageAdministratorGuard extends DsoPageSingleFeatureGuard { constructor(protected resolver: CommunityPageResolver, protected authorizationService: AuthorizationDataService, protected router: Router, diff --git a/src/app/+item-page/edit-item-page/edit-item-page.routing.module.ts b/src/app/+item-page/edit-item-page/edit-item-page.routing.module.ts index b7d650d8c3..2535e42216 100644 --- a/src/app/+item-page/edit-item-page/edit-item-page.routing.module.ts +++ b/src/app/+item-page/edit-item-page/edit-item-page.routing.module.ts @@ -31,8 +31,13 @@ import { } from './edit-item-page.routing-paths'; import { ItemPageReinstateGuard } from './item-page-reinstate.guard'; import { ItemPageWithdrawGuard } from './item-page-withdraw.guard'; -import { ItemPageEditMetadataGuard } from '../item-page-edit-metadata.guard'; +import { ItemPageMetadataGuard } from './item-page-metadata.guard'; import { ItemPageAdministratorGuard } from '../item-page-administrator.guard'; +import { ItemPageStatusGuard } from './item-page-status.guard'; +import { ItemPageBitstreamsGuard } from './item-page-bitstreams.guard'; +import { ItemPageRelationshipsGuard } from './item-page-relationships.guard'; +import { ItemPageVersionHistoryGuard } from './item-page-version-history.guard'; +import { ItemPageCollectionMapperGuard } from './item-page-collection-mapper.guard'; /** * Routing module that handles the routing for the Edit Item page administrator functionality @@ -60,25 +65,25 @@ import { ItemPageAdministratorGuard } from '../item-page-administrator.guard'; path: 'status', component: ItemStatusComponent, data: { title: 'item.edit.tabs.status.title', showBreadcrumbs: true }, - canActivate: [ItemPageAdministratorGuard] + canActivate: [ItemPageStatusGuard] }, { path: 'bitstreams', component: ItemBitstreamsComponent, data: { title: 'item.edit.tabs.bitstreams.title', showBreadcrumbs: true }, - canActivate: [ItemPageAdministratorGuard] + canActivate: [ItemPageBitstreamsGuard] }, { path: 'metadata', component: ItemMetadataComponent, data: { title: 'item.edit.tabs.metadata.title', showBreadcrumbs: true }, - canActivate: [ItemPageEditMetadataGuard] + canActivate: [ItemPageMetadataGuard] }, { path: 'relationships', component: ItemRelationshipsComponent, data: { title: 'item.edit.tabs.relationships.title', showBreadcrumbs: true }, - canActivate: [ItemPageEditMetadataGuard] + canActivate: [ItemPageRelationshipsGuard] }, /* TODO - uncomment & fix when view page exists { @@ -96,13 +101,13 @@ import { ItemPageAdministratorGuard } from '../item-page-administrator.guard'; path: 'versionhistory', component: ItemVersionHistoryComponent, data: { title: 'item.edit.tabs.versionhistory.title', showBreadcrumbs: true }, - canActivate: [ItemPageAdministratorGuard] + canActivate: [ItemPageVersionHistoryGuard] }, { path: 'mapper', component: ItemCollectionMapperComponent, data: { title: 'item.edit.tabs.item-mapper.title', showBreadcrumbs: true }, - canActivate: [ItemPageAdministratorGuard] + canActivate: [ItemPageCollectionMapperGuard] } ] }, @@ -175,7 +180,12 @@ import { ItemPageAdministratorGuard } from '../item-page-administrator.guard'; ItemPageReinstateGuard, ItemPageWithdrawGuard, ItemPageAdministratorGuard, - ItemPageEditMetadataGuard, + ItemPageMetadataGuard, + ItemPageStatusGuard, + ItemPageBitstreamsGuard, + ItemPageRelationshipsGuard, + ItemPageVersionHistoryGuard, + ItemPageCollectionMapperGuard, ] }) export class EditItemPageRoutingModule { diff --git a/src/app/+item-page/edit-item-page/item-operation/item-operation.component.html b/src/app/+item-page/edit-item-page/item-operation/item-operation.component.html index e3989f02f3..ecbc19aea8 100644 --- a/src/app/+item-page/edit-item-page/item-operation/item-operation.component.html +++ b/src/app/+item-page/edit-item-page/item-operation/item-operation.component.html @@ -3,13 +3,15 @@ {{'item.edit.tabs.status.buttons.' + operation.operationKey + '.label' | translate}} - -
- - {{'item.edit.tabs.status.buttons.' + operation.operationKey + '.button' | translate}} +
+ + + + +
diff --git a/src/app/+item-page/edit-item-page/item-operation/itemOperation.model.ts b/src/app/+item-page/edit-item-page/item-operation/itemOperation.model.ts index 105889d42d..33302dcba6 100644 --- a/src/app/+item-page/edit-item-page/item-operation/itemOperation.model.ts +++ b/src/app/+item-page/edit-item-page/item-operation/itemOperation.model.ts @@ -1,3 +1,5 @@ +import { FeatureID } from '../../../core/data/feature-authorization/feature-id'; + /** * Represents an item operation used on the edit item page with a key, an operation URL to which will be navigated * when performing the action and an option to disable the operation. @@ -7,11 +9,15 @@ export class ItemOperation { operationKey: string; operationUrl: string; disabled: boolean; + authorized: boolean; + featureID: FeatureID; - constructor(operationKey: string, operationUrl: string) { + constructor(operationKey: string, operationUrl: string, featureID?: FeatureID, disabled = false, authorized = true) { this.operationKey = operationKey; this.operationUrl = operationUrl; - this.setDisabled(false); + this.featureID = featureID; + this.authorized = authorized; + this.setDisabled(disabled); } /** diff --git a/src/app/+item-page/edit-item-page/item-page-bitstreams.guard.ts b/src/app/+item-page/edit-item-page/item-page-bitstreams.guard.ts new file mode 100644 index 0000000000..bf4a6dc681 --- /dev/null +++ b/src/app/+item-page/edit-item-page/item-page-bitstreams.guard.ts @@ -0,0 +1,31 @@ +import { Injectable } from '@angular/core'; +import { DsoPageSingleFeatureGuard } from '../../core/data/feature-authorization/feature-authorization-guard/dso-page-single-feature.guard'; +import { Item } from '../../core/shared/item.model'; +import { ItemPageResolver } from '../item-page.resolver'; +import { AuthorizationDataService } from '../../core/data/feature-authorization/authorization-data.service'; +import { ActivatedRouteSnapshot, Router, RouterStateSnapshot } from '@angular/router'; +import { Observable, of as observableOf } from 'rxjs'; +import { FeatureID } from '../../core/data/feature-authorization/feature-id'; +import { AuthService } from '../../core/auth/auth.service'; + +@Injectable({ + providedIn: 'root' +}) +/** + * Guard for preventing unauthorized access to certain {@link Item} pages requiring manage bitstreams rights + */ +export class ItemPageBitstreamsGuard extends DsoPageSingleFeatureGuard { + constructor(protected resolver: ItemPageResolver, + protected authorizationService: AuthorizationDataService, + protected router: Router, + protected authService: AuthService) { + super(resolver, authorizationService, router, authService); + } + + /** + * Check manage bitstreams authorization rights + */ + getFeatureID(next: ActivatedRouteSnapshot, state: RouterStateSnapshot): Observable { + return observableOf(FeatureID.CanManageBitstreams); + } +} diff --git a/src/app/+item-page/edit-item-page/item-page-collection-mapper.guard.ts b/src/app/+item-page/edit-item-page/item-page-collection-mapper.guard.ts new file mode 100644 index 0000000000..2380377aea --- /dev/null +++ b/src/app/+item-page/edit-item-page/item-page-collection-mapper.guard.ts @@ -0,0 +1,31 @@ +import { Injectable } from '@angular/core'; +import { ActivatedRouteSnapshot, Router, RouterStateSnapshot } from '@angular/router'; +import { AuthorizationDataService } from '../../core/data/feature-authorization/authorization-data.service'; +import { ItemPageResolver } from '../item-page.resolver'; +import { Item } from '../../core/shared/item.model'; +import { DsoPageSingleFeatureGuard } from '../../core/data/feature-authorization/feature-authorization-guard/dso-page-single-feature.guard'; +import { Observable, of as observableOf } from 'rxjs'; +import { FeatureID } from '../../core/data/feature-authorization/feature-id'; +import { AuthService } from '../../core/auth/auth.service'; + +@Injectable({ + providedIn: 'root' +}) +/** + * Guard for preventing unauthorized access to certain {@link Item} pages requiring manage mappings rights + */ +export class ItemPageCollectionMapperGuard extends DsoPageSingleFeatureGuard { + constructor(protected resolver: ItemPageResolver, + protected authorizationService: AuthorizationDataService, + protected router: Router, + protected authService: AuthService) { + super(resolver, authorizationService, router, authService); + } + + /** + * Check manage mappings authorization rights + */ + getFeatureID(next: ActivatedRouteSnapshot, state: RouterStateSnapshot): Observable { + return observableOf(FeatureID.CanManageMappings); + } +} diff --git a/src/app/+item-page/item-page-edit-metadata.guard.ts b/src/app/+item-page/edit-item-page/item-page-metadata.guard.ts similarity index 59% rename from src/app/+item-page/item-page-edit-metadata.guard.ts rename to src/app/+item-page/edit-item-page/item-page-metadata.guard.ts index a9b870b1cd..a6846bec4e 100644 --- a/src/app/+item-page/item-page-edit-metadata.guard.ts +++ b/src/app/+item-page/edit-item-page/item-page-metadata.guard.ts @@ -1,12 +1,12 @@ import { Injectable } from '@angular/core'; import { ActivatedRouteSnapshot, Router, RouterStateSnapshot } from '@angular/router'; -import { AuthorizationDataService } from '../core/data/feature-authorization/authorization-data.service'; -import { ItemPageResolver } from './item-page.resolver'; -import { Item } from '../core/shared/item.model'; -import { DsoPageFeatureGuard } from '../core/data/feature-authorization/feature-authorization-guard/dso-page-feature.guard'; +import { AuthorizationDataService } from '../../core/data/feature-authorization/authorization-data.service'; +import { ItemPageResolver } from '../item-page.resolver'; +import { Item } from '../../core/shared/item.model'; +import { DsoPageSingleFeatureGuard } from '../../core/data/feature-authorization/feature-authorization-guard/dso-page-single-feature.guard'; import { Observable, of as observableOf } from 'rxjs'; -import { FeatureID } from '../core/data/feature-authorization/feature-id'; -import { AuthService } from '../core/auth/auth.service'; +import { FeatureID } from '../../core/data/feature-authorization/feature-id'; +import { AuthService } from '../../core/auth/auth.service'; @Injectable({ providedIn: 'root' @@ -14,7 +14,7 @@ import { AuthService } from '../core/auth/auth.service'; /** * Guard for preventing unauthorized access to certain {@link Item} pages requiring edit metadata rights */ -export class ItemPageEditMetadataGuard extends DsoPageFeatureGuard { +export class ItemPageMetadataGuard extends DsoPageSingleFeatureGuard { constructor(protected resolver: ItemPageResolver, protected authorizationService: AuthorizationDataService, protected router: Router, diff --git a/src/app/+item-page/edit-item-page/item-page-reinstate.guard.ts b/src/app/+item-page/edit-item-page/item-page-reinstate.guard.ts index 0288e30b0a..88c9c20b12 100644 --- a/src/app/+item-page/edit-item-page/item-page-reinstate.guard.ts +++ b/src/app/+item-page/edit-item-page/item-page-reinstate.guard.ts @@ -1,5 +1,5 @@ import { Injectable } from '@angular/core'; -import { DsoPageFeatureGuard } from '../../core/data/feature-authorization/feature-authorization-guard/dso-page-feature.guard'; +import { DsoPageSingleFeatureGuard } from '../../core/data/feature-authorization/feature-authorization-guard/dso-page-single-feature.guard'; import { Item } from '../../core/shared/item.model'; import { ItemPageResolver } from '../item-page.resolver'; import { AuthorizationDataService } from '../../core/data/feature-authorization/authorization-data.service'; @@ -14,7 +14,7 @@ import { AuthService } from '../../core/auth/auth.service'; /** * Guard for preventing unauthorized access to certain {@link Item} pages requiring reinstate rights */ -export class ItemPageReinstateGuard extends DsoPageFeatureGuard { +export class ItemPageReinstateGuard extends DsoPageSingleFeatureGuard { constructor(protected resolver: ItemPageResolver, protected authorizationService: AuthorizationDataService, protected router: Router, diff --git a/src/app/+item-page/edit-item-page/item-page-relationships.guard.ts b/src/app/+item-page/edit-item-page/item-page-relationships.guard.ts new file mode 100644 index 0000000000..77da92ae02 --- /dev/null +++ b/src/app/+item-page/edit-item-page/item-page-relationships.guard.ts @@ -0,0 +1,31 @@ +import { Injectable } from '@angular/core'; +import { ActivatedRouteSnapshot, Router, RouterStateSnapshot } from '@angular/router'; +import { AuthorizationDataService } from '../../core/data/feature-authorization/authorization-data.service'; +import { ItemPageResolver } from '../item-page.resolver'; +import { Item } from '../../core/shared/item.model'; +import { DsoPageSingleFeatureGuard } from '../../core/data/feature-authorization/feature-authorization-guard/dso-page-single-feature.guard'; +import { Observable, of as observableOf } from 'rxjs'; +import { FeatureID } from '../../core/data/feature-authorization/feature-id'; +import { AuthService } from '../../core/auth/auth.service'; + +@Injectable({ + providedIn: 'root' +}) +/** + * Guard for preventing unauthorized access to certain {@link Item} pages requiring manage relationships rights + */ +export class ItemPageRelationshipsGuard extends DsoPageSingleFeatureGuard { + constructor(protected resolver: ItemPageResolver, + protected authorizationService: AuthorizationDataService, + protected router: Router, + protected authService: AuthService) { + super(resolver, authorizationService, router, authService); + } + + /** + * Check manage relationships authorization rights + */ + getFeatureID(next: ActivatedRouteSnapshot, state: RouterStateSnapshot): Observable { + return observableOf(FeatureID.CanManageRelationships); + } +} diff --git a/src/app/+item-page/edit-item-page/item-page-status.guard.ts b/src/app/+item-page/edit-item-page/item-page-status.guard.ts new file mode 100644 index 0000000000..98f963a4be --- /dev/null +++ b/src/app/+item-page/edit-item-page/item-page-status.guard.ts @@ -0,0 +1,32 @@ +import { Injectable } from '@angular/core'; +import { Item } from '../../core/shared/item.model'; +import { ItemPageResolver } from '../item-page.resolver'; +import { AuthorizationDataService } from '../../core/data/feature-authorization/authorization-data.service'; +import { ActivatedRouteSnapshot, Router, RouterStateSnapshot } from '@angular/router'; +import { Observable, of as observableOf } from 'rxjs'; +import { FeatureID } from '../../core/data/feature-authorization/feature-id'; +import { AuthService } from '../../core/auth/auth.service'; +import { DsoPageSomeFeatureGuard } from '../../core/data/feature-authorization/feature-authorization-guard/dso-page-some-feature.guard'; + +@Injectable({ + providedIn: 'root' +}) +/** + * Guard for preventing unauthorized access to certain {@link Item} pages requiring any of the rights required for + * the status page + */ +export class ItemPageStatusGuard extends DsoPageSomeFeatureGuard { + constructor(protected resolver: ItemPageResolver, + protected authorizationService: AuthorizationDataService, + protected router: Router, + protected authService: AuthService) { + super(resolver, authorizationService, router, authService); + } + + /** + * Check authorization rights + */ + getFeatureIDs(next: ActivatedRouteSnapshot, state: RouterStateSnapshot): Observable { + return observableOf([FeatureID.CanManageMappings, FeatureID.WithdrawItem, FeatureID.ReinstateItem, FeatureID.CanManagePolicies, FeatureID.CanMakePrivate, FeatureID.CanDelete, FeatureID.CanMove]); + } +} diff --git a/src/app/+item-page/edit-item-page/item-page-version-history.guard.ts b/src/app/+item-page/edit-item-page/item-page-version-history.guard.ts new file mode 100644 index 0000000000..dccdd9e641 --- /dev/null +++ b/src/app/+item-page/edit-item-page/item-page-version-history.guard.ts @@ -0,0 +1,31 @@ +import { Injectable } from '@angular/core'; +import { ActivatedRouteSnapshot, Router, RouterStateSnapshot } from '@angular/router'; +import { AuthorizationDataService } from '../../core/data/feature-authorization/authorization-data.service'; +import { ItemPageResolver } from '../item-page.resolver'; +import { Item } from '../../core/shared/item.model'; +import { DsoPageSingleFeatureGuard } from '../../core/data/feature-authorization/feature-authorization-guard/dso-page-single-feature.guard'; +import { Observable, of as observableOf } from 'rxjs'; +import { FeatureID } from '../../core/data/feature-authorization/feature-id'; +import { AuthService } from '../../core/auth/auth.service'; + +@Injectable({ + providedIn: 'root' +}) +/** + * Guard for preventing unauthorized access to certain {@link Item} pages requiring manage versions rights + */ +export class ItemPageVersionHistoryGuard extends DsoPageSingleFeatureGuard { + constructor(protected resolver: ItemPageResolver, + protected authorizationService: AuthorizationDataService, + protected router: Router, + protected authService: AuthService) { + super(resolver, authorizationService, router, authService); + } + + /** + * Check manage versions authorization rights + */ + getFeatureID(next: ActivatedRouteSnapshot, state: RouterStateSnapshot): Observable { + return observableOf(FeatureID.CanManageVersions); + } +} diff --git a/src/app/+item-page/edit-item-page/item-page-withdraw.guard.ts b/src/app/+item-page/edit-item-page/item-page-withdraw.guard.ts index 243f751974..de9b7d0147 100644 --- a/src/app/+item-page/edit-item-page/item-page-withdraw.guard.ts +++ b/src/app/+item-page/edit-item-page/item-page-withdraw.guard.ts @@ -1,4 +1,4 @@ -import { DsoPageFeatureGuard } from '../../core/data/feature-authorization/feature-authorization-guard/dso-page-feature.guard'; +import { DsoPageSingleFeatureGuard } from '../../core/data/feature-authorization/feature-authorization-guard/dso-page-single-feature.guard'; import { Item } from '../../core/shared/item.model'; import { Injectable } from '@angular/core'; import { ItemPageResolver } from '../item-page.resolver'; @@ -14,7 +14,7 @@ import { AuthService } from '../../core/auth/auth.service'; /** * Guard for preventing unauthorized access to certain {@link Item} pages requiring withdraw rights */ -export class ItemPageWithdrawGuard extends DsoPageFeatureGuard { +export class ItemPageWithdrawGuard extends DsoPageSingleFeatureGuard { constructor(protected resolver: ItemPageResolver, protected authorizationService: AuthorizationDataService, protected router: Router, diff --git a/src/app/+item-page/edit-item-page/item-status/item-status.component.ts b/src/app/+item-page/edit-item-page/item-status/item-status.component.ts index 2745fc8df7..51aa24ea6c 100644 --- a/src/app/+item-page/edit-item-page/item-status/item-status.component.ts +++ b/src/app/+item-page/edit-item-page/item-status/item-status.component.ts @@ -4,7 +4,7 @@ import { Item } from '../../../core/shared/item.model'; import { ActivatedRoute } from '@angular/router'; import { ItemOperation } from '../item-operation/itemOperation.model'; import { distinctUntilChanged, first, map } from 'rxjs/operators'; -import { BehaviorSubject, Observable } from 'rxjs'; +import { BehaviorSubject, combineLatest as observableCombineLatest, Observable, of } from 'rxjs'; import { RemoteData } from '../../../core/data/remote-data'; import { getItemEditRoute, getItemPageRoute } from '../../item-page-routing-paths'; import { AuthorizationDataService } from '../../../core/data/feature-authorization/authorization-data.service'; @@ -78,42 +78,33 @@ export class ItemStatusComponent implements OnInit { The value is supposed to be a href for the button */ const operations = []; - operations.push(new ItemOperation('authorizations', this.getCurrentUrl(item) + '/authorizations')); - operations.push(new ItemOperation('mappedCollections', this.getCurrentUrl(item) + '/mapper')); - operations.push(undefined); - // Store the index of the "withdraw" or "reinstate" operation, because it's added asynchronously - const indexOfWithdrawReinstate = operations.length - 1; - if (item.isDiscoverable) { - operations.push(new ItemOperation('private', this.getCurrentUrl(item) + '/private')); + operations.push(new ItemOperation('authorizations', this.getCurrentUrl(item) + '/authorizations', FeatureID.CanManagePolicies, true)); + operations.push(new ItemOperation('mappedCollections', this.getCurrentUrl(item) + '/mapper', FeatureID.CanManageMappings, true)); + if (item.isWithdrawn) { + operations.push(new ItemOperation('reinstate', this.getCurrentUrl(item) + '/reinstate', FeatureID.ReinstateItem, true)); } else { - operations.push(new ItemOperation('public', this.getCurrentUrl(item) + '/public')); + operations.push(new ItemOperation('reinstate', this.getCurrentUrl(item) + '/reinstate', FeatureID.WithdrawItem, true)); } - operations.push(new ItemOperation('delete', this.getCurrentUrl(item) + '/delete')); - operations.push(new ItemOperation('move', this.getCurrentUrl(item) + '/move')); + if (item.isDiscoverable) { + operations.push(new ItemOperation('private', this.getCurrentUrl(item) + '/private', FeatureID.CanMakePrivate, true)); + } else { + operations.push(new ItemOperation('public', this.getCurrentUrl(item) + '/public', FeatureID.CanMakePrivate, true)); + } + operations.push(new ItemOperation('delete', this.getCurrentUrl(item) + '/delete', FeatureID.CanDelete, true)); + operations.push(new ItemOperation('move', this.getCurrentUrl(item) + '/move', FeatureID.CanMove, true)); this.operations$.next(operations); - if (item.isWithdrawn) { - this.authorizationService.isAuthorized(FeatureID.ReinstateItem, item.self).pipe(distinctUntilChanged()).subscribe((authorized) => { - const newOperations = [...this.operations$.value]; - if (authorized) { - newOperations[indexOfWithdrawReinstate] = new ItemOperation('reinstate', this.getCurrentUrl(item) + '/reinstate'); - } else { - newOperations[indexOfWithdrawReinstate] = undefined; - } - this.operations$.next(newOperations); - }); - } else { - this.authorizationService.isAuthorized(FeatureID.WithdrawItem, item.self).pipe(distinctUntilChanged()).subscribe((authorized) => { - const newOperations = [...this.operations$.value]; - if (authorized) { - newOperations[indexOfWithdrawReinstate] = new ItemOperation('withdraw', this.getCurrentUrl(item) + '/withdraw'); - } else { - newOperations[indexOfWithdrawReinstate] = undefined; - } - this.operations$.next(newOperations); - }); - } + observableCombineLatest(operations.map((operation) => { + if (hasValue(operation.featureID)) { + return this.authorizationService.isAuthorized(operation.featureID, item.self).pipe( + distinctUntilChanged(), + map((authorized) => new ItemOperation(operation.operationKey, operation.operationUrl, operation.featureID, !authorized, authorized)) + ); + } else { + return of(operation); + } + })).subscribe((ops) => this.operations$.next(ops)); }); this.itemPageRoute$ = this.itemRD$.pipe( getAllSucceededRemoteDataPayload(), diff --git a/src/app/+item-page/item-page-administrator.guard.ts b/src/app/+item-page/item-page-administrator.guard.ts index c90502472e..5d3464aa75 100644 --- a/src/app/+item-page/item-page-administrator.guard.ts +++ b/src/app/+item-page/item-page-administrator.guard.ts @@ -3,7 +3,7 @@ import { ActivatedRouteSnapshot, Router, RouterStateSnapshot } from '@angular/ro import { AuthorizationDataService } from '../core/data/feature-authorization/authorization-data.service'; import { ItemPageResolver } from './item-page.resolver'; import { Item } from '../core/shared/item.model'; -import { DsoPageFeatureGuard } from '../core/data/feature-authorization/feature-authorization-guard/dso-page-feature.guard'; +import { DsoPageSingleFeatureGuard } from '../core/data/feature-authorization/feature-authorization-guard/dso-page-single-feature.guard'; import { Observable, of as observableOf } from 'rxjs'; import { FeatureID } from '../core/data/feature-authorization/feature-id'; import { AuthService } from '../core/auth/auth.service'; @@ -14,7 +14,7 @@ import { AuthService } from '../core/auth/auth.service'; /** * Guard for preventing unauthorized access to certain {@link Item} pages requiring administrator rights */ -export class ItemPageAdministratorGuard extends DsoPageFeatureGuard { +export class ItemPageAdministratorGuard extends DsoPageSingleFeatureGuard { constructor(protected resolver: ItemPageResolver, protected authorizationService: AuthorizationDataService, protected router: Router, diff --git a/src/app/core/data/feature-authorization/feature-authorization-guard/collection-administrator.guard.ts b/src/app/core/data/feature-authorization/feature-authorization-guard/collection-administrator.guard.ts index bc39397ed9..b41a322cb6 100644 --- a/src/app/core/data/feature-authorization/feature-authorization-guard/collection-administrator.guard.ts +++ b/src/app/core/data/feature-authorization/feature-authorization-guard/collection-administrator.guard.ts @@ -1,5 +1,5 @@ import { Injectable } from '@angular/core'; -import { FeatureAuthorizationGuard } from './feature-authorization.guard'; +import { SingleFeatureAuthorizationGuard } from './single-feature-authorization.guard'; import { AuthorizationDataService } from '../authorization-data.service'; import { ActivatedRouteSnapshot, Router, RouterStateSnapshot } from '@angular/router'; import { AuthService } from '../../../auth/auth.service'; @@ -13,7 +13,7 @@ import { FeatureID } from '../feature-id'; @Injectable({ providedIn: 'root' }) -export class CollectionAdministratorGuard extends FeatureAuthorizationGuard { +export class CollectionAdministratorGuard extends SingleFeatureAuthorizationGuard { constructor(protected authorizationService: AuthorizationDataService, protected router: Router, protected authService: AuthService) { super(authorizationService, router, authService); } diff --git a/src/app/core/data/feature-authorization/feature-authorization-guard/community-administrator.guard.ts b/src/app/core/data/feature-authorization/feature-authorization-guard/community-administrator.guard.ts index afb1fea63d..2ab77a00cc 100644 --- a/src/app/core/data/feature-authorization/feature-authorization-guard/community-administrator.guard.ts +++ b/src/app/core/data/feature-authorization/feature-authorization-guard/community-administrator.guard.ts @@ -1,5 +1,5 @@ import { Injectable } from '@angular/core'; -import { FeatureAuthorizationGuard } from './feature-authorization.guard'; +import { SingleFeatureAuthorizationGuard } from './single-feature-authorization.guard'; import { AuthorizationDataService } from '../authorization-data.service'; import { ActivatedRouteSnapshot, Router, RouterStateSnapshot } from '@angular/router'; import { AuthService } from '../../../auth/auth.service'; @@ -13,7 +13,7 @@ import { FeatureID } from '../feature-id'; @Injectable({ providedIn: 'root' }) -export class CommunityAdministratorGuard extends FeatureAuthorizationGuard { +export class CommunityAdministratorGuard extends SingleFeatureAuthorizationGuard { constructor(protected authorizationService: AuthorizationDataService, protected router: Router, protected authService: AuthService) { super(authorizationService, router, authService); } diff --git a/src/app/core/data/feature-authorization/feature-authorization-guard/dso-page-feature.guard.spec.ts b/src/app/core/data/feature-authorization/feature-authorization-guard/dso-page-single-feature.guard.spec.ts similarity index 93% rename from src/app/core/data/feature-authorization/feature-authorization-guard/dso-page-feature.guard.spec.ts rename to src/app/core/data/feature-authorization/feature-authorization-guard/dso-page-single-feature.guard.spec.ts index f98e3f1837..06677d1f22 100644 --- a/src/app/core/data/feature-authorization/feature-authorization-guard/dso-page-feature.guard.spec.ts +++ b/src/app/core/data/feature-authorization/feature-authorization-guard/dso-page-single-feature.guard.spec.ts @@ -4,14 +4,14 @@ import { RemoteData } from '../../remote-data'; import { Observable, of as observableOf } from 'rxjs'; import { createSuccessfulRemoteDataObject$ } from '../../../../shared/remote-data.utils'; import { DSpaceObject } from '../../../shared/dspace-object.model'; -import { DsoPageFeatureGuard } from './dso-page-feature.guard'; +import { DsoPageSingleFeatureGuard } from './dso-page-single-feature.guard'; import { FeatureID } from '../feature-id'; import { AuthService } from '../../../auth/auth.service'; /** * Test implementation of abstract class DsoPageAdministratorGuard */ -class DsoPageFeatureGuardImpl extends DsoPageFeatureGuard { +class DsoPageFeatureGuardImpl extends DsoPageSingleFeatureGuard { constructor(protected resolver: Resolve>, protected authorizationService: AuthorizationDataService, protected router: Router, @@ -26,7 +26,7 @@ class DsoPageFeatureGuardImpl extends DsoPageFeatureGuard { } describe('DsoPageAdministratorGuard', () => { - let guard: DsoPageFeatureGuard; + let guard: DsoPageSingleFeatureGuard; let authorizationService: AuthorizationDataService; let router: Router; let authService: AuthService; diff --git a/src/app/core/data/feature-authorization/feature-authorization-guard/dso-page-single-feature.guard.ts b/src/app/core/data/feature-authorization/feature-authorization-guard/dso-page-single-feature.guard.ts new file mode 100644 index 0000000000..3fc90f9069 --- /dev/null +++ b/src/app/core/data/feature-authorization/feature-authorization-guard/dso-page-single-feature.guard.ts @@ -0,0 +1,27 @@ +import { DSpaceObject } from '../../../shared/dspace-object.model'; +import { DsoPageSomeFeatureGuard } from './dso-page-some-feature.guard'; +import { ActivatedRouteSnapshot, RouterStateSnapshot } from '@angular/router'; +import { FeatureID } from '../feature-id'; +import { map } from 'rxjs/operators'; +import { Observable } from 'rxjs'; + +/** + * Abstract Guard for preventing unauthorized access to {@link DSpaceObject} pages that require rights for a specific feature + * This guard utilizes a resolver to retrieve the relevant object to check authorizations for + */ +export abstract class DsoPageSingleFeatureGuard extends DsoPageSomeFeatureGuard { + /** + * The features to check authorization for + */ + getFeatureIDs(route: ActivatedRouteSnapshot, state: RouterStateSnapshot): Observable { + return this.getFeatureID(route, state).pipe( + map((featureID) => [featureID]), + ); + } + + /** + * The type of feature to check authorization for + * Override this method to define a feature + */ + abstract getFeatureID(route: ActivatedRouteSnapshot, state: RouterStateSnapshot): Observable; +} diff --git a/src/app/core/data/feature-authorization/feature-authorization-guard/dso-page-feature.guard.ts b/src/app/core/data/feature-authorization/feature-authorization-guard/dso-page-some-feature.guard.ts similarity index 90% rename from src/app/core/data/feature-authorization/feature-authorization-guard/dso-page-feature.guard.ts rename to src/app/core/data/feature-authorization/feature-authorization-guard/dso-page-some-feature.guard.ts index c50dd7f95d..7b7cb4c196 100644 --- a/src/app/core/data/feature-authorization/feature-authorization-guard/dso-page-feature.guard.ts +++ b/src/app/core/data/feature-authorization/feature-authorization-guard/dso-page-some-feature.guard.ts @@ -5,15 +5,15 @@ import { Observable } from 'rxjs'; import { getAllSucceededRemoteDataPayload } from '../../../shared/operators'; import { map } from 'rxjs/operators'; import { DSpaceObject } from '../../../shared/dspace-object.model'; -import { FeatureAuthorizationGuard } from './feature-authorization.guard'; import { AuthService } from '../../../auth/auth.service'; import { hasNoValue, hasValue } from '../../../../shared/empty.util'; +import { SomeFeatureAuthorizationGuard } from './some-feature-authorization.guard'; /** * Abstract Guard for preventing unauthorized access to {@link DSpaceObject} pages that require rights for a specific feature * This guard utilizes a resolver to retrieve the relevant object to check authorizations for */ -export abstract class DsoPageFeatureGuard extends FeatureAuthorizationGuard { +export abstract class DsoPageSomeFeatureGuard extends SomeFeatureAuthorizationGuard { constructor(protected resolver: Resolve>, protected authorizationService: AuthorizationDataService, protected router: Router, diff --git a/src/app/core/data/feature-authorization/feature-authorization-guard/group-administrator.guard.ts b/src/app/core/data/feature-authorization/feature-authorization-guard/group-administrator.guard.ts index 3fee767fdc..5afd572326 100644 --- a/src/app/core/data/feature-authorization/feature-authorization-guard/group-administrator.guard.ts +++ b/src/app/core/data/feature-authorization/feature-authorization-guard/group-administrator.guard.ts @@ -1,5 +1,5 @@ import { Injectable } from '@angular/core'; -import { FeatureAuthorizationGuard } from './feature-authorization.guard'; +import { SingleFeatureAuthorizationGuard } from './single-feature-authorization.guard'; import { AuthorizationDataService } from '../authorization-data.service'; import { ActivatedRouteSnapshot, Router, RouterStateSnapshot } from '@angular/router'; import { AuthService } from '../../../auth/auth.service'; @@ -13,7 +13,7 @@ import { FeatureID } from '../feature-id'; @Injectable({ providedIn: 'root' }) -export class GroupAdministratorGuard extends FeatureAuthorizationGuard { +export class GroupAdministratorGuard extends SingleFeatureAuthorizationGuard { constructor(protected authorizationService: AuthorizationDataService, protected router: Router, protected authService: AuthService) { super(authorizationService, router, authService); } diff --git a/src/app/core/data/feature-authorization/feature-authorization-guard/feature-authorization.guard.spec.ts b/src/app/core/data/feature-authorization/feature-authorization-guard/single-feature-authorization.guard.spec.ts similarity index 92% rename from src/app/core/data/feature-authorization/feature-authorization-guard/feature-authorization.guard.spec.ts rename to src/app/core/data/feature-authorization/feature-authorization-guard/single-feature-authorization.guard.spec.ts index 2c6f4b0717..1fa5498f12 100644 --- a/src/app/core/data/feature-authorization/feature-authorization-guard/feature-authorization.guard.spec.ts +++ b/src/app/core/data/feature-authorization/feature-authorization-guard/single-feature-authorization.guard.spec.ts @@ -1,4 +1,4 @@ -import { FeatureAuthorizationGuard } from './feature-authorization.guard'; +import { SingleFeatureAuthorizationGuard } from './single-feature-authorization.guard'; import { AuthorizationDataService } from '../authorization-data.service'; import { FeatureID } from '../feature-id'; import { Observable, of as observableOf } from 'rxjs'; @@ -9,7 +9,7 @@ import { AuthService } from '../../../auth/auth.service'; * Test implementation of abstract class FeatureAuthorizationGuard * Provide the return values of the overwritten getters as constructor arguments */ -class FeatureAuthorizationGuardImpl extends FeatureAuthorizationGuard { +class FeatureAuthorizationGuardImpl extends SingleFeatureAuthorizationGuard { constructor(protected authorizationService: AuthorizationDataService, protected router: Router, protected authService: AuthService, @@ -33,7 +33,7 @@ class FeatureAuthorizationGuardImpl extends FeatureAuthorizationGuard { } describe('FeatureAuthorizationGuard', () => { - let guard: FeatureAuthorizationGuard; + let guard: SingleFeatureAuthorizationGuard; let authorizationService: AuthorizationDataService; let router: Router; let authService: AuthService; diff --git a/src/app/core/data/feature-authorization/feature-authorization-guard/single-feature-authorization.guard.ts b/src/app/core/data/feature-authorization/feature-authorization-guard/single-feature-authorization.guard.ts new file mode 100644 index 0000000000..cb71d2f418 --- /dev/null +++ b/src/app/core/data/feature-authorization/feature-authorization-guard/single-feature-authorization.guard.ts @@ -0,0 +1,27 @@ +import { ActivatedRouteSnapshot, RouterStateSnapshot } from '@angular/router'; +import { FeatureID } from '../feature-id'; +import { Observable } from 'rxjs'; +import { map} from 'rxjs/operators'; +import { SomeFeatureAuthorizationGuard } from './some-feature-authorization.guard'; + +/** + * Abstract Guard for preventing unauthorized activating and loading of routes when a user + * doesn't have authorized rights on a specific feature and/or object. + * Override the desired getters in the parent class for checking specific authorization on a feature and/or object. + */ +export abstract class SingleFeatureAuthorizationGuard extends SomeFeatureAuthorizationGuard { + /** + * The features to check authorization for + */ + getFeatureIDs(route: ActivatedRouteSnapshot, state: RouterStateSnapshot): Observable { + return this.getFeatureID(route, state).pipe( + map((featureID) => [featureID]), + ); + } + + /** + * The type of feature to check authorization for + * Override this method to define a feature + */ + abstract getFeatureID(route: ActivatedRouteSnapshot, state: RouterStateSnapshot): Observable; +} diff --git a/src/app/core/data/feature-authorization/feature-authorization-guard/site-administrator.guard.ts b/src/app/core/data/feature-authorization/feature-authorization-guard/site-administrator.guard.ts index bb678ebf33..cc6f50c161 100644 --- a/src/app/core/data/feature-authorization/feature-authorization-guard/site-administrator.guard.ts +++ b/src/app/core/data/feature-authorization/feature-authorization-guard/site-administrator.guard.ts @@ -1,5 +1,5 @@ import { Injectable } from '@angular/core'; -import { FeatureAuthorizationGuard } from './feature-authorization.guard'; +import { SingleFeatureAuthorizationGuard } from './single-feature-authorization.guard'; import { FeatureID } from '../feature-id'; import { AuthorizationDataService } from '../authorization-data.service'; import { ActivatedRouteSnapshot, Router, RouterStateSnapshot } from '@angular/router'; @@ -13,7 +13,7 @@ import { AuthService } from '../../../auth/auth.service'; @Injectable({ providedIn: 'root' }) -export class SiteAdministratorGuard extends FeatureAuthorizationGuard { +export class SiteAdministratorGuard extends SingleFeatureAuthorizationGuard { constructor(protected authorizationService: AuthorizationDataService, protected router: Router, protected authService: AuthService) { super(authorizationService, router, authService); } diff --git a/src/app/core/data/feature-authorization/feature-authorization-guard/site-register.guard.ts b/src/app/core/data/feature-authorization/feature-authorization-guard/site-register.guard.ts index 709d9ff266..bdbb8250e2 100644 --- a/src/app/core/data/feature-authorization/feature-authorization-guard/site-register.guard.ts +++ b/src/app/core/data/feature-authorization/feature-authorization-guard/site-register.guard.ts @@ -1,4 +1,4 @@ -import { FeatureAuthorizationGuard } from './feature-authorization.guard'; +import { SingleFeatureAuthorizationGuard } from './single-feature-authorization.guard'; import { Injectable } from '@angular/core'; import { AuthorizationDataService } from '../authorization-data.service'; import { ActivatedRouteSnapshot, Router, RouterStateSnapshot } from '@angular/router'; @@ -13,7 +13,7 @@ import { AuthService } from '../../../auth/auth.service'; @Injectable({ providedIn: 'root' }) -export class SiteRegisterGuard extends FeatureAuthorizationGuard { +export class SiteRegisterGuard extends SingleFeatureAuthorizationGuard { constructor(protected authorizationService: AuthorizationDataService, protected router: Router, protected authService: AuthService) { super(authorizationService, router, authService); } diff --git a/src/app/core/data/feature-authorization/feature-authorization-guard/feature-authorization.guard.ts b/src/app/core/data/feature-authorization/feature-authorization-guard/some-feature-authorization.guard.ts similarity index 64% rename from src/app/core/data/feature-authorization/feature-authorization-guard/feature-authorization.guard.ts rename to src/app/core/data/feature-authorization/feature-authorization-guard/some-feature-authorization.guard.ts index 86b75b637e..3a6cf745c9 100644 --- a/src/app/core/data/feature-authorization/feature-authorization-guard/feature-authorization.guard.ts +++ b/src/app/core/data/feature-authorization/feature-authorization-guard/some-feature-authorization.guard.ts @@ -2,16 +2,16 @@ import { ActivatedRouteSnapshot, CanActivate, Router, RouterStateSnapshot, UrlTr import { AuthorizationDataService } from '../authorization-data.service'; import { FeatureID } from '../feature-id'; import { combineLatest as observableCombineLatest, Observable, of as observableOf } from 'rxjs'; -import { returnForbiddenUrlTreeOrLoginOnFalse } from '../../../shared/operators'; +import { returnForbiddenUrlTreeOrLoginOnAllFalse } from '../../../shared/operators'; import { switchMap } from 'rxjs/operators'; import { AuthService } from '../../../auth/auth.service'; /** * Abstract Guard for preventing unauthorized activating and loading of routes when a user - * doesn't have authorized rights on a specific feature and/or object. - * Override the desired getters in the parent class for checking specific authorization on a feature and/or object. + * doesn't have authorized rights on any of the specified features and/or object. + * Override the desired getters in the parent class for checking specific authorization on a list of features and/or object. */ -export abstract class FeatureAuthorizationGuard implements CanActivate { +export abstract class SomeFeatureAuthorizationGuard implements CanActivate { constructor(protected authorizationService: AuthorizationDataService, protected router: Router, protected authService: AuthService) { @@ -22,17 +22,19 @@ export abstract class FeatureAuthorizationGuard implements CanActivate { * Redirect the user to the unauthorized page when he/she's not authorized for the given feature */ canActivate(route: ActivatedRouteSnapshot, state: RouterStateSnapshot): Observable { - return observableCombineLatest(this.getFeatureID(route, state), this.getObjectUrl(route, state), this.getEPersonUuid(route, state)).pipe( - switchMap(([featureID, objectUrl, ePersonUuid]) => this.authorizationService.isAuthorized(featureID, objectUrl, ePersonUuid)), - returnForbiddenUrlTreeOrLoginOnFalse(this.router, this.authService, state.url) + return observableCombineLatest(this.getFeatureIDs(route, state), this.getObjectUrl(route, state), this.getEPersonUuid(route, state)).pipe( + switchMap(([featureIDs, objectUrl, ePersonUuid]) => + observableCombineLatest(...featureIDs.map((featureID) => this.authorizationService.isAuthorized(featureID, objectUrl, ePersonUuid))) + ), + returnForbiddenUrlTreeOrLoginOnAllFalse(this.router, this.authService, state.url) ); } /** - * The type of feature to check authorization for - * Override this method to define a feature + * The features to check authorization for + * Override this method to define a list of features */ - abstract getFeatureID(route: ActivatedRouteSnapshot, state: RouterStateSnapshot): Observable; + abstract getFeatureIDs(route: ActivatedRouteSnapshot, state: RouterStateSnapshot): Observable; /** * The URL of the object to check if the user has authorized rights for diff --git a/src/app/core/data/feature-authorization/feature-id.ts b/src/app/core/data/feature-authorization/feature-id.ts index e3473a895e..df77dd8949 100644 --- a/src/app/core/data/feature-authorization/feature-id.ts +++ b/src/app/core/data/feature-authorization/feature-id.ts @@ -12,4 +12,11 @@ export enum FeatureID { CanManageGroups = 'canManageGroups', IsCollectionAdmin = 'isCollectionAdmin', IsCommunityAdmin = 'isCommunityAdmin', + CanManageVersions = 'canManageVersions', + CanManageBitstreams = 'canManageBitstreams', + CanManageRelationships = 'canManageRelationships', + CanManageMappings = 'canManageMappings', + CanManagePolicies = 'canManagePolicies', + CanMakePrivate = 'canMakePrivate', + CanMove = 'canMove', } diff --git a/src/app/core/shared/operators.ts b/src/app/core/shared/operators.ts index dd610b6ca7..2d0ab70e2c 100644 --- a/src/app/core/shared/operators.ts +++ b/src/app/core/shared/operators.ts @@ -201,10 +201,23 @@ export const redirectOn4xx = (router: Router, authService: AuthService) => */ export const returnForbiddenUrlTreeOrLoginOnFalse = (router: Router, authService: AuthService, redirectUrl: string) => (source: Observable): Observable => + source.pipe( + map((authorized) => [authorized]), + returnForbiddenUrlTreeOrLoginOnAllFalse(router, authService, redirectUrl), + ); + +/** + * Operator that returns a UrlTree to a forbidden page or the login page when the booleans received are all false + * @param router The router used to navigate to a forbidden page + * @param authService The AuthService used to determine whether or not the user is logged in + * @param redirectUrl The URL to redirect back to after logging in + */ +export const returnForbiddenUrlTreeOrLoginOnAllFalse = (router: Router, authService: AuthService, redirectUrl: string) => + (source: Observable): Observable => observableCombineLatest(source, authService.isAuthenticated()).pipe( - map(([authorized, authenticated]: [boolean, boolean]) => { - if (authorized) { - return authorized; + map(([authorizedList, authenticated]: [boolean[], boolean]) => { + if (authorizedList.indexOf(true) > -1) { + return true; } else { if (authenticated) { return router.parseUrl(getForbiddenRoute()); diff --git a/src/app/shared/mocks/dspace-rest/endpoint-mocking-rest.service.ts b/src/app/shared/mocks/dspace-rest/endpoint-mocking-rest.service.ts index 278a392e18..8d621ad4be 100644 --- a/src/app/shared/mocks/dspace-rest/endpoint-mocking-rest.service.ts +++ b/src/app/shared/mocks/dspace-rest/endpoint-mocking-rest.service.ts @@ -97,8 +97,14 @@ export class EndpointMockingRestService extends DspaceRestService { * the mock response if there is one, undefined otherwise */ private getMockData(urlStr: string): any { - const url = new URL(urlStr); - const key = url.pathname.slice(environment.rest.nameSpace.length); + let key; + if (this.mockResponseMap.has(urlStr)) { + key = urlStr; + } else { + // didn't find an exact match for the url, try to match only the endpoint without namespace and parameters + const url = new URL(urlStr); + key = url.pathname.slice(environment.rest.nameSpace.length); + } if (this.mockResponseMap.has(key)) { // parse and stringify to clone the object to ensure that any changes made // to it afterwards don't affect future calls diff --git a/src/app/shared/mocks/dspace-rest/mocks/mock-feature-item-can-manage-bitstreams-response.json b/src/app/shared/mocks/dspace-rest/mocks/mock-feature-item-can-manage-bitstreams-response.json new file mode 100644 index 0000000000..1642691672 --- /dev/null +++ b/src/app/shared/mocks/dspace-rest/mocks/mock-feature-item-can-manage-bitstreams-response.json @@ -0,0 +1,51 @@ +{ + "_embedded": { + "authorizations": [ + { + "id": "cd824a61-95be-4e16-bccd-51fea26707d0_canManageBitstreams_core.item_96715576-3748-4761-ad45-001646632963", + "type": "authorization", + "_links": { + "eperson": { + "href": "https://api7.dspace.org/server/api/authz/authorizations/cd824a61-95be-4e16-bccd-51fea26707d0_canManageBitstreams_core.item_96715576-3748-4761-ad45-001646632963/eperson" + }, + "feature": { + "href": "https://api7.dspace.org/server/api/authz/authorizations/cd824a61-95be-4e16-bccd-51fea26707d0_canManageBitstreams_core.item_96715576-3748-4761-ad45-001646632963/feature" + }, + "object": { + "href": "https://api7.dspace.org/server/api/authz/authorizations/cd824a61-95be-4e16-bccd-51fea26707d0_canManageBitstreams_core.item_96715576-3748-4761-ad45-001646632963/object" + }, + "self": { + "href": "https://api7.dspace.org/server/api/authz/authorizations/cd824a61-95be-4e16-bccd-51fea26707d0_canManageBitstreams_core.item_96715576-3748-4761-ad45-001646632963" + } + }, + "_embedded": { + "feature": { + "id": "canManageBitstreams", + "description": "It can be used to verify if the bitstreams of the specified objects can be managed", + "type": "feature", + "resourcetypes": [ + "core.item", + "core.bundle" + ], + "_links": { + "self": { + "href": "https://api7.dspace.org/server/api/authz/features/canManageBitstreams" + } + } + } + } + } + ] + }, + "_links": { + "self": { + "href": "https://api7.dspace.org/server/api/authz/authorizations/search/object?uri=https://api7.dspace.org/server/api/core/items/96715576-3748-4761-ad45-001646632963&feature=canManageBitstreams" + } + }, + "page": { + "size": 20, + "totalElements": 1, + "totalPages": 1, + "number": 0 + } +} diff --git a/src/app/shared/mocks/dspace-rest/mocks/response-map.mock.ts b/src/app/shared/mocks/dspace-rest/mocks/response-map.mock.ts index 92f04a7bc5..4cc5c39b7d 100644 --- a/src/app/shared/mocks/dspace-rest/mocks/response-map.mock.ts +++ b/src/app/shared/mocks/dspace-rest/mocks/response-map.mock.ts @@ -2,6 +2,7 @@ import { InjectionToken } from '@angular/core'; // import mockSubmissionResponse from './mock-submission-response.json'; // import mockPublicationResponse from './mock-publication-response.json'; // import mockUntypedItemResponse from './mock-untyped-item-response.json'; +import mockFeatureItemCanManageBitstreamsResponse from './mock-feature-item-can-manage-bitstreams-response.json'; export class ResponseMapMock extends Map {} @@ -16,4 +17,5 @@ export const mockResponseMap: ResponseMapMock = new Map([ // [ '/config/submissionforms/traditionalpageone', mockSubmissionResponse ] // [ '/api/pid/find', mockPublicationResponse ], // [ '/api/pid/find', mockUntypedItemResponse ], + [ 'https://api7.dspace.org/server/api/authz/authorizations/search/object?uri=https://api7.dspace.org/server/api/core/items/96715576-3748-4761-ad45-001646632963&feature=canManageBitstreams&embed=feature', mockFeatureItemCanManageBitstreamsResponse ], ]); diff --git a/src/assets/i18n/en.json5 b/src/assets/i18n/en.json5 index 2924a3a47e..abecfce291 100644 --- a/src/assets/i18n/en.json5 +++ b/src/assets/i18n/en.json5 @@ -1761,6 +1761,8 @@ "item.edit.tabs.status.buttons.reinstate.label": "Reinstate item into the repository", + "item.edit.tabs.status.buttons.unauthorized": "You don't have permission to perform this action", + "item.edit.tabs.status.buttons.withdraw.button": "Withdraw...", "item.edit.tabs.status.buttons.withdraw.label": "Withdraw item from the repository",