ACS-11861 Sharable link redirects to Login Page (#5206)

* [ACS-11861]: adds publicUrl check

* [ACS-11861]: UTs

* [ACS-11861]: brings in minimatch dependency

* [ACS-11861]: sonar fix
This commit is contained in:
Anton Ramanovich
2026-06-03 08:44:14 +02:00
committed by GitHub
parent 45c51f4018
commit 804824ebb0
3 changed files with 78 additions and 4 deletions
@@ -25,6 +25,7 @@
import { AppService } from './app.service';
import { TestBed } from '@angular/core/testing';
import {
AppConfigService,
AuthenticationService,
NoopTranslateModule,
NotificationService,
@@ -43,15 +44,24 @@ import {
SharedLinksApiService,
UploadService
} from '@alfresco/adf-content-services';
import { ActivatedRoute } from '@angular/router';
import { ActivatedRoute, Router } from '@angular/router';
import { provideMockStore } from '@ngrx/store/testing';
import { RepositoryInfo, VersionInfo } from '@alfresco/js-api';
import { MatDialogModule } from '@angular/material/dialog';
import { MatDialog, MatDialogModule } from '@angular/material/dialog';
import { Store } from '@ngrx/store';
import { ContentApiService } from './content-api.service';
import { AppSettingsService, UserProfileService } from '@alfresco/aca-shared';
import { MatSnackBarModule } from '@angular/material/snack-bar';
interface ApiUnauthorizedError {
status: number;
response: {
req?: {
url?: string;
};
};
}
describe('AppService', () => {
let service: AppService;
let auth: AuthenticationService;
@@ -95,7 +105,9 @@ describe('AppService', () => {
{
provide: ActivatedRoute,
useValue: {
snapshot: {}
snapshot: {
queryParams: {}
}
}
},
{
@@ -222,6 +234,63 @@ describe('AppService', () => {
expect(contentApi.getRepositoryInformation).toHaveBeenCalled();
});
describe('Init with unauthorized api error', () => {
let appConfigService: AppConfigService;
let router: Router;
let matDialog: MatDialog;
let alfrescoApiService: AlfrescoApiService;
let navigateSpy: jasmine.Spy;
let closeAllSpy: jasmine.Spy;
let apiErrorListener: (error: ApiUnauthorizedError) => void;
const setupUnauthorizedErrorListener = (currentUrl: string): void => {
spyOn(auth, 'isLoggedIn').and.returnValue(false);
spyOn(alfrescoApiService, 'isExcludedErrorListener').and.returnValue(false);
spyOn(appConfigService, 'get').and.callFake((key: string, defaultValue?: any) => (key === 'oauth2.publicUrls' ? ['/public/**'] : defaultValue));
spyOnProperty(router, 'url', 'get').and.returnValue(currentUrl);
closeAllSpy = spyOn(matDialog, 'closeAll');
navigateSpy = spyOn(router, 'navigate').and.returnValue(Promise.resolve(true));
const apiInstance = alfrescoApiService.getInstance();
spyOn(apiInstance, 'on').and.callFake((eventName: string | symbol, listener: (...args: unknown[]) => void) => {
if (eventName === 'error') {
apiErrorListener = listener;
}
return apiInstance;
});
service.init();
};
beforeEach(() => {
appConfigService = TestBed.inject(AppConfigService);
router = TestBed.inject(Router);
matDialog = TestBed.inject(MatDialog);
alfrescoApiService = TestBed.inject(AlfrescoApiService);
});
it('should navigate to login on 401 for non-public url when user is logged out', () => {
setupUnauthorizedErrorListener('/private/page');
apiErrorListener({ status: 401, response: { req: { url: '/api/private' } } });
expect(closeAllSpy).toHaveBeenCalled();
expect(navigateSpy).toHaveBeenCalledWith(['/login'], {
queryParams: { redirectUrl: '/private/page' }
});
});
it('should not navigate to login on 401 for public url when user is logged out', () => {
setupUnauthorizedErrorListener('/public/home');
apiErrorListener({ status: 401, response: { req: { url: '/api/public' } } });
expect(closeAllSpy).not.toHaveBeenCalled();
expect(navigateSpy).not.toHaveBeenCalled();
});
});
it('should load user profile on login', async () => {
const person: any = { id: 'person' };
@@ -52,6 +52,7 @@ import { ShellAppService } from '@alfresco/adf-core/shell';
import { AppSettingsService } from './app-settings.service';
import { UserProfileService } from './user-profile.service';
import { MatDialog } from '@angular/material/dialog';
import { minimatch } from 'minimatch';
@Injectable({
providedIn: 'root'
@@ -133,7 +134,10 @@ export class AppService implements ShellAppService {
init(): void {
this.alfrescoApiService.getInstance().on('error', (error: { status: number; response: any }) => {
if (error.status === 401 && !this.alfrescoApiService.isExcludedErrorListener(error?.response?.req?.url)) {
if (!this.authenticationService.isLoggedIn()) {
const publicUrls: string[] = this.config.get('oauth2.publicUrls', []);
const isPublicUrl = publicUrls.some((pattern) => minimatch(this.router.url, pattern));
if (!this.authenticationService.isLoggedIn() && !isPublicUrl) {
this.matDialog.closeAll();
let redirectUrl = this.activatedRoute.snapshot.queryParams['redirectUrl'];