121534: Removed unauthorized metadata-export-search request on search page for non-admins

This commit is contained in:
Alexandre Vryghem
2024-11-24 22:42:05 +01:00
parent 404ccd9b0e
commit 70b855e785
2 changed files with 26 additions and 24 deletions

View File

@@ -6,7 +6,6 @@ import { AuthorizationDataService } from '../../../core/data/feature-authorizati
import { SearchExportCsvComponent } from './search-export-csv.component'; import { SearchExportCsvComponent } from './search-export-csv.component';
import { ScriptDataService } from '../../../core/data/processes/script-data.service'; import { ScriptDataService } from '../../../core/data/processes/script-data.service';
import { createFailedRemoteDataObject$, createSuccessfulRemoteDataObject$ } from '../../remote-data.utils'; import { createFailedRemoteDataObject$, createSuccessfulRemoteDataObject$ } from '../../remote-data.utils';
import { Script } from '../../../process-page/scripts/script.model';
import { Process } from '../../../process-page/processes/process.model'; import { Process } from '../../../process-page/processes/process.model';
import { NotificationsServiceStub } from '../../testing/notifications-service.stub'; import { NotificationsServiceStub } from '../../testing/notifications-service.stub';
import { NotificationsService } from '../../notifications/notifications.service'; import { NotificationsService } from '../../notifications/notifications.service';
@@ -25,7 +24,6 @@ describe('SearchExportCsvComponent', () => {
let notificationsService; let notificationsService;
let router; let router;
const script = Object.assign(new Script(), {id: 'metadata-export-search', name: 'metadata-export-search'});
const process = Object.assign(new Process(), {processId: 5, scriptName: 'metadata-export-search'}); const process = Object.assign(new Process(), {processId: 5, scriptName: 'metadata-export-search'});
const searchConfig = new PaginatedSearchOptions({ const searchConfig = new PaginatedSearchOptions({
@@ -41,7 +39,7 @@ describe('SearchExportCsvComponent', () => {
function initBeforeEachAsync() { function initBeforeEachAsync() {
scriptDataService = jasmine.createSpyObj('scriptDataService', { scriptDataService = jasmine.createSpyObj('scriptDataService', {
findById: createSuccessfulRemoteDataObject$(script), scriptWithNameExistsAndCanExecute: observableOf(true),
invoke: createSuccessfulRemoteDataObject$(process) invoke: createSuccessfulRemoteDataObject$(process)
}); });
authorizationDataService = jasmine.createSpyObj('authorizationService', { authorizationDataService = jasmine.createSpyObj('authorizationService', {
@@ -110,15 +108,22 @@ describe('SearchExportCsvComponent', () => {
describe('when the metadata-export-search script is not present', () => { describe('when the metadata-export-search script is not present', () => {
beforeEach(waitForAsync(() => { beforeEach(waitForAsync(() => {
initBeforeEachAsync(); initBeforeEachAsync();
(scriptDataService.findById as jasmine.Spy).and.returnValue(createFailedRemoteDataObject$('Not found', 404)); (scriptDataService.scriptWithNameExistsAndCanExecute as jasmine.Spy).and.returnValue(observableOf(false));
})); }));
beforeEach(() => {
initBeforeEach();
});
it('should should not add the button', () => { it('should should not add the button', () => {
initBeforeEach();
const debugElement = fixture.debugElement.query(By.css('button.export-button')); const debugElement = fixture.debugElement.query(By.css('button.export-button'));
expect(debugElement).toBeNull(); expect(debugElement).toBeNull();
}); });
it('should not call scriptWithNameExistsAndCanExecute when unauthorized', () => {
(authorizationDataService.isAuthorized as jasmine.Spy).and.returnValue(observableOf(false));
initBeforeEach();
expect(scriptDataService.scriptWithNameExistsAndCanExecute).not.toHaveBeenCalled();
});
}); });
}); });
describe('export', () => { describe('export', () => {

View File

@@ -1,8 +1,8 @@
import { Component, Input, OnInit } from '@angular/core'; import { Component, Input, OnInit } from '@angular/core';
import { combineLatest as observableCombineLatest, Observable } from 'rxjs'; import { Observable } from 'rxjs';
import { ScriptDataService } from '../../../core/data/processes/script-data.service'; import { ScriptDataService } from '../../../core/data/processes/script-data.service';
import { getFirstCompletedRemoteData } from '../../../core/shared/operators'; import { getFirstCompletedRemoteData } from '../../../core/shared/operators';
import { map } from 'rxjs/operators'; import { map, switchMap, filter, startWith } from 'rxjs/operators';
import { FeatureID } from '../../../core/data/feature-authorization/feature-id'; import { FeatureID } from '../../../core/data/feature-authorization/feature-id';
import { AuthorizationDataService } from '../../../core/data/feature-authorization/authorization-data.service'; import { AuthorizationDataService } from '../../../core/data/feature-authorization/authorization-data.service';
import { hasValue, isNotEmpty } from '../../empty.util'; import { hasValue, isNotEmpty } from '../../empty.util';
@@ -13,6 +13,7 @@ import { NotificationsService } from '../../notifications/notifications.service'
import { TranslateService } from '@ngx-translate/core'; import { TranslateService } from '@ngx-translate/core';
import { Router } from '@angular/router'; import { Router } from '@angular/router';
import { PaginatedSearchOptions } from '../models/paginated-search-options.model'; import { PaginatedSearchOptions } from '../models/paginated-search-options.model';
import { SearchFilter } from '../models/search-filter.model';
@Component({ @Component({
selector: 'ds-search-export-csv', selector: 'ds-search-export-csv',
@@ -48,15 +49,11 @@ export class SearchExportCsvComponent implements OnInit {
} }
ngOnInit(): void { ngOnInit(): void {
const scriptExists$ = this.scriptDataService.findById('metadata-export-search').pipe( this.shouldShowButton$ = this.authorizationDataService.isAuthorized(FeatureID.AdministratorOf).pipe(
getFirstCompletedRemoteData(), filter((isAuthorized: boolean) => isAuthorized),
map((rd) => rd.isSuccess && hasValue(rd.payload)) switchMap(() => this.scriptDataService.scriptWithNameExistsAndCanExecute('metadata-export-search')),
); map((canExecute: boolean) => canExecute),
startWith(false),
const isAuthorized$ = this.authorizationDataService.isAuthorized(FeatureID.AdministratorOf);
this.shouldShowButton$ = observableCombineLatest([scriptExists$, isAuthorized$]).pipe(
map(([scriptExists, isAuthorized]: [boolean, boolean]) => scriptExists && isAuthorized)
); );
} }
@@ -76,19 +73,19 @@ export class SearchExportCsvComponent implements OnInit {
parameters.push({name: '-c', value: this.searchConfig.configuration}); parameters.push({name: '-c', value: this.searchConfig.configuration});
} }
if (isNotEmpty(this.searchConfig.filters)) { if (isNotEmpty(this.searchConfig.filters)) {
this.searchConfig.filters.forEach((filter) => { this.searchConfig.filters.forEach((searchFilter: SearchFilter) => {
if (hasValue(filter.values)) { if (hasValue(searchFilter.values)) {
filter.values.forEach((value) => { searchFilter.values.forEach((value: string) => {
let operator; let operator;
let filterValue; let filterValue;
if (hasValue(filter.operator)) { if (hasValue(searchFilter.operator)) {
operator = filter.operator; operator = searchFilter.operator;
filterValue = value; filterValue = value;
} else { } else {
operator = value.substring(value.lastIndexOf(',') + 1); operator = value.substring(value.lastIndexOf(',') + 1);
filterValue = value.substring(0, value.lastIndexOf(',')); filterValue = value.substring(0, value.lastIndexOf(','));
} }
const valueToAdd = `${filter.key.substring(2)},${operator}=${filterValue}`; const valueToAdd = `${searchFilter.key.substring(2)},${operator}=${filterValue}`;
parameters.push({name: '-f', value: valueToAdd}); parameters.push({name: '-f', value: valueToAdd});
}); });
} }