mirror of
https://github.com/DSpace/dspace-angular.git
synced 2025-10-08 02:24:11 +00:00
Merge pull request #1105 from atmire/Edit-item-page-permission-checks
Edit item page permission checks
This commit is contained in:
@@ -4,7 +4,7 @@ import { Collection } from '../core/shared/collection.model';
|
|||||||
import { CollectionPageResolver } from './collection-page.resolver';
|
import { CollectionPageResolver } from './collection-page.resolver';
|
||||||
import { AuthorizationDataService } from '../core/data/feature-authorization/authorization-data.service';
|
import { AuthorizationDataService } from '../core/data/feature-authorization/authorization-data.service';
|
||||||
import { Observable, of as observableOf } from 'rxjs';
|
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 { FeatureID } from '../core/data/feature-authorization/feature-id';
|
||||||
import { AuthService } from '../core/auth/auth.service';
|
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
|
* Guard for preventing unauthorized access to certain {@link Collection} pages requiring administrator rights
|
||||||
*/
|
*/
|
||||||
export class CollectionPageAdministratorGuard extends DsoPageFeatureGuard<Collection> {
|
export class CollectionPageAdministratorGuard extends DsoPageSingleFeatureGuard<Collection> {
|
||||||
constructor(protected resolver: CollectionPageResolver,
|
constructor(protected resolver: CollectionPageResolver,
|
||||||
protected authorizationService: AuthorizationDataService,
|
protected authorizationService: AuthorizationDataService,
|
||||||
protected router: Router,
|
protected router: Router,
|
||||||
|
@@ -4,7 +4,7 @@ import { Community } from '../core/shared/community.model';
|
|||||||
import { CommunityPageResolver } from './community-page.resolver';
|
import { CommunityPageResolver } from './community-page.resolver';
|
||||||
import { AuthorizationDataService } from '../core/data/feature-authorization/authorization-data.service';
|
import { AuthorizationDataService } from '../core/data/feature-authorization/authorization-data.service';
|
||||||
import { Observable, of as observableOf } from 'rxjs';
|
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 { FeatureID } from '../core/data/feature-authorization/feature-id';
|
||||||
import { AuthService } from '../core/auth/auth.service';
|
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
|
* Guard for preventing unauthorized access to certain {@link Community} pages requiring administrator rights
|
||||||
*/
|
*/
|
||||||
export class CommunityPageAdministratorGuard extends DsoPageFeatureGuard<Community> {
|
export class CommunityPageAdministratorGuard extends DsoPageSingleFeatureGuard<Community> {
|
||||||
constructor(protected resolver: CommunityPageResolver,
|
constructor(protected resolver: CommunityPageResolver,
|
||||||
protected authorizationService: AuthorizationDataService,
|
protected authorizationService: AuthorizationDataService,
|
||||||
protected router: Router,
|
protected router: Router,
|
||||||
|
@@ -31,8 +31,13 @@ import {
|
|||||||
} from './edit-item-page.routing-paths';
|
} from './edit-item-page.routing-paths';
|
||||||
import { ItemPageReinstateGuard } from './item-page-reinstate.guard';
|
import { ItemPageReinstateGuard } from './item-page-reinstate.guard';
|
||||||
import { ItemPageWithdrawGuard } from './item-page-withdraw.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 { 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
|
* 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',
|
path: 'status',
|
||||||
component: ItemStatusComponent,
|
component: ItemStatusComponent,
|
||||||
data: { title: 'item.edit.tabs.status.title', showBreadcrumbs: true },
|
data: { title: 'item.edit.tabs.status.title', showBreadcrumbs: true },
|
||||||
canActivate: [ItemPageAdministratorGuard]
|
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: [ItemPageAdministratorGuard]
|
canActivate: [ItemPageBitstreamsGuard]
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
path: 'metadata',
|
path: 'metadata',
|
||||||
component: ItemMetadataComponent,
|
component: ItemMetadataComponent,
|
||||||
data: { title: 'item.edit.tabs.metadata.title', showBreadcrumbs: true },
|
data: { title: 'item.edit.tabs.metadata.title', showBreadcrumbs: true },
|
||||||
canActivate: [ItemPageEditMetadataGuard]
|
canActivate: [ItemPageMetadataGuard]
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
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: [ItemPageEditMetadataGuard]
|
canActivate: [ItemPageRelationshipsGuard]
|
||||||
},
|
},
|
||||||
/* TODO - uncomment & fix when view page exists
|
/* TODO - uncomment & fix when view page exists
|
||||||
{
|
{
|
||||||
@@ -96,13 +101,13 @@ import { ItemPageAdministratorGuard } from '../item-page-administrator.guard';
|
|||||||
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: [ItemPageAdministratorGuard]
|
canActivate: [ItemPageVersionHistoryGuard]
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
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: [ItemPageAdministratorGuard]
|
canActivate: [ItemPageCollectionMapperGuard]
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
@@ -175,7 +180,12 @@ import { ItemPageAdministratorGuard } from '../item-page-administrator.guard';
|
|||||||
ItemPageReinstateGuard,
|
ItemPageReinstateGuard,
|
||||||
ItemPageWithdrawGuard,
|
ItemPageWithdrawGuard,
|
||||||
ItemPageAdministratorGuard,
|
ItemPageAdministratorGuard,
|
||||||
ItemPageEditMetadataGuard,
|
ItemPageMetadataGuard,
|
||||||
|
ItemPageStatusGuard,
|
||||||
|
ItemPageBitstreamsGuard,
|
||||||
|
ItemPageRelationshipsGuard,
|
||||||
|
ItemPageVersionHistoryGuard,
|
||||||
|
ItemPageCollectionMapperGuard,
|
||||||
]
|
]
|
||||||
})
|
})
|
||||||
export class EditItemPageRoutingModule {
|
export class EditItemPageRoutingModule {
|
||||||
|
@@ -3,13 +3,15 @@
|
|||||||
{{'item.edit.tabs.status.buttons.' + operation.operationKey + '.label' | translate}}
|
{{'item.edit.tabs.status.buttons.' + operation.operationKey + '.label' | translate}}
|
||||||
</span>
|
</span>
|
||||||
</div>
|
</div>
|
||||||
<div *ngIf="!operation.disabled" class="col-9 float-left action-button">
|
<div class="col-9 float-left action-button">
|
||||||
<a class="btn btn-outline-primary" [routerLink]="operation.operationUrl">
|
<span *ngIf="operation.authorized">
|
||||||
{{'item.edit.tabs.status.buttons.' + operation.operationKey + '.button' | translate}}
|
<button class="btn btn-outline-primary" [disabled]="operation.disabled" [routerLink]="operation.operationUrl">
|
||||||
</a>
|
{{'item.edit.tabs.status.buttons.' + operation.operationKey + '.button' | translate}}
|
||||||
</div>
|
</button>
|
||||||
<div *ngIf="operation.disabled" class="col-9 float-left action-button">
|
</span>
|
||||||
<span class="btn btn-danger">
|
<span *ngIf="!operation.authorized" [ngbTooltip]="'item.edit.tabs.status.buttons.unauthorized' | translate">
|
||||||
{{'item.edit.tabs.status.buttons.' + operation.operationKey + '.button' | translate}}
|
<button class="btn btn-outline-primary" [disabled]="true">
|
||||||
|
{{'item.edit.tabs.status.buttons.' + operation.operationKey + '.button' | translate}}
|
||||||
|
</button>
|
||||||
</span>
|
</span>
|
||||||
</div>
|
</div>
|
||||||
|
@@ -28,19 +28,19 @@ describe('ItemOperationComponent', () => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
it('should render operation row', () => {
|
it('should render operation row', () => {
|
||||||
const span = fixture.debugElement.query(By.css('span')).nativeElement;
|
const span = fixture.debugElement.query(By.css('.action-label span')).nativeElement;
|
||||||
expect(span.textContent).toContain('item.edit.tabs.status.buttons.key1.label');
|
expect(span.textContent).toContain('item.edit.tabs.status.buttons.key1.label');
|
||||||
const link = fixture.debugElement.query(By.css('a')).nativeElement;
|
const button = fixture.debugElement.query(By.css('button')).nativeElement;
|
||||||
expect(link.href).toContain('url1');
|
expect(button.textContent).toContain('item.edit.tabs.status.buttons.key1.button');
|
||||||
expect(link.textContent).toContain('item.edit.tabs.status.buttons.key1.button');
|
|
||||||
});
|
});
|
||||||
it('should render disabled operation row', () => {
|
it('should render disabled operation row', () => {
|
||||||
itemOperation.setDisabled(true);
|
itemOperation.setDisabled(true);
|
||||||
fixture.detectChanges();
|
fixture.detectChanges();
|
||||||
|
|
||||||
const span = fixture.debugElement.query(By.css('span')).nativeElement;
|
const span = fixture.debugElement.query(By.css('.action-label span')).nativeElement;
|
||||||
expect(span.textContent).toContain('item.edit.tabs.status.buttons.key1.label');
|
expect(span.textContent).toContain('item.edit.tabs.status.buttons.key1.label');
|
||||||
const span2 = fixture.debugElement.query(By.css('span.btn-danger')).nativeElement;
|
const button = fixture.debugElement.query(By.css('button')).nativeElement;
|
||||||
expect(span2.textContent).toContain('item.edit.tabs.status.buttons.key1.button');
|
expect(button.disabled).toBeTrue();
|
||||||
|
expect(button.textContent).toContain('item.edit.tabs.status.buttons.key1.button');
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
@@ -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
|
* 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.
|
* when performing the action and an option to disable the operation.
|
||||||
@@ -7,11 +9,15 @@ export class ItemOperation {
|
|||||||
operationKey: string;
|
operationKey: string;
|
||||||
operationUrl: string;
|
operationUrl: string;
|
||||||
disabled: boolean;
|
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.operationKey = operationKey;
|
||||||
this.operationUrl = operationUrl;
|
this.operationUrl = operationUrl;
|
||||||
this.setDisabled(false);
|
this.featureID = featureID;
|
||||||
|
this.authorized = authorized;
|
||||||
|
this.setDisabled(disabled);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@@ -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<Item> {
|
||||||
|
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<FeatureID> {
|
||||||
|
return observableOf(FeatureID.CanManageBitstreamBundles);
|
||||||
|
}
|
||||||
|
}
|
@@ -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<Item> {
|
||||||
|
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<FeatureID> {
|
||||||
|
return observableOf(FeatureID.CanManageMappings);
|
||||||
|
}
|
||||||
|
}
|
@@ -1,12 +1,12 @@
|
|||||||
import { Injectable } from '@angular/core';
|
import { Injectable } from '@angular/core';
|
||||||
import { ActivatedRouteSnapshot, Router, RouterStateSnapshot } from '@angular/router';
|
import { ActivatedRouteSnapshot, Router, RouterStateSnapshot } from '@angular/router';
|
||||||
import { AuthorizationDataService } from '../core/data/feature-authorization/authorization-data.service';
|
import { AuthorizationDataService } from '../../core/data/feature-authorization/authorization-data.service';
|
||||||
import { ItemPageResolver } from './item-page.resolver';
|
import { ItemPageResolver } from '../item-page.resolver';
|
||||||
import { Item } from '../core/shared/item.model';
|
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 { Observable, of as observableOf } from 'rxjs';
|
||||||
import { FeatureID } from '../core/data/feature-authorization/feature-id';
|
import { FeatureID } from '../../core/data/feature-authorization/feature-id';
|
||||||
import { AuthService } from '../core/auth/auth.service';
|
import { AuthService } from '../../core/auth/auth.service';
|
||||||
|
|
||||||
@Injectable({
|
@Injectable({
|
||||||
providedIn: 'root'
|
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
|
* Guard for preventing unauthorized access to certain {@link Item} pages requiring edit metadata rights
|
||||||
*/
|
*/
|
||||||
export class ItemPageEditMetadataGuard extends DsoPageFeatureGuard<Item> {
|
export class ItemPageMetadataGuard extends DsoPageSingleFeatureGuard<Item> {
|
||||||
constructor(protected resolver: ItemPageResolver,
|
constructor(protected resolver: ItemPageResolver,
|
||||||
protected authorizationService: AuthorizationDataService,
|
protected authorizationService: AuthorizationDataService,
|
||||||
protected router: Router,
|
protected router: Router,
|
@@ -1,5 +1,5 @@
|
|||||||
import { Injectable } from '@angular/core';
|
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 { Item } from '../../core/shared/item.model';
|
||||||
import { ItemPageResolver } from '../item-page.resolver';
|
import { ItemPageResolver } from '../item-page.resolver';
|
||||||
import { AuthorizationDataService } from '../../core/data/feature-authorization/authorization-data.service';
|
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
|
* Guard for preventing unauthorized access to certain {@link Item} pages requiring reinstate rights
|
||||||
*/
|
*/
|
||||||
export class ItemPageReinstateGuard extends DsoPageFeatureGuard<Item> {
|
export class ItemPageReinstateGuard extends DsoPageSingleFeatureGuard<Item> {
|
||||||
constructor(protected resolver: ItemPageResolver,
|
constructor(protected resolver: ItemPageResolver,
|
||||||
protected authorizationService: AuthorizationDataService,
|
protected authorizationService: AuthorizationDataService,
|
||||||
protected router: Router,
|
protected router: Router,
|
||||||
|
@@ -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<Item> {
|
||||||
|
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<FeatureID> {
|
||||||
|
return observableOf(FeatureID.CanManageRelationships);
|
||||||
|
}
|
||||||
|
}
|
32
src/app/+item-page/edit-item-page/item-page-status.guard.ts
Normal file
32
src/app/+item-page/edit-item-page/item-page-status.guard.ts
Normal file
@@ -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<Item> {
|
||||||
|
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<FeatureID[]> {
|
||||||
|
return observableOf([FeatureID.CanManageMappings, FeatureID.WithdrawItem, FeatureID.ReinstateItem, FeatureID.CanManagePolicies, FeatureID.CanMakePrivate, FeatureID.CanDelete, FeatureID.CanMove]);
|
||||||
|
}
|
||||||
|
}
|
@@ -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<Item> {
|
||||||
|
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<FeatureID> {
|
||||||
|
return observableOf(FeatureID.CanManageVersions);
|
||||||
|
}
|
||||||
|
}
|
@@ -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 { Item } from '../../core/shared/item.model';
|
||||||
import { Injectable } from '@angular/core';
|
import { Injectable } from '@angular/core';
|
||||||
import { ItemPageResolver } from '../item-page.resolver';
|
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
|
* Guard for preventing unauthorized access to certain {@link Item} pages requiring withdraw rights
|
||||||
*/
|
*/
|
||||||
export class ItemPageWithdrawGuard extends DsoPageFeatureGuard<Item> {
|
export class ItemPageWithdrawGuard extends DsoPageSingleFeatureGuard<Item> {
|
||||||
constructor(protected resolver: ItemPageResolver,
|
constructor(protected resolver: ItemPageResolver,
|
||||||
protected authorizationService: AuthorizationDataService,
|
protected authorizationService: AuthorizationDataService,
|
||||||
protected router: Router,
|
protected router: Router,
|
||||||
|
@@ -3,8 +3,8 @@ import { fadeIn, fadeInOut } from '../../../shared/animations/fade';
|
|||||||
import { Item } from '../../../core/shared/item.model';
|
import { Item } from '../../../core/shared/item.model';
|
||||||
import { ActivatedRoute } from '@angular/router';
|
import { ActivatedRoute } from '@angular/router';
|
||||||
import { ItemOperation } from '../item-operation/itemOperation.model';
|
import { ItemOperation } from '../item-operation/itemOperation.model';
|
||||||
import { distinctUntilChanged, first, map } from 'rxjs/operators';
|
import { distinctUntilChanged, first, map, mergeMap, toArray } from 'rxjs/operators';
|
||||||
import { BehaviorSubject, Observable } from 'rxjs';
|
import { BehaviorSubject, Observable, from as observableFrom } from 'rxjs';
|
||||||
import { RemoteData } from '../../../core/data/remote-data';
|
import { RemoteData } from '../../../core/data/remote-data';
|
||||||
import { getItemEditRoute, getItemPageRoute } from '../../item-page-routing-paths';
|
import { getItemEditRoute, getItemPageRoute } from '../../item-page-routing-paths';
|
||||||
import { AuthorizationDataService } from '../../../core/data/feature-authorization/authorization-data.service';
|
import { AuthorizationDataService } from '../../../core/data/feature-authorization/authorization-data.service';
|
||||||
@@ -78,42 +78,36 @@ export class ItemStatusComponent implements OnInit {
|
|||||||
The value is supposed to be a href for the button
|
The value is supposed to be a href for the button
|
||||||
*/
|
*/
|
||||||
const operations = [];
|
const operations = [];
|
||||||
operations.push(new ItemOperation('authorizations', this.getCurrentUrl(item) + '/authorizations'));
|
operations.push(new ItemOperation('authorizations', this.getCurrentUrl(item) + '/authorizations', FeatureID.CanManagePolicies, true));
|
||||||
operations.push(new ItemOperation('mappedCollections', this.getCurrentUrl(item) + '/mapper'));
|
operations.push(new ItemOperation('mappedCollections', this.getCurrentUrl(item) + '/mapper', FeatureID.CanManageMappings, true));
|
||||||
operations.push(undefined);
|
if (item.isWithdrawn) {
|
||||||
// Store the index of the "withdraw" or "reinstate" operation, because it's added asynchronously
|
operations.push(new ItemOperation('reinstate', this.getCurrentUrl(item) + '/reinstate', FeatureID.ReinstateItem, true));
|
||||||
const indexOfWithdrawReinstate = operations.length - 1;
|
|
||||||
if (item.isDiscoverable) {
|
|
||||||
operations.push(new ItemOperation('private', this.getCurrentUrl(item) + '/private'));
|
|
||||||
} else {
|
} else {
|
||||||
operations.push(new ItemOperation('public', this.getCurrentUrl(item) + '/public'));
|
operations.push(new ItemOperation('withdraw', this.getCurrentUrl(item) + '/withdraw', FeatureID.WithdrawItem, true));
|
||||||
}
|
}
|
||||||
operations.push(new ItemOperation('delete', this.getCurrentUrl(item) + '/delete'));
|
if (item.isDiscoverable) {
|
||||||
operations.push(new ItemOperation('move', this.getCurrentUrl(item) + '/move'));
|
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);
|
this.operations$.next(operations);
|
||||||
|
|
||||||
if (item.isWithdrawn) {
|
observableFrom(operations).pipe(
|
||||||
this.authorizationService.isAuthorized(FeatureID.ReinstateItem, item.self).pipe(distinctUntilChanged()).subscribe((authorized) => {
|
mergeMap((operation) => {
|
||||||
const newOperations = [...this.operations$.value];
|
if (hasValue(operation.featureID)) {
|
||||||
if (authorized) {
|
return this.authorizationService.isAuthorized(operation.featureID, item.self).pipe(
|
||||||
newOperations[indexOfWithdrawReinstate] = new ItemOperation('reinstate', this.getCurrentUrl(item) + '/reinstate');
|
distinctUntilChanged(),
|
||||||
|
map((authorized) => new ItemOperation(operation.operationKey, operation.operationUrl, operation.featureID, !authorized, authorized))
|
||||||
|
);
|
||||||
} else {
|
} else {
|
||||||
newOperations[indexOfWithdrawReinstate] = undefined;
|
return [operation];
|
||||||
}
|
}
|
||||||
this.operations$.next(newOperations);
|
}),
|
||||||
});
|
toArray()
|
||||||
} else {
|
).subscribe((ops) => this.operations$.next(ops));
|
||||||
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);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
});
|
});
|
||||||
this.itemPageRoute$ = this.itemRD$.pipe(
|
this.itemPageRoute$ = this.itemRD$.pipe(
|
||||||
getAllSucceededRemoteDataPayload(),
|
getAllSucceededRemoteDataPayload(),
|
||||||
|
@@ -3,7 +3,7 @@ import { ActivatedRouteSnapshot, Router, RouterStateSnapshot } from '@angular/ro
|
|||||||
import { AuthorizationDataService } from '../core/data/feature-authorization/authorization-data.service';
|
import { AuthorizationDataService } from '../core/data/feature-authorization/authorization-data.service';
|
||||||
import { ItemPageResolver } from './item-page.resolver';
|
import { ItemPageResolver } from './item-page.resolver';
|
||||||
import { Item } from '../core/shared/item.model';
|
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 { Observable, of as observableOf } from 'rxjs';
|
||||||
import { FeatureID } from '../core/data/feature-authorization/feature-id';
|
import { FeatureID } from '../core/data/feature-authorization/feature-id';
|
||||||
import { AuthService } from '../core/auth/auth.service';
|
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
|
* Guard for preventing unauthorized access to certain {@link Item} pages requiring administrator rights
|
||||||
*/
|
*/
|
||||||
export class ItemPageAdministratorGuard extends DsoPageFeatureGuard<Item> {
|
export class ItemPageAdministratorGuard extends DsoPageSingleFeatureGuard<Item> {
|
||||||
constructor(protected resolver: ItemPageResolver,
|
constructor(protected resolver: ItemPageResolver,
|
||||||
protected authorizationService: AuthorizationDataService,
|
protected authorizationService: AuthorizationDataService,
|
||||||
protected router: Router,
|
protected router: Router,
|
||||||
|
@@ -1,5 +1,5 @@
|
|||||||
import { Injectable } from '@angular/core';
|
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 { AuthorizationDataService } from '../authorization-data.service';
|
||||||
import { ActivatedRouteSnapshot, Router, RouterStateSnapshot } from '@angular/router';
|
import { ActivatedRouteSnapshot, Router, RouterStateSnapshot } from '@angular/router';
|
||||||
import { AuthService } from '../../../auth/auth.service';
|
import { AuthService } from '../../../auth/auth.service';
|
||||||
@@ -13,7 +13,7 @@ import { FeatureID } from '../feature-id';
|
|||||||
@Injectable({
|
@Injectable({
|
||||||
providedIn: 'root'
|
providedIn: 'root'
|
||||||
})
|
})
|
||||||
export class CollectionAdministratorGuard extends FeatureAuthorizationGuard {
|
export class CollectionAdministratorGuard extends SingleFeatureAuthorizationGuard {
|
||||||
constructor(protected authorizationService: AuthorizationDataService, protected router: Router, protected authService: AuthService) {
|
constructor(protected authorizationService: AuthorizationDataService, protected router: Router, protected authService: AuthService) {
|
||||||
super(authorizationService, router, authService);
|
super(authorizationService, router, authService);
|
||||||
}
|
}
|
||||||
|
@@ -1,5 +1,5 @@
|
|||||||
import { Injectable } from '@angular/core';
|
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 { AuthorizationDataService } from '../authorization-data.service';
|
||||||
import { ActivatedRouteSnapshot, Router, RouterStateSnapshot } from '@angular/router';
|
import { ActivatedRouteSnapshot, Router, RouterStateSnapshot } from '@angular/router';
|
||||||
import { AuthService } from '../../../auth/auth.service';
|
import { AuthService } from '../../../auth/auth.service';
|
||||||
@@ -13,7 +13,7 @@ import { FeatureID } from '../feature-id';
|
|||||||
@Injectable({
|
@Injectable({
|
||||||
providedIn: 'root'
|
providedIn: 'root'
|
||||||
})
|
})
|
||||||
export class CommunityAdministratorGuard extends FeatureAuthorizationGuard {
|
export class CommunityAdministratorGuard extends SingleFeatureAuthorizationGuard {
|
||||||
constructor(protected authorizationService: AuthorizationDataService, protected router: Router, protected authService: AuthService) {
|
constructor(protected authorizationService: AuthorizationDataService, protected router: Router, protected authService: AuthService) {
|
||||||
super(authorizationService, router, authService);
|
super(authorizationService, router, authService);
|
||||||
}
|
}
|
||||||
|
@@ -4,14 +4,14 @@ import { RemoteData } from '../../remote-data';
|
|||||||
import { Observable, of as observableOf } from 'rxjs';
|
import { Observable, of as observableOf } from 'rxjs';
|
||||||
import { createSuccessfulRemoteDataObject$ } from '../../../../shared/remote-data.utils';
|
import { createSuccessfulRemoteDataObject$ } from '../../../../shared/remote-data.utils';
|
||||||
import { DSpaceObject } from '../../../shared/dspace-object.model';
|
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 { FeatureID } from '../feature-id';
|
||||||
import { AuthService } from '../../../auth/auth.service';
|
import { AuthService } from '../../../auth/auth.service';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Test implementation of abstract class DsoPageAdministratorGuard
|
* Test implementation of abstract class DsoPageSingleFeatureGuard
|
||||||
*/
|
*/
|
||||||
class DsoPageFeatureGuardImpl extends DsoPageFeatureGuard<any> {
|
class DsoPageSingleFeatureGuardImpl extends DsoPageSingleFeatureGuard<any> {
|
||||||
constructor(protected resolver: Resolve<RemoteData<any>>,
|
constructor(protected resolver: Resolve<RemoteData<any>>,
|
||||||
protected authorizationService: AuthorizationDataService,
|
protected authorizationService: AuthorizationDataService,
|
||||||
protected router: Router,
|
protected router: Router,
|
||||||
@@ -25,8 +25,8 @@ class DsoPageFeatureGuardImpl extends DsoPageFeatureGuard<any> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
describe('DsoPageAdministratorGuard', () => {
|
describe('DsoPageSingleFeatureGuard', () => {
|
||||||
let guard: DsoPageFeatureGuard<any>;
|
let guard: DsoPageSingleFeatureGuard<any>;
|
||||||
let authorizationService: AuthorizationDataService;
|
let authorizationService: AuthorizationDataService;
|
||||||
let router: Router;
|
let router: Router;
|
||||||
let authService: AuthService;
|
let authService: AuthService;
|
||||||
@@ -62,7 +62,7 @@ describe('DsoPageAdministratorGuard', () => {
|
|||||||
},
|
},
|
||||||
parent: parentRoute
|
parent: parentRoute
|
||||||
};
|
};
|
||||||
guard = new DsoPageFeatureGuardImpl(resolver, authorizationService, router, authService, undefined);
|
guard = new DsoPageSingleFeatureGuardImpl(resolver, authorizationService, router, authService, undefined);
|
||||||
}
|
}
|
||||||
|
|
||||||
beforeEach(() => {
|
beforeEach(() => {
|
@@ -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<T extends DSpaceObject> extends DsoPageSomeFeatureGuard<T> {
|
||||||
|
/**
|
||||||
|
* The features to check authorization for
|
||||||
|
*/
|
||||||
|
getFeatureIDs(route: ActivatedRouteSnapshot, state: RouterStateSnapshot): Observable<FeatureID[]> {
|
||||||
|
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<FeatureID>;
|
||||||
|
}
|
@@ -0,0 +1,87 @@
|
|||||||
|
import { AuthorizationDataService } from '../authorization-data.service';
|
||||||
|
import { ActivatedRouteSnapshot, Resolve, Router, RouterStateSnapshot } from '@angular/router';
|
||||||
|
import { RemoteData } from '../../remote-data';
|
||||||
|
import { Observable, of as observableOf } from 'rxjs';
|
||||||
|
import { createSuccessfulRemoteDataObject$ } from '../../../../shared/remote-data.utils';
|
||||||
|
import { DSpaceObject } from '../../../shared/dspace-object.model';
|
||||||
|
import { FeatureID } from '../feature-id';
|
||||||
|
import { AuthService } from '../../../auth/auth.service';
|
||||||
|
import { DsoPageSomeFeatureGuard } from './dso-page-some-feature.guard';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Test implementation of abstract class DsoPageSomeFeatureGuard
|
||||||
|
*/
|
||||||
|
class DsoPageSomeFeatureGuardImpl extends DsoPageSomeFeatureGuard<any> {
|
||||||
|
constructor(protected resolver: Resolve<RemoteData<any>>,
|
||||||
|
protected authorizationService: AuthorizationDataService,
|
||||||
|
protected router: Router,
|
||||||
|
protected authService: AuthService,
|
||||||
|
protected featureIDs: FeatureID[]) {
|
||||||
|
super(resolver, authorizationService, router, authService);
|
||||||
|
}
|
||||||
|
|
||||||
|
getFeatureIDs(route: ActivatedRouteSnapshot, state: RouterStateSnapshot): Observable<FeatureID[]> {
|
||||||
|
return observableOf(this.featureIDs);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
describe('DsoPageSomeFeatureGuard', () => {
|
||||||
|
let guard: DsoPageSomeFeatureGuard<any>;
|
||||||
|
let authorizationService: AuthorizationDataService;
|
||||||
|
let router: Router;
|
||||||
|
let authService: AuthService;
|
||||||
|
let resolver: Resolve<RemoteData<any>>;
|
||||||
|
let object: DSpaceObject;
|
||||||
|
let route;
|
||||||
|
let parentRoute;
|
||||||
|
|
||||||
|
function init() {
|
||||||
|
object = {
|
||||||
|
self: 'test-selflink'
|
||||||
|
} as DSpaceObject;
|
||||||
|
|
||||||
|
authorizationService = jasmine.createSpyObj('authorizationService', {
|
||||||
|
isAuthorized: observableOf(true)
|
||||||
|
});
|
||||||
|
router = jasmine.createSpyObj('router', {
|
||||||
|
parseUrl: {}
|
||||||
|
});
|
||||||
|
resolver = jasmine.createSpyObj('resolver', {
|
||||||
|
resolve: createSuccessfulRemoteDataObject$(object)
|
||||||
|
});
|
||||||
|
authService = jasmine.createSpyObj('authService', {
|
||||||
|
isAuthenticated: observableOf(true)
|
||||||
|
});
|
||||||
|
parentRoute = {
|
||||||
|
params: {
|
||||||
|
id: '3e1a5327-dabb-41ff-af93-e6cab9d032f0'
|
||||||
|
}
|
||||||
|
};
|
||||||
|
route = {
|
||||||
|
params: {
|
||||||
|
},
|
||||||
|
parent: parentRoute
|
||||||
|
};
|
||||||
|
guard = new DsoPageSomeFeatureGuardImpl(resolver, authorizationService, router, authService, []);
|
||||||
|
}
|
||||||
|
|
||||||
|
beforeEach(() => {
|
||||||
|
init();
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('getObjectUrl', () => {
|
||||||
|
it('should return the resolved object\'s selflink', (done) => {
|
||||||
|
guard.getObjectUrl(route, undefined).subscribe((selflink) => {
|
||||||
|
expect(selflink).toEqual(object.self);
|
||||||
|
done();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('getRouteWithDSOId', () => {
|
||||||
|
it('should return the route that has the UUID of the DSO', () => {
|
||||||
|
const foundRoute = (guard as any).getRouteWithDSOId(route);
|
||||||
|
expect(foundRoute).toBe(parentRoute);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
@@ -5,15 +5,15 @@ import { Observable } from 'rxjs';
|
|||||||
import { getAllSucceededRemoteDataPayload } from '../../../shared/operators';
|
import { getAllSucceededRemoteDataPayload } from '../../../shared/operators';
|
||||||
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 { FeatureAuthorizationGuard } from './feature-authorization.guard';
|
|
||||||
import { AuthService } from '../../../auth/auth.service';
|
import { AuthService } from '../../../auth/auth.service';
|
||||||
import { hasNoValue, hasValue } from '../../../../shared/empty.util';
|
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
|
* Abstract Guard for preventing unauthorized access to {@link DSpaceObject} pages that require rights for any specific feature in a list
|
||||||
* This guard utilizes a resolver to retrieve the relevant object to check authorizations for
|
* This guard utilizes a resolver to retrieve the relevant object to check authorizations for
|
||||||
*/
|
*/
|
||||||
export abstract class DsoPageFeatureGuard<T extends DSpaceObject> extends FeatureAuthorizationGuard {
|
export abstract class DsoPageSomeFeatureGuard<T extends DSpaceObject> extends SomeFeatureAuthorizationGuard {
|
||||||
constructor(protected resolver: Resolve<RemoteData<T>>,
|
constructor(protected resolver: Resolve<RemoteData<T>>,
|
||||||
protected authorizationService: AuthorizationDataService,
|
protected authorizationService: AuthorizationDataService,
|
||||||
protected router: Router,
|
protected router: Router,
|
@@ -1,5 +1,5 @@
|
|||||||
import { Injectable } from '@angular/core';
|
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 { AuthorizationDataService } from '../authorization-data.service';
|
||||||
import { ActivatedRouteSnapshot, Router, RouterStateSnapshot } from '@angular/router';
|
import { ActivatedRouteSnapshot, Router, RouterStateSnapshot } from '@angular/router';
|
||||||
import { AuthService } from '../../../auth/auth.service';
|
import { AuthService } from '../../../auth/auth.service';
|
||||||
@@ -13,7 +13,7 @@ import { FeatureID } from '../feature-id';
|
|||||||
@Injectable({
|
@Injectable({
|
||||||
providedIn: 'root'
|
providedIn: 'root'
|
||||||
})
|
})
|
||||||
export class GroupAdministratorGuard extends FeatureAuthorizationGuard {
|
export class GroupAdministratorGuard extends SingleFeatureAuthorizationGuard {
|
||||||
constructor(protected authorizationService: AuthorizationDataService, protected router: Router, protected authService: AuthService) {
|
constructor(protected authorizationService: AuthorizationDataService, protected router: Router, protected authService: AuthService) {
|
||||||
super(authorizationService, router, authService);
|
super(authorizationService, router, authService);
|
||||||
}
|
}
|
||||||
|
@@ -1,4 +1,4 @@
|
|||||||
import { FeatureAuthorizationGuard } from './feature-authorization.guard';
|
import { SingleFeatureAuthorizationGuard } from './single-feature-authorization.guard';
|
||||||
import { AuthorizationDataService } from '../authorization-data.service';
|
import { AuthorizationDataService } from '../authorization-data.service';
|
||||||
import { FeatureID } from '../feature-id';
|
import { FeatureID } from '../feature-id';
|
||||||
import { Observable, of as observableOf } from 'rxjs';
|
import { Observable, of as observableOf } from 'rxjs';
|
||||||
@@ -6,10 +6,10 @@ import { ActivatedRouteSnapshot, Router, RouterStateSnapshot } from '@angular/ro
|
|||||||
import { AuthService } from '../../../auth/auth.service';
|
import { AuthService } from '../../../auth/auth.service';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Test implementation of abstract class FeatureAuthorizationGuard
|
* Test implementation of abstract class SingleFeatureAuthorizationGuard
|
||||||
* Provide the return values of the overwritten getters as constructor arguments
|
* Provide the return values of the overwritten getters as constructor arguments
|
||||||
*/
|
*/
|
||||||
class FeatureAuthorizationGuardImpl extends FeatureAuthorizationGuard {
|
class SingleFeatureAuthorizationGuardImpl extends SingleFeatureAuthorizationGuard {
|
||||||
constructor(protected authorizationService: AuthorizationDataService,
|
constructor(protected authorizationService: AuthorizationDataService,
|
||||||
protected router: Router,
|
protected router: Router,
|
||||||
protected authService: AuthService,
|
protected authService: AuthService,
|
||||||
@@ -32,8 +32,8 @@ class FeatureAuthorizationGuardImpl extends FeatureAuthorizationGuard {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
describe('FeatureAuthorizationGuard', () => {
|
describe('SingleFeatureAuthorizationGuard', () => {
|
||||||
let guard: FeatureAuthorizationGuard;
|
let guard: SingleFeatureAuthorizationGuard;
|
||||||
let authorizationService: AuthorizationDataService;
|
let authorizationService: AuthorizationDataService;
|
||||||
let router: Router;
|
let router: Router;
|
||||||
let authService: AuthService;
|
let authService: AuthService;
|
||||||
@@ -56,7 +56,7 @@ describe('FeatureAuthorizationGuard', () => {
|
|||||||
authService = jasmine.createSpyObj('authService', {
|
authService = jasmine.createSpyObj('authService', {
|
||||||
isAuthenticated: observableOf(true)
|
isAuthenticated: observableOf(true)
|
||||||
});
|
});
|
||||||
guard = new FeatureAuthorizationGuardImpl(authorizationService, router, authService, featureId, objectUrl, ePersonUuid);
|
guard = new SingleFeatureAuthorizationGuardImpl(authorizationService, router, authService, featureId, objectUrl, ePersonUuid);
|
||||||
}
|
}
|
||||||
|
|
||||||
beforeEach(() => {
|
beforeEach(() => {
|
@@ -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<FeatureID[]> {
|
||||||
|
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<FeatureID>;
|
||||||
|
}
|
@@ -1,5 +1,5 @@
|
|||||||
import { Injectable } from '@angular/core';
|
import { Injectable } from '@angular/core';
|
||||||
import { FeatureAuthorizationGuard } from './feature-authorization.guard';
|
import { SingleFeatureAuthorizationGuard } from './single-feature-authorization.guard';
|
||||||
import { FeatureID } from '../feature-id';
|
import { FeatureID } from '../feature-id';
|
||||||
import { AuthorizationDataService } from '../authorization-data.service';
|
import { AuthorizationDataService } from '../authorization-data.service';
|
||||||
import { ActivatedRouteSnapshot, Router, RouterStateSnapshot } from '@angular/router';
|
import { ActivatedRouteSnapshot, Router, RouterStateSnapshot } from '@angular/router';
|
||||||
@@ -13,7 +13,7 @@ import { AuthService } from '../../../auth/auth.service';
|
|||||||
@Injectable({
|
@Injectable({
|
||||||
providedIn: 'root'
|
providedIn: 'root'
|
||||||
})
|
})
|
||||||
export class SiteAdministratorGuard extends FeatureAuthorizationGuard {
|
export class SiteAdministratorGuard extends SingleFeatureAuthorizationGuard {
|
||||||
constructor(protected authorizationService: AuthorizationDataService, protected router: Router, protected authService: AuthService) {
|
constructor(protected authorizationService: AuthorizationDataService, protected router: Router, protected authService: AuthService) {
|
||||||
super(authorizationService, router, authService);
|
super(authorizationService, router, authService);
|
||||||
}
|
}
|
||||||
|
@@ -1,4 +1,4 @@
|
|||||||
import { FeatureAuthorizationGuard } from './feature-authorization.guard';
|
import { SingleFeatureAuthorizationGuard } from './single-feature-authorization.guard';
|
||||||
import { Injectable } from '@angular/core';
|
import { Injectable } from '@angular/core';
|
||||||
import { AuthorizationDataService } from '../authorization-data.service';
|
import { AuthorizationDataService } from '../authorization-data.service';
|
||||||
import { ActivatedRouteSnapshot, Router, RouterStateSnapshot } from '@angular/router';
|
import { ActivatedRouteSnapshot, Router, RouterStateSnapshot } from '@angular/router';
|
||||||
@@ -13,7 +13,7 @@ import { AuthService } from '../../../auth/auth.service';
|
|||||||
@Injectable({
|
@Injectable({
|
||||||
providedIn: 'root'
|
providedIn: 'root'
|
||||||
})
|
})
|
||||||
export class SiteRegisterGuard extends FeatureAuthorizationGuard {
|
export class SiteRegisterGuard extends SingleFeatureAuthorizationGuard {
|
||||||
constructor(protected authorizationService: AuthorizationDataService, protected router: Router, protected authService: AuthService) {
|
constructor(protected authorizationService: AuthorizationDataService, protected router: Router, protected authService: AuthService) {
|
||||||
super(authorizationService, router, authService);
|
super(authorizationService, router, authService);
|
||||||
}
|
}
|
||||||
|
@@ -0,0 +1,110 @@
|
|||||||
|
import { AuthorizationDataService } from '../authorization-data.service';
|
||||||
|
import { FeatureID } from '../feature-id';
|
||||||
|
import { Observable, of as observableOf } from 'rxjs';
|
||||||
|
import { ActivatedRouteSnapshot, Router, RouterStateSnapshot } from '@angular/router';
|
||||||
|
import { AuthService } from '../../../auth/auth.service';
|
||||||
|
import { SomeFeatureAuthorizationGuard } from './some-feature-authorization.guard';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Test implementation of abstract class SomeFeatureAuthorizationGuard
|
||||||
|
* Provide the return values of the overwritten getters as constructor arguments
|
||||||
|
*/
|
||||||
|
class SomeFeatureAuthorizationGuardImpl extends SomeFeatureAuthorizationGuard {
|
||||||
|
constructor(protected authorizationService: AuthorizationDataService,
|
||||||
|
protected router: Router,
|
||||||
|
protected authService: AuthService,
|
||||||
|
protected featureIds: FeatureID[],
|
||||||
|
protected objectUrl: string,
|
||||||
|
protected ePersonUuid: string) {
|
||||||
|
super(authorizationService, router, authService);
|
||||||
|
}
|
||||||
|
|
||||||
|
getFeatureIDs(route: ActivatedRouteSnapshot, state: RouterStateSnapshot): Observable<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', () => {
|
||||||
|
let guard: SomeFeatureAuthorizationGuard;
|
||||||
|
let authorizationService: AuthorizationDataService;
|
||||||
|
let router: Router;
|
||||||
|
let authService: AuthService;
|
||||||
|
|
||||||
|
let featureIds: FeatureID[];
|
||||||
|
let authorizedFeatureIds: FeatureID[];
|
||||||
|
let objectUrl: string;
|
||||||
|
let ePersonUuid: string;
|
||||||
|
|
||||||
|
function init() {
|
||||||
|
featureIds = [FeatureID.LoginOnBehalfOf, FeatureID.CanDelete];
|
||||||
|
authorizedFeatureIds = [];
|
||||||
|
objectUrl = 'fake-object-url';
|
||||||
|
ePersonUuid = 'fake-eperson-uuid';
|
||||||
|
|
||||||
|
authorizationService = Object.assign({
|
||||||
|
isAuthorized(featureId?: FeatureID): Observable<boolean> {
|
||||||
|
return observableOf(authorizedFeatureIds.indexOf(featureId) > -1);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
router = jasmine.createSpyObj('router', {
|
||||||
|
parseUrl: {}
|
||||||
|
});
|
||||||
|
authService = jasmine.createSpyObj('authService', {
|
||||||
|
isAuthenticated: observableOf(true)
|
||||||
|
});
|
||||||
|
guard = new SomeFeatureAuthorizationGuardImpl(authorizationService, router, authService, featureIds, objectUrl, ePersonUuid);
|
||||||
|
}
|
||||||
|
|
||||||
|
beforeEach(() => {
|
||||||
|
init();
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('canActivate', () => {
|
||||||
|
describe('when the user isn\'t authorized', () => {
|
||||||
|
beforeEach(() => {
|
||||||
|
authorizedFeatureIds = [];
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should not return true', (done) => {
|
||||||
|
guard.canActivate(undefined, { url: 'current-url' } as any).subscribe((result) => {
|
||||||
|
expect(result).not.toEqual(true);
|
||||||
|
done();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('when the user is authorized for at least one of the guard\'s features', () => {
|
||||||
|
beforeEach(() => {
|
||||||
|
authorizedFeatureIds = [featureIds[0]];
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should return true', (done) => {
|
||||||
|
guard.canActivate(undefined, { url: 'current-url' } as any).subscribe((result) => {
|
||||||
|
expect(result).toEqual(true);
|
||||||
|
done();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('when the user is authorized for all of the guard\'s features', () => {
|
||||||
|
beforeEach(() => {
|
||||||
|
authorizedFeatureIds = featureIds;
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should return true', (done) => {
|
||||||
|
guard.canActivate(undefined, { url: 'current-url' } as any).subscribe((result) => {
|
||||||
|
expect(result).toEqual(true);
|
||||||
|
done();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
@@ -2,16 +2,16 @@ import { ActivatedRouteSnapshot, CanActivate, Router, RouterStateSnapshot, UrlTr
|
|||||||
import { AuthorizationDataService } from '../authorization-data.service';
|
import { AuthorizationDataService } from '../authorization-data.service';
|
||||||
import { FeatureID } from '../feature-id';
|
import { FeatureID } from '../feature-id';
|
||||||
import { combineLatest as observableCombineLatest, Observable, of as observableOf } from 'rxjs';
|
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 { switchMap } from 'rxjs/operators';
|
||||||
import { AuthService } from '../../../auth/auth.service';
|
import { AuthService } from '../../../auth/auth.service';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Abstract Guard for preventing unauthorized activating and loading of routes when a user
|
* 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.
|
* 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 feature 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,
|
constructor(protected authorizationService: AuthorizationDataService,
|
||||||
protected router: Router,
|
protected router: Router,
|
||||||
protected authService: AuthService) {
|
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
|
* Redirect the user to the unauthorized page when he/she's not authorized for the given feature
|
||||||
*/
|
*/
|
||||||
canActivate(route: ActivatedRouteSnapshot, state: RouterStateSnapshot): Observable<boolean | UrlTree> {
|
canActivate(route: ActivatedRouteSnapshot, state: RouterStateSnapshot): Observable<boolean | UrlTree> {
|
||||||
return observableCombineLatest(this.getFeatureID(route, state), this.getObjectUrl(route, state), this.getEPersonUuid(route, state)).pipe(
|
return observableCombineLatest(this.getFeatureIDs(route, state), this.getObjectUrl(route, state), this.getEPersonUuid(route, state)).pipe(
|
||||||
switchMap(([featureID, objectUrl, ePersonUuid]) => this.authorizationService.isAuthorized(featureID, objectUrl, ePersonUuid)),
|
switchMap(([featureIDs, objectUrl, ePersonUuid]) =>
|
||||||
returnForbiddenUrlTreeOrLoginOnFalse(this.router, this.authService, state.url)
|
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
|
* The features to check authorization for
|
||||||
* Override this method to define a feature
|
* Override this method to define a list of features
|
||||||
*/
|
*/
|
||||||
abstract getFeatureID(route: ActivatedRouteSnapshot, state: RouterStateSnapshot): Observable<FeatureID>;
|
abstract getFeatureIDs(route: ActivatedRouteSnapshot, state: RouterStateSnapshot): Observable<FeatureID[]>;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The URL of the object to check if the user has authorized rights for
|
* The URL of the object to check if the user has authorized rights for
|
@@ -13,4 +13,11 @@ export enum FeatureID {
|
|||||||
IsCollectionAdmin = 'isCollectionAdmin',
|
IsCollectionAdmin = 'isCollectionAdmin',
|
||||||
IsCommunityAdmin = 'isCommunityAdmin',
|
IsCommunityAdmin = 'isCommunityAdmin',
|
||||||
CanDownload = 'canDownload',
|
CanDownload = 'canDownload',
|
||||||
|
CanManageVersions = 'canManageVersions',
|
||||||
|
CanManageBitstreamBundles = 'canManageBitstreamBundles',
|
||||||
|
CanManageRelationships = 'canManageRelationships',
|
||||||
|
CanManageMappings = 'canManageMappings',
|
||||||
|
CanManagePolicies = 'canManagePolicies',
|
||||||
|
CanMakePrivate = 'canMakePrivate',
|
||||||
|
CanMove = 'canMove',
|
||||||
}
|
}
|
||||||
|
@@ -207,10 +207,23 @@ export const redirectOn4xx = <T>(router: Router, authService: AuthService) =>
|
|||||||
*/
|
*/
|
||||||
export const returnForbiddenUrlTreeOrLoginOnFalse = (router: Router, authService: AuthService, redirectUrl: string) =>
|
export const returnForbiddenUrlTreeOrLoginOnFalse = (router: Router, authService: AuthService, redirectUrl: string) =>
|
||||||
(source: Observable<boolean>): Observable<boolean | UrlTree> =>
|
(source: Observable<boolean>): Observable<boolean | UrlTree> =>
|
||||||
|
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<boolean[]>): Observable<boolean | UrlTree> =>
|
||||||
observableCombineLatest(source, authService.isAuthenticated()).pipe(
|
observableCombineLatest(source, authService.isAuthenticated()).pipe(
|
||||||
map(([authorized, authenticated]: [boolean, boolean]) => {
|
map(([authorizedList, authenticated]: [boolean[], boolean]) => {
|
||||||
if (authorized) {
|
if (authorizedList.some((b: boolean) => b === true)) {
|
||||||
return authorized;
|
return true;
|
||||||
} else {
|
} else {
|
||||||
if (authenticated) {
|
if (authenticated) {
|
||||||
return router.parseUrl(getForbiddenRoute());
|
return router.parseUrl(getForbiddenRoute());
|
||||||
|
@@ -97,8 +97,14 @@ export class EndpointMockingRestService extends DspaceRestService {
|
|||||||
* the mock response if there is one, undefined otherwise
|
* the mock response if there is one, undefined otherwise
|
||||||
*/
|
*/
|
||||||
private getMockData(urlStr: string): any {
|
private getMockData(urlStr: string): any {
|
||||||
const url = new URL(urlStr);
|
let key;
|
||||||
const key = url.pathname.slice(environment.rest.nameSpace.length);
|
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)) {
|
if (this.mockResponseMap.has(key)) {
|
||||||
// parse and stringify to clone the object to ensure that any changes made
|
// parse and stringify to clone the object to ensure that any changes made
|
||||||
// to it afterwards don't affect future calls
|
// to it afterwards don't affect future calls
|
||||||
|
@@ -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
|
||||||
|
}
|
||||||
|
}
|
@@ -0,0 +1,50 @@
|
|||||||
|
{
|
||||||
|
"_embedded": {
|
||||||
|
"authorizations": [
|
||||||
|
{
|
||||||
|
"id": "cd824a61-95be-4e16-bccd-51fea26707d0_canManageMappings_core.item_e98b0f27-5c19-49a0-960d-eb6ad5287067",
|
||||||
|
"type": "authorization",
|
||||||
|
"_links": {
|
||||||
|
"eperson": {
|
||||||
|
"href": "https://api7.dspace.org/server/api/authz/authorizations/cd824a61-95be-4e16-bccd-51fea26707d0_canManageMappings_core.item_e98b0f27-5c19-49a0-960d-eb6ad5287067/eperson"
|
||||||
|
},
|
||||||
|
"feature": {
|
||||||
|
"href": "https://api7.dspace.org/server/api/authz/authorizations/cd824a61-95be-4e16-bccd-51fea26707d0_canManageMappings_core.item_e98b0f27-5c19-49a0-960d-eb6ad5287067/feature"
|
||||||
|
},
|
||||||
|
"object": {
|
||||||
|
"href": "https://api7.dspace.org/server/api/authz/authorizations/cd824a61-95be-4e16-bccd-51fea26707d0_canManageMappings_core.item_e98b0f27-5c19-49a0-960d-eb6ad5287067/object"
|
||||||
|
},
|
||||||
|
"self": {
|
||||||
|
"href": "https://api7.dspace.org/server/api/authz/authorizations/cd824a61-95be-4e16-bccd-51fea26707d0_canManageMappings_core.item_e98b0f27-5c19-49a0-960d-eb6ad5287067"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"_embedded": {
|
||||||
|
"feature": {
|
||||||
|
"id": "canManageMappings",
|
||||||
|
"description": "It can be used to verify if the mappings of the specified objects can be managed",
|
||||||
|
"type": "feature",
|
||||||
|
"resourcetypes": [
|
||||||
|
"core.item"
|
||||||
|
],
|
||||||
|
"_links": {
|
||||||
|
"self": {
|
||||||
|
"href": "https://api7.dspace.org/server/api/authz/features/canManageMappings"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"_links": {
|
||||||
|
"self": {
|
||||||
|
"href": "https://api7.dspace.org/server/api/authz/authorizations/search/object?uri=https://api7.dspace.org/server/api/core/items/e98b0f27-5c19-49a0-960d-eb6ad5287067&feature=canManageMappings"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"page": {
|
||||||
|
"size": 20,
|
||||||
|
"totalElements": 1,
|
||||||
|
"totalPages": 1,
|
||||||
|
"number": 0
|
||||||
|
}
|
||||||
|
}
|
@@ -0,0 +1,50 @@
|
|||||||
|
{
|
||||||
|
"_embedded": {
|
||||||
|
"authorizations": [
|
||||||
|
{
|
||||||
|
"id": "cd824a61-95be-4e16-bccd-51fea26707d0_canManageRelationships_core.item_047556d1-3d01-4c53-bc68-0cee7ad7ed4e",
|
||||||
|
"type": "authorization",
|
||||||
|
"_links": {
|
||||||
|
"eperson": {
|
||||||
|
"href": "https://api7.dspace.org/server/api/authz/authorizations/cd824a61-95be-4e16-bccd-51fea26707d0_canManageRelationships_core.item_047556d1-3d01-4c53-bc68-0cee7ad7ed4e/eperson"
|
||||||
|
},
|
||||||
|
"feature": {
|
||||||
|
"href": "https://api7.dspace.org/server/api/authz/authorizations/cd824a61-95be-4e16-bccd-51fea26707d0_canManageRelationships_core.item_047556d1-3d01-4c53-bc68-0cee7ad7ed4e/feature"
|
||||||
|
},
|
||||||
|
"object": {
|
||||||
|
"href": "https://api7.dspace.org/server/api/authz/authorizations/cd824a61-95be-4e16-bccd-51fea26707d0_canManageRelationships_core.item_047556d1-3d01-4c53-bc68-0cee7ad7ed4e/object"
|
||||||
|
},
|
||||||
|
"self": {
|
||||||
|
"href": "https://api7.dspace.org/server/api/authz/authorizations/cd824a61-95be-4e16-bccd-51fea26707d0_canManageRelationships_core.item_047556d1-3d01-4c53-bc68-0cee7ad7ed4e"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"_embedded": {
|
||||||
|
"feature": {
|
||||||
|
"id": "canManageRelationships",
|
||||||
|
"description": "It can be used to verify if the relationships of the specified objects can be managed",
|
||||||
|
"type": "feature",
|
||||||
|
"resourcetypes": [
|
||||||
|
"core.item"
|
||||||
|
],
|
||||||
|
"_links": {
|
||||||
|
"self": {
|
||||||
|
"href": "https://api7.dspace.org/server/api/authz/features/canManageRelationships"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"_links": {
|
||||||
|
"self": {
|
||||||
|
"href": "https://api7.dspace.org/server/api/authz/authorizations/search/object?uri=https://api7.dspace.org/server/api/core/items/047556d1-3d01-4c53-bc68-0cee7ad7ed4e&feature=canManageRelationships"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"page": {
|
||||||
|
"size": 20,
|
||||||
|
"totalElements": 1,
|
||||||
|
"totalPages": 1,
|
||||||
|
"number": 0
|
||||||
|
}
|
||||||
|
}
|
@@ -0,0 +1,50 @@
|
|||||||
|
{
|
||||||
|
"_embedded": {
|
||||||
|
"authorizations": [
|
||||||
|
{
|
||||||
|
"id": "cd824a61-95be-4e16-bccd-51fea26707d0_canManageVersions_core.item_e98b0f27-5c19-49a0-960d-eb6ad5287067",
|
||||||
|
"type": "authorization",
|
||||||
|
"_links": {
|
||||||
|
"eperson": {
|
||||||
|
"href": "https://api7.dspace.org/server/api/authz/authorizations/cd824a61-95be-4e16-bccd-51fea26707d0_canManageVersions_core.item_e98b0f27-5c19-49a0-960d-eb6ad5287067/eperson"
|
||||||
|
},
|
||||||
|
"feature": {
|
||||||
|
"href": "https://api7.dspace.org/server/api/authz/authorizations/cd824a61-95be-4e16-bccd-51fea26707d0_canManageVersions_core.item_e98b0f27-5c19-49a0-960d-eb6ad5287067/feature"
|
||||||
|
},
|
||||||
|
"object": {
|
||||||
|
"href": "https://api7.dspace.org/server/api/authz/authorizations/cd824a61-95be-4e16-bccd-51fea26707d0_canManageVersions_core.item_e98b0f27-5c19-49a0-960d-eb6ad5287067/object"
|
||||||
|
},
|
||||||
|
"self": {
|
||||||
|
"href": "https://api7.dspace.org/server/api/authz/authorizations/cd824a61-95be-4e16-bccd-51fea26707d0_canManageVersions_core.item_e98b0f27-5c19-49a0-960d-eb6ad5287067"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"_embedded": {
|
||||||
|
"feature": {
|
||||||
|
"id": "canManageVersions",
|
||||||
|
"description": "It can be used to verify if the versions of the specified objects can be managed",
|
||||||
|
"type": "feature",
|
||||||
|
"resourcetypes": [
|
||||||
|
"core.item"
|
||||||
|
],
|
||||||
|
"_links": {
|
||||||
|
"self": {
|
||||||
|
"href": "https://api7.dspace.org/server/api/authz/features/canManageVersions"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"_links": {
|
||||||
|
"self": {
|
||||||
|
"href": "https://api7.dspace.org/server/api/authz/authorizations/search/object?uri=https://api7.dspace.org/server/api/core/items/e98b0f27-5c19-49a0-960d-eb6ad5287067&feature=canManageVersions"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"page": {
|
||||||
|
"size": 20,
|
||||||
|
"totalElements": 1,
|
||||||
|
"totalPages": 1,
|
||||||
|
"number": 0
|
||||||
|
}
|
||||||
|
}
|
@@ -2,6 +2,10 @@ import { InjectionToken } from '@angular/core';
|
|||||||
// import mockSubmissionResponse from './mock-submission-response.json';
|
// import mockSubmissionResponse from './mock-submission-response.json';
|
||||||
// import mockPublicationResponse from './mock-publication-response.json';
|
// import mockPublicationResponse from './mock-publication-response.json';
|
||||||
// import mockUntypedItemResponse from './mock-untyped-item-response.json';
|
// import mockUntypedItemResponse from './mock-untyped-item-response.json';
|
||||||
|
import mockFeatureItemCanManageBitstreamsResponse from './mock-feature-item-can-manage-bitstreams-response.json';
|
||||||
|
import mockFeatureItemCanManageRelationshipsResponse from './mock-feature-item-can-manage-relationships-response.json';
|
||||||
|
import mockFeatureItemCanManageVersionsResponse from './mock-feature-item-can-manage-versions-response.json';
|
||||||
|
import mockFeatureItemCanManageMappingsResponse from './mock-feature-item-can-manage-mappings-response.json';
|
||||||
|
|
||||||
export class ResponseMapMock extends Map<string, any> {}
|
export class ResponseMapMock extends Map<string, any> {}
|
||||||
|
|
||||||
@@ -16,4 +20,8 @@ export const mockResponseMap: ResponseMapMock = new Map([
|
|||||||
// [ '/config/submissionforms/traditionalpageone', mockSubmissionResponse ]
|
// [ '/config/submissionforms/traditionalpageone', mockSubmissionResponse ]
|
||||||
// [ '/api/pid/find', mockPublicationResponse ],
|
// [ '/api/pid/find', mockPublicationResponse ],
|
||||||
// [ '/api/pid/find', mockUntypedItemResponse ],
|
// [ '/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 ],
|
||||||
|
[ 'https://api7.dspace.org/server/api/authz/authorizations/search/object?uri=https://api7.dspace.org/server/api/core/items/047556d1-3d01-4c53-bc68-0cee7ad7ed4e&feature=canManageRelationships&embed=feature', mockFeatureItemCanManageRelationshipsResponse ],
|
||||||
|
[ 'https://api7.dspace.org/server/api/authz/authorizations/search/object?uri=https://api7.dspace.org/server/api/core/items/e98b0f27-5c19-49a0-960d-eb6ad5287067&feature=canManageVersions&embed=feature', mockFeatureItemCanManageVersionsResponse ],
|
||||||
|
[ 'https://api7.dspace.org/server/api/authz/authorizations/search/object?uri=https://api7.dspace.org/server/api/core/items/e98b0f27-5c19-49a0-960d-eb6ad5287067&feature=canManageMappings&embed=feature', mockFeatureItemCanManageMappingsResponse ],
|
||||||
]);
|
]);
|
||||||
|
@@ -1520,7 +1520,7 @@
|
|||||||
|
|
||||||
"item.edit.breadcrumbs": "Edit Item",
|
"item.edit.breadcrumbs": "Edit Item",
|
||||||
|
|
||||||
"item.edit.tabs.disabled.tooltip": "You don't have permission to access this tab",
|
"item.edit.tabs.disabled.tooltip": "You're not authorized to access this tab",
|
||||||
|
|
||||||
|
|
||||||
"item.edit.tabs.mapper.head": "Collection Mapper",
|
"item.edit.tabs.mapper.head": "Collection Mapper",
|
||||||
@@ -1765,6 +1765,8 @@
|
|||||||
|
|
||||||
"item.edit.tabs.status.buttons.reinstate.label": "Reinstate item into the repository",
|
"item.edit.tabs.status.buttons.reinstate.label": "Reinstate item into the repository",
|
||||||
|
|
||||||
|
"item.edit.tabs.status.buttons.unauthorized": "You're not authorized to perform this action",
|
||||||
|
|
||||||
"item.edit.tabs.status.buttons.withdraw.button": "Withdraw...",
|
"item.edit.tabs.status.buttons.withdraw.button": "Withdraw...",
|
||||||
|
|
||||||
"item.edit.tabs.status.buttons.withdraw.label": "Withdraw item from the repository",
|
"item.edit.tabs.status.buttons.withdraw.label": "Withdraw item from the repository",
|
||||||
|
Reference in New Issue
Block a user