Skip to content

Commit e590839

Browse files
[ACS-10409] Fix permission checking logic in library navigation (#4824)
* [ACS-10409] Fix permission checking logic in library navigation * [ACS-10409] cr fix
1 parent 55a899b commit e590839

File tree

7 files changed

+135
-37
lines changed

7 files changed

+135
-37
lines changed

projects/aca-content/src/lib/components/favorite-libraries/favorite-libraries.component.spec.ts

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,7 @@ import { LibraryEffects } from '../../store/effects';
3636
import { NodeEntry } from '@alfresco/js-api';
3737
import { getTitleElementText } from '../../testing/test-utils';
3838
import { MatSnackBarModule } from '@angular/material/snack-bar';
39+
import { SiteEntry } from '@alfresco/js-api/typings';
3940

4041
describe('FavoriteLibrariesComponent', () => {
4142
let fixture: ComponentFixture<FavoriteLibrariesComponent>;
@@ -126,7 +127,13 @@ describe('FavoriteLibrariesComponent', () => {
126127

127128
it('does not navigate when id is not passed', () => {
128129
spyOn(router, 'navigate').and.stub();
129-
component.navigateTo({ entry: { guid: 'guid' } } as any);
130+
component.navigateTo({
131+
entry: {
132+
guid: 'test-guid',
133+
visibility: 'PUBLIC',
134+
role: 'SiteConsumer'
135+
}
136+
} as SiteEntry);
130137

131138
expect(router.navigate).toHaveBeenCalledWith(['favorite/libraries', 'libraryId']);
132139
});

projects/aca-content/src/lib/components/favorite-libraries/favorite-libraries.component.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -104,7 +104,7 @@ export class FavoriteLibrariesComponent extends PageComponent implements OnInit
104104

105105
navigateTo(node: SiteEntry) {
106106
if (node?.entry?.guid) {
107-
this.store.dispatch(new NavigateLibraryAction(node.entry.guid, 'favorite/libraries'));
107+
this.store.dispatch(new NavigateLibraryAction(node.entry, 'favorite/libraries'));
108108
}
109109
}
110110

projects/aca-content/src/lib/components/libraries/libraries.component.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -89,7 +89,7 @@ export class LibrariesComponent extends PageComponent implements OnInit {
8989

9090
navigateTo(node: SiteEntry) {
9191
if (node?.entry?.guid) {
92-
this.store.dispatch(new NavigateLibraryAction(node.entry.guid));
92+
this.store.dispatch(new NavigateLibraryAction(node.entry));
9393
}
9494
}
9595

projects/aca-content/src/lib/components/search/search-libraries-results/search-libraries-results.component.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -168,7 +168,7 @@ export class SearchLibrariesResultsComponent extends PageComponent implements On
168168

169169
navigateTo(node: SiteEntry) {
170170
if (node?.entry?.guid) {
171-
this.store.dispatch(new NavigateLibraryAction(node.entry.guid));
171+
this.store.dispatch(new NavigateLibraryAction(node.entry));
172172
}
173173
}
174174

projects/aca-content/src/lib/store/effects/library.effects.spec.ts

Lines changed: 79 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -32,7 +32,7 @@ import { NotificationService } from '@alfresco/adf-core';
3232
import { provideEffects } from '@ngrx/effects';
3333
import { LibraryEffects } from './library.effects';
3434
import { AppTestingModule } from '../../testing/app-testing.module';
35-
import { NodeEntry } from '@alfresco/js-api';
35+
import { NodeEntry, Site } from '@alfresco/js-api';
3636

3737
describe('LibraryEffects', () => {
3838
let store: Store<AppStore>;
@@ -48,31 +48,106 @@ describe('LibraryEffects', () => {
4848
describe('navigateLibrary$', () => {
4949
let notificationService: NotificationService;
5050
let node$: Subject<NodeEntry>;
51+
let admin$: Subject<boolean>;
52+
let site: Site;
5153

5254
beforeEach(() => {
5355
node$ = new Subject<NodeEntry>();
56+
admin$ = new Subject<boolean>();
57+
site = { guid: 'site-guid', visibility: 'PUBLIC', role: 'SiteConsumer', id: 'site-id', title: 'Title' };
5458
spyOn(TestBed.inject(ContentApiService), 'getNode').and.returnValue(node$);
5559
notificationService = TestBed.inject(NotificationService);
5660
spyOn(notificationService, 'showError');
61+
spyOn(store, 'dispatch').and.callThrough();
62+
spyOn(store, 'select').and.returnValue(admin$);
5763
});
5864

5965
it('should display library no permission warning if user does not have permission', () => {
6066
spyOn(notificationService, 'showWarning');
61-
store.dispatch(new NavigateLibraryAction('libraryId'));
67+
store.dispatch(new NavigateLibraryAction(site));
68+
admin$.next(false);
6269
node$.error(new HttpErrorResponse({ status: 403 }));
6370
expect(notificationService.showWarning).toHaveBeenCalledWith('APP.BROWSE.LIBRARIES.LIBRARY_NO_PERMISSIONS_WARNING');
6471
});
6572

6673
it('should display library not found error if library does not exist', () => {
67-
store.dispatch(new NavigateLibraryAction('libraryId'));
74+
store.dispatch(new NavigateLibraryAction(site));
75+
admin$.next(false);
6876
node$.error(new HttpErrorResponse({ status: 404 }));
6977
expect(notificationService.showError).toHaveBeenCalledWith('APP.BROWSE.LIBRARIES.ERRORS.LIBRARY_NOT_FOUND');
7078
});
7179

7280
it('should display generic library loading error if there is different problem than missing permissions or absence of library', () => {
73-
store.dispatch(new NavigateLibraryAction('libraryId'));
81+
store.dispatch(new NavigateLibraryAction(site));
82+
admin$.next(false);
7483
node$.error(new HttpErrorResponse({ status: 500 }));
7584
expect(notificationService.showError).toHaveBeenCalledWith('APP.BROWSE.LIBRARIES.ERRORS.LIBRARY_LOADING_ERROR');
7685
});
86+
87+
it('should show error when non-admin user without site role tries to access private site', () => {
88+
store.dispatch(new NavigateLibraryAction({ ...site, visibility: 'PRIVATE', role: null }));
89+
admin$.next(false);
90+
91+
expect(store.dispatch).not.toHaveBeenCalledWith(
92+
jasmine.objectContaining({
93+
type: 'NAVIGATE_ROUTE'
94+
})
95+
);
96+
expect(notificationService.showError).toHaveBeenCalledWith('APP.BROWSE.LIBRARIES.LIBRARY_NO_PERMISSIONS_WARNING');
97+
});
98+
99+
it('should allow admin user to navigate to private site without role', () => {
100+
store.dispatch(new NavigateLibraryAction({ ...site, visibility: 'PRIVATE', role: null }));
101+
admin$.next(true);
102+
node$.next({ entry: { id: 'private-doc-lib-id' } } as NodeEntry);
103+
104+
expect(store.dispatch).toHaveBeenCalledWith(
105+
jasmine.objectContaining({
106+
type: 'NAVIGATE_ROUTE',
107+
payload: ['libraries', 'private-doc-lib-id']
108+
})
109+
);
110+
expect(notificationService.showError).not.toHaveBeenCalled();
111+
});
112+
113+
it('should allow navigation to public site for non-admin user without a role', () => {
114+
store.dispatch(new NavigateLibraryAction({ ...site, role: null }));
115+
admin$.next(false);
116+
node$.next({ entry: { id: 'public-doc-lib-id' } } as NodeEntry);
117+
118+
expect(store.dispatch).toHaveBeenCalledWith(
119+
jasmine.objectContaining({
120+
type: 'NAVIGATE_ROUTE',
121+
payload: ['libraries', 'public-doc-lib-id']
122+
})
123+
);
124+
expect(notificationService.showError).not.toHaveBeenCalled();
125+
});
126+
127+
it('should allow navigation to private site for non-admin user with a role', () => {
128+
store.dispatch(new NavigateLibraryAction({ ...site, visibility: 'PRIVATE' }));
129+
admin$.next(false);
130+
node$.next({ entry: { id: 'role-doc-lib-id' } } as NodeEntry);
131+
132+
expect(store.dispatch).toHaveBeenCalledWith(
133+
jasmine.objectContaining({
134+
type: 'NAVIGATE_ROUTE',
135+
payload: ['libraries', 'role-doc-lib-id']
136+
})
137+
);
138+
});
139+
140+
it('should use custom route if provided', () => {
141+
store.dispatch(new NavigateLibraryAction(site, 'custom-route'));
142+
admin$.next(false);
143+
node$.next({ entry: { id: 'doc-lib-id' } } as NodeEntry);
144+
145+
expect(store.dispatch).toHaveBeenCalledWith(
146+
jasmine.objectContaining({
147+
type: 'NAVIGATE_ROUTE',
148+
payload: ['custom-route', 'doc-lib-id']
149+
})
150+
);
151+
});
77152
});
78153
});

projects/aca-content/src/lib/store/effects/library.effects.ts

Lines changed: 43 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -31,12 +31,13 @@ import {
3131
LibraryActionTypes,
3232
NavigateLibraryAction,
3333
NavigateRouteAction,
34-
UpdateLibraryAction
34+
UpdateLibraryAction,
35+
isAdmin
3536
} from '@alfresco/aca-shared/store';
3637
import { inject, Injectable } from '@angular/core';
3738
import { Actions, createEffect, ofType } from '@ngrx/effects';
3839
import { Store } from '@ngrx/store';
39-
import { map, mergeMap, take } from 'rxjs/operators';
40+
import { map, mergeMap, take, tap } from 'rxjs/operators';
4041
import { ContentApiService } from '@alfresco/aca-shared';
4142
import { ContentManagementService } from '../../services/content-management.service';
4243
import { NotificationService } from '@alfresco/adf-core';
@@ -99,45 +100,60 @@ export class LibraryEffects {
99100
this.actions$.pipe(
100101
ofType<CreateLibraryAction>(LibraryActionTypes.Create),
101102
mergeMap(() => this.content.createLibrary()),
102-
map((libraryId) => new NavigateLibraryAction(libraryId))
103+
tap((libraryId) => this.navigateToLibraryById(libraryId))
103104
),
104-
{ dispatch: true }
105+
{ dispatch: false }
105106
);
106107

107108
navigateLibrary$ = createEffect(
108109
() =>
109110
this.actions$.pipe(
110111
ofType<NavigateLibraryAction>(LibraryActionTypes.Navigate),
111-
map((action) => {
112-
const libraryId = action.payload;
113-
if (libraryId) {
114-
this.contentApi
115-
.getNode(libraryId, { relativePath: '/documentLibrary' })
116-
.pipe(map((node) => node.entry.id))
117-
.subscribe(
118-
(id) => {
119-
const route = action.route ? action.route : 'libraries';
120-
this.store.dispatch(new NavigateRouteAction([route, id]));
121-
},
122-
(error: HttpErrorResponse) => {
123-
switch (error.status) {
124-
case 403:
125-
this.notificationService.showWarning('APP.BROWSE.LIBRARIES.LIBRARY_NO_PERMISSIONS_WARNING');
126-
break;
127-
case 404:
128-
this.notificationService.showError('APP.BROWSE.LIBRARIES.ERRORS.LIBRARY_NOT_FOUND');
129-
break;
130-
default:
131-
this.notificationService.showError('APP.BROWSE.LIBRARIES.ERRORS.LIBRARY_LOADING_ERROR');
132-
}
112+
tap((action) => {
113+
const payload = action.payload;
114+
if (payload && 'guid' in payload) {
115+
this.store
116+
.select(isAdmin)
117+
.pipe(take(1))
118+
.subscribe((isUserAdmin) => {
119+
if (!isUserAdmin && payload.visibility !== 'PUBLIC' && !payload.role) {
120+
this.notificationService.showError('APP.BROWSE.LIBRARIES.LIBRARY_NO_PERMISSIONS_WARNING');
121+
} else {
122+
this.navigateToLibraryById(payload.guid, action.route);
133123
}
134-
);
124+
});
135125
}
136126
})
137127
),
138128
{ dispatch: false }
139129
);
140130

131+
private navigateToLibraryById(libraryId: string, route = 'libraries'): void {
132+
this.contentApi
133+
.getNode(libraryId, { relativePath: '/documentLibrary' })
134+
.pipe(
135+
map((node) => node.entry.id),
136+
take(1)
137+
)
138+
.subscribe({
139+
next: (id) => {
140+
this.store.dispatch(new NavigateRouteAction([route, id]));
141+
},
142+
error: (error: HttpErrorResponse) => {
143+
switch (error.status) {
144+
case 403:
145+
this.notificationService.showWarning('APP.BROWSE.LIBRARIES.LIBRARY_NO_PERMISSIONS_WARNING');
146+
break;
147+
case 404:
148+
this.notificationService.showError('APP.BROWSE.LIBRARIES.ERRORS.LIBRARY_NOT_FOUND');
149+
break;
150+
default:
151+
this.notificationService.showError('APP.BROWSE.LIBRARIES.ERRORS.LIBRARY_LOADING_ERROR');
152+
}
153+
}
154+
});
155+
}
156+
141157
updateLibrary$ = createEffect(
142158
() =>
143159
this.actions$.pipe(

projects/aca-shared/store/src/actions/library.actions.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,7 @@
2323
*/
2424

2525
import { Action } from '@ngrx/store';
26-
import { SiteBodyCreate } from '@alfresco/js-api';
26+
import { Site, SiteBodyCreate } from '@alfresco/js-api';
2727
import { ModalConfiguration } from '../models/modal-configuration';
2828

2929
export enum LibraryActionTypes {
@@ -48,7 +48,7 @@ export class NavigateLibraryAction implements Action {
4848
readonly type = LibraryActionTypes.Navigate;
4949

5050
constructor(
51-
public payload?: string,
51+
public payload?: Site,
5252
public route?: string
5353
) {}
5454
}

0 commit comments

Comments
 (0)