mirror of
https://github.com/Alfresco/alfresco-content-app.git
synced 2026-09-09 18:02:54 +00:00
[ACS-10409] Fix permission checking logic in library navigation (#4824)
* [ACS-10409] Fix permission checking logic in library navigation * [ACS-10409] cr fix
This commit is contained in:
+8
-1
@@ -36,6 +36,7 @@ import { LibraryEffects } from '../../store/effects';
|
||||
import { NodeEntry } from '@alfresco/js-api';
|
||||
import { getTitleElementText } from '../../testing/test-utils';
|
||||
import { MatSnackBarModule } from '@angular/material/snack-bar';
|
||||
import { SiteEntry } from '@alfresco/js-api/typings';
|
||||
|
||||
describe('FavoriteLibrariesComponent', () => {
|
||||
let fixture: ComponentFixture<FavoriteLibrariesComponent>;
|
||||
@@ -126,7 +127,13 @@ describe('FavoriteLibrariesComponent', () => {
|
||||
|
||||
it('does not navigate when id is not passed', () => {
|
||||
spyOn(router, 'navigate').and.stub();
|
||||
component.navigateTo({ entry: { guid: 'guid' } } as any);
|
||||
component.navigateTo({
|
||||
entry: {
|
||||
guid: 'test-guid',
|
||||
visibility: 'PUBLIC',
|
||||
role: 'SiteConsumer'
|
||||
}
|
||||
} as SiteEntry);
|
||||
|
||||
expect(router.navigate).toHaveBeenCalledWith(['favorite/libraries', 'libraryId']);
|
||||
});
|
||||
|
||||
+1
-1
@@ -104,7 +104,7 @@ export class FavoriteLibrariesComponent extends PageComponent implements OnInit
|
||||
|
||||
navigateTo(node: SiteEntry) {
|
||||
if (node?.entry?.guid) {
|
||||
this.store.dispatch(new NavigateLibraryAction(node.entry.guid, 'favorite/libraries'));
|
||||
this.store.dispatch(new NavigateLibraryAction(node.entry, 'favorite/libraries'));
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -89,7 +89,7 @@ export class LibrariesComponent extends PageComponent implements OnInit {
|
||||
|
||||
navigateTo(node: SiteEntry) {
|
||||
if (node?.entry?.guid) {
|
||||
this.store.dispatch(new NavigateLibraryAction(node.entry.guid));
|
||||
this.store.dispatch(new NavigateLibraryAction(node.entry));
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
+1
-1
@@ -168,7 +168,7 @@ export class SearchLibrariesResultsComponent extends PageComponent implements On
|
||||
|
||||
navigateTo(node: SiteEntry) {
|
||||
if (node?.entry?.guid) {
|
||||
this.store.dispatch(new NavigateLibraryAction(node.entry.guid));
|
||||
this.store.dispatch(new NavigateLibraryAction(node.entry));
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -32,7 +32,7 @@ import { NotificationService } from '@alfresco/adf-core';
|
||||
import { provideEffects } from '@ngrx/effects';
|
||||
import { LibraryEffects } from './library.effects';
|
||||
import { AppTestingModule } from '../../testing/app-testing.module';
|
||||
import { NodeEntry } from '@alfresco/js-api';
|
||||
import { NodeEntry, Site } from '@alfresco/js-api';
|
||||
|
||||
describe('LibraryEffects', () => {
|
||||
let store: Store<AppStore>;
|
||||
@@ -48,31 +48,106 @@ describe('LibraryEffects', () => {
|
||||
describe('navigateLibrary$', () => {
|
||||
let notificationService: NotificationService;
|
||||
let node$: Subject<NodeEntry>;
|
||||
let admin$: Subject<boolean>;
|
||||
let site: Site;
|
||||
|
||||
beforeEach(() => {
|
||||
node$ = new Subject<NodeEntry>();
|
||||
admin$ = new Subject<boolean>();
|
||||
site = { guid: 'site-guid', visibility: 'PUBLIC', role: 'SiteConsumer', id: 'site-id', title: 'Title' };
|
||||
spyOn(TestBed.inject(ContentApiService), 'getNode').and.returnValue(node$);
|
||||
notificationService = TestBed.inject(NotificationService);
|
||||
spyOn(notificationService, 'showError');
|
||||
spyOn(store, 'dispatch').and.callThrough();
|
||||
spyOn(store, 'select').and.returnValue(admin$);
|
||||
});
|
||||
|
||||
it('should display library no permission warning if user does not have permission', () => {
|
||||
spyOn(notificationService, 'showWarning');
|
||||
store.dispatch(new NavigateLibraryAction('libraryId'));
|
||||
store.dispatch(new NavigateLibraryAction(site));
|
||||
admin$.next(false);
|
||||
node$.error(new HttpErrorResponse({ status: 403 }));
|
||||
expect(notificationService.showWarning).toHaveBeenCalledWith('APP.BROWSE.LIBRARIES.LIBRARY_NO_PERMISSIONS_WARNING');
|
||||
});
|
||||
|
||||
it('should display library not found error if library does not exist', () => {
|
||||
store.dispatch(new NavigateLibraryAction('libraryId'));
|
||||
store.dispatch(new NavigateLibraryAction(site));
|
||||
admin$.next(false);
|
||||
node$.error(new HttpErrorResponse({ status: 404 }));
|
||||
expect(notificationService.showError).toHaveBeenCalledWith('APP.BROWSE.LIBRARIES.ERRORS.LIBRARY_NOT_FOUND');
|
||||
});
|
||||
|
||||
it('should display generic library loading error if there is different problem than missing permissions or absence of library', () => {
|
||||
store.dispatch(new NavigateLibraryAction('libraryId'));
|
||||
store.dispatch(new NavigateLibraryAction(site));
|
||||
admin$.next(false);
|
||||
node$.error(new HttpErrorResponse({ status: 500 }));
|
||||
expect(notificationService.showError).toHaveBeenCalledWith('APP.BROWSE.LIBRARIES.ERRORS.LIBRARY_LOADING_ERROR');
|
||||
});
|
||||
|
||||
it('should show error when non-admin user without site role tries to access private site', () => {
|
||||
store.dispatch(new NavigateLibraryAction({ ...site, visibility: 'PRIVATE', role: null }));
|
||||
admin$.next(false);
|
||||
|
||||
expect(store.dispatch).not.toHaveBeenCalledWith(
|
||||
jasmine.objectContaining({
|
||||
type: 'NAVIGATE_ROUTE'
|
||||
})
|
||||
);
|
||||
expect(notificationService.showError).toHaveBeenCalledWith('APP.BROWSE.LIBRARIES.LIBRARY_NO_PERMISSIONS_WARNING');
|
||||
});
|
||||
|
||||
it('should allow admin user to navigate to private site without role', () => {
|
||||
store.dispatch(new NavigateLibraryAction({ ...site, visibility: 'PRIVATE', role: null }));
|
||||
admin$.next(true);
|
||||
node$.next({ entry: { id: 'private-doc-lib-id' } } as NodeEntry);
|
||||
|
||||
expect(store.dispatch).toHaveBeenCalledWith(
|
||||
jasmine.objectContaining({
|
||||
type: 'NAVIGATE_ROUTE',
|
||||
payload: ['libraries', 'private-doc-lib-id']
|
||||
})
|
||||
);
|
||||
expect(notificationService.showError).not.toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it('should allow navigation to public site for non-admin user without a role', () => {
|
||||
store.dispatch(new NavigateLibraryAction({ ...site, role: null }));
|
||||
admin$.next(false);
|
||||
node$.next({ entry: { id: 'public-doc-lib-id' } } as NodeEntry);
|
||||
|
||||
expect(store.dispatch).toHaveBeenCalledWith(
|
||||
jasmine.objectContaining({
|
||||
type: 'NAVIGATE_ROUTE',
|
||||
payload: ['libraries', 'public-doc-lib-id']
|
||||
})
|
||||
);
|
||||
expect(notificationService.showError).not.toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it('should allow navigation to private site for non-admin user with a role', () => {
|
||||
store.dispatch(new NavigateLibraryAction({ ...site, visibility: 'PRIVATE' }));
|
||||
admin$.next(false);
|
||||
node$.next({ entry: { id: 'role-doc-lib-id' } } as NodeEntry);
|
||||
|
||||
expect(store.dispatch).toHaveBeenCalledWith(
|
||||
jasmine.objectContaining({
|
||||
type: 'NAVIGATE_ROUTE',
|
||||
payload: ['libraries', 'role-doc-lib-id']
|
||||
})
|
||||
);
|
||||
});
|
||||
|
||||
it('should use custom route if provided', () => {
|
||||
store.dispatch(new NavigateLibraryAction(site, 'custom-route'));
|
||||
admin$.next(false);
|
||||
node$.next({ entry: { id: 'doc-lib-id' } } as NodeEntry);
|
||||
|
||||
expect(store.dispatch).toHaveBeenCalledWith(
|
||||
jasmine.objectContaining({
|
||||
type: 'NAVIGATE_ROUTE',
|
||||
payload: ['custom-route', 'doc-lib-id']
|
||||
})
|
||||
);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
@@ -31,12 +31,13 @@ import {
|
||||
LibraryActionTypes,
|
||||
NavigateLibraryAction,
|
||||
NavigateRouteAction,
|
||||
UpdateLibraryAction
|
||||
UpdateLibraryAction,
|
||||
isAdmin
|
||||
} from '@alfresco/aca-shared/store';
|
||||
import { inject, Injectable } from '@angular/core';
|
||||
import { Actions, createEffect, ofType } from '@ngrx/effects';
|
||||
import { Store } from '@ngrx/store';
|
||||
import { map, mergeMap, take } from 'rxjs/operators';
|
||||
import { map, mergeMap, take, tap } from 'rxjs/operators';
|
||||
import { ContentApiService } from '@alfresco/aca-shared';
|
||||
import { ContentManagementService } from '../../services/content-management.service';
|
||||
import { NotificationService } from '@alfresco/adf-core';
|
||||
@@ -99,45 +100,60 @@ export class LibraryEffects {
|
||||
this.actions$.pipe(
|
||||
ofType<CreateLibraryAction>(LibraryActionTypes.Create),
|
||||
mergeMap(() => this.content.createLibrary()),
|
||||
map((libraryId) => new NavigateLibraryAction(libraryId))
|
||||
tap((libraryId) => this.navigateToLibraryById(libraryId))
|
||||
),
|
||||
{ dispatch: true }
|
||||
{ dispatch: false }
|
||||
);
|
||||
|
||||
navigateLibrary$ = createEffect(
|
||||
() =>
|
||||
this.actions$.pipe(
|
||||
ofType<NavigateLibraryAction>(LibraryActionTypes.Navigate),
|
||||
map((action) => {
|
||||
const libraryId = action.payload;
|
||||
if (libraryId) {
|
||||
this.contentApi
|
||||
.getNode(libraryId, { relativePath: '/documentLibrary' })
|
||||
.pipe(map((node) => node.entry.id))
|
||||
.subscribe(
|
||||
(id) => {
|
||||
const route = action.route ? action.route : 'libraries';
|
||||
this.store.dispatch(new NavigateRouteAction([route, id]));
|
||||
},
|
||||
(error: HttpErrorResponse) => {
|
||||
switch (error.status) {
|
||||
case 403:
|
||||
this.notificationService.showWarning('APP.BROWSE.LIBRARIES.LIBRARY_NO_PERMISSIONS_WARNING');
|
||||
break;
|
||||
case 404:
|
||||
this.notificationService.showError('APP.BROWSE.LIBRARIES.ERRORS.LIBRARY_NOT_FOUND');
|
||||
break;
|
||||
default:
|
||||
this.notificationService.showError('APP.BROWSE.LIBRARIES.ERRORS.LIBRARY_LOADING_ERROR');
|
||||
}
|
||||
tap((action) => {
|
||||
const payload = action.payload;
|
||||
if (payload && 'guid' in payload) {
|
||||
this.store
|
||||
.select(isAdmin)
|
||||
.pipe(take(1))
|
||||
.subscribe((isUserAdmin) => {
|
||||
if (!isUserAdmin && payload.visibility !== 'PUBLIC' && !payload.role) {
|
||||
this.notificationService.showError('APP.BROWSE.LIBRARIES.LIBRARY_NO_PERMISSIONS_WARNING');
|
||||
} else {
|
||||
this.navigateToLibraryById(payload.guid, action.route);
|
||||
}
|
||||
);
|
||||
});
|
||||
}
|
||||
})
|
||||
),
|
||||
{ dispatch: false }
|
||||
);
|
||||
|
||||
private navigateToLibraryById(libraryId: string, route = 'libraries'): void {
|
||||
this.contentApi
|
||||
.getNode(libraryId, { relativePath: '/documentLibrary' })
|
||||
.pipe(
|
||||
map((node) => node.entry.id),
|
||||
take(1)
|
||||
)
|
||||
.subscribe({
|
||||
next: (id) => {
|
||||
this.store.dispatch(new NavigateRouteAction([route, id]));
|
||||
},
|
||||
error: (error: HttpErrorResponse) => {
|
||||
switch (error.status) {
|
||||
case 403:
|
||||
this.notificationService.showWarning('APP.BROWSE.LIBRARIES.LIBRARY_NO_PERMISSIONS_WARNING');
|
||||
break;
|
||||
case 404:
|
||||
this.notificationService.showError('APP.BROWSE.LIBRARIES.ERRORS.LIBRARY_NOT_FOUND');
|
||||
break;
|
||||
default:
|
||||
this.notificationService.showError('APP.BROWSE.LIBRARIES.ERRORS.LIBRARY_LOADING_ERROR');
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
updateLibrary$ = createEffect(
|
||||
() =>
|
||||
this.actions$.pipe(
|
||||
|
||||
@@ -23,7 +23,7 @@
|
||||
*/
|
||||
|
||||
import { Action } from '@ngrx/store';
|
||||
import { SiteBodyCreate } from '@alfresco/js-api';
|
||||
import { Site, SiteBodyCreate } from '@alfresco/js-api';
|
||||
import { ModalConfiguration } from '../models/modal-configuration';
|
||||
|
||||
export enum LibraryActionTypes {
|
||||
@@ -48,7 +48,7 @@ export class NavigateLibraryAction implements Action {
|
||||
readonly type = LibraryActionTypes.Navigate;
|
||||
|
||||
constructor(
|
||||
public payload?: string,
|
||||
public payload?: Site,
|
||||
public route?: string
|
||||
) {}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user