frontend: replace UnauthorizedComponent by ErrorComponent and improve error handling in guards
This commit is contained in:
parent
b037edb92b
commit
ab270239b5
@ -1,4 +0,0 @@
|
|||||||
<div id="unauthorized-content">
|
|
||||||
<h4>The page you are trying to access is restricted.</h4>
|
|
||||||
<h4 id="error-reason">Reason: {{ message }}</h4>
|
|
||||||
</div>
|
|
||||||
@ -1,23 +0,0 @@
|
|||||||
import { ComponentFixture, TestBed } from '@angular/core/testing';
|
|
||||||
|
|
||||||
import { UnauthorizedComponent } from './unauthorized.component';
|
|
||||||
|
|
||||||
describe('UnauthorizedComponent', () => {
|
|
||||||
let component: UnauthorizedComponent;
|
|
||||||
let fixture: ComponentFixture<UnauthorizedComponent>;
|
|
||||||
|
|
||||||
beforeEach(async () => {
|
|
||||||
await TestBed.configureTestingModule({
|
|
||||||
imports: [UnauthorizedComponent]
|
|
||||||
})
|
|
||||||
.compileComponents();
|
|
||||||
|
|
||||||
fixture = TestBed.createComponent(UnauthorizedComponent);
|
|
||||||
component = fixture.componentInstance;
|
|
||||||
fixture.detectChanges();
|
|
||||||
});
|
|
||||||
|
|
||||||
it('should create', () => {
|
|
||||||
expect(component).toBeTruthy();
|
|
||||||
});
|
|
||||||
});
|
|
||||||
@ -1,38 +0,0 @@
|
|||||||
import { Component, OnInit } from '@angular/core';
|
|
||||||
import { ActivatedRoute } from '@angular/router';
|
|
||||||
|
|
||||||
@Component({
|
|
||||||
selector: 'ov-unauthorized',
|
|
||||||
standalone: true,
|
|
||||||
imports: [],
|
|
||||||
templateUrl: './unauthorized.component.html',
|
|
||||||
styleUrl: './unauthorized.component.scss'
|
|
||||||
})
|
|
||||||
export class UnauthorizedComponent implements OnInit {
|
|
||||||
message = 'Unauthorized access';
|
|
||||||
constructor(private route: ActivatedRoute) {}
|
|
||||||
|
|
||||||
ngOnInit(): void {
|
|
||||||
this.route.queryParams.subscribe((params) => {
|
|
||||||
const reason = params['reason'];
|
|
||||||
switch (reason) {
|
|
||||||
case 'invalid-token':
|
|
||||||
this.message = 'The token provided is not valid';
|
|
||||||
break;
|
|
||||||
case 'invalid-room':
|
|
||||||
this.message = 'The room name is not valid';
|
|
||||||
break;
|
|
||||||
case 'invalid-participant':
|
|
||||||
this.message = 'The participant name must be provided';
|
|
||||||
break;
|
|
||||||
case 'unauthorized-participant':
|
|
||||||
this.message = 'You are not authorized to join this room';
|
|
||||||
break;
|
|
||||||
|
|
||||||
default:
|
|
||||||
this.message = 'Unauthorized access';
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@ -1,7 +1,14 @@
|
|||||||
import { inject } from '@angular/core';
|
import { inject } from '@angular/core';
|
||||||
import { ActivatedRouteSnapshot, CanActivateFn, RedirectCommand, Router, RouterStateSnapshot } from '@angular/router';
|
import {
|
||||||
import { AuthService, ContextService, HttpService, SessionStorageService } from '../services';
|
ActivatedRouteSnapshot,
|
||||||
|
CanActivateFn,
|
||||||
|
RedirectCommand,
|
||||||
|
Router,
|
||||||
|
RouterStateSnapshot,
|
||||||
|
UrlTree
|
||||||
|
} from '@angular/router';
|
||||||
import { AuthMode, ParticipantRole } from '@lib/typings/ce';
|
import { AuthMode, ParticipantRole } from '@lib/typings/ce';
|
||||||
|
import { AuthService, ContextService, HttpService, SessionStorageService } from '../services';
|
||||||
|
|
||||||
export const checkUserAuthenticatedGuard: CanActivateFn = async (
|
export const checkUserAuthenticatedGuard: CanActivateFn = async (
|
||||||
route: ActivatedRouteSnapshot,
|
route: ActivatedRouteSnapshot,
|
||||||
@ -64,9 +71,18 @@ export const checkParticipantRoleAndAuthGuard: CanActivateFn = async (
|
|||||||
const roomRoleAndPermissions = await httpService.getRoomRoleAndPermissions(roomId, storageSecret || secret);
|
const roomRoleAndPermissions = await httpService.getRoomRoleAndPermissions(roomId, storageSecret || secret);
|
||||||
participantRole = roomRoleAndPermissions.role;
|
participantRole = roomRoleAndPermissions.role;
|
||||||
contextService.setParticipantRole(participantRole);
|
contextService.setParticipantRole(participantRole);
|
||||||
} catch (error) {
|
} catch (error: any) {
|
||||||
console.error('Error getting participant role:', error);
|
console.error('Error getting participant role:', error);
|
||||||
return router.createUrlTree(['unauthorized'], { queryParams: { reason: 'unauthorized-participant' } });
|
switch (error.status) {
|
||||||
|
case 400:
|
||||||
|
// Invalid secret
|
||||||
|
return redirectToErrorPage(router, 'invalid-secret');
|
||||||
|
case 404:
|
||||||
|
// Room not found
|
||||||
|
return redirectToErrorPage(router, 'invalid-room');
|
||||||
|
default:
|
||||||
|
return redirectToErrorPage(router, 'internal-error');
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const authMode = await contextService.getAuthModeToEnterRoom();
|
const authMode = await contextService.getAuthModeToEnterRoom();
|
||||||
@ -114,3 +130,7 @@ export const checkUserNotAuthenticatedGuard: CanActivateFn = async (
|
|||||||
// Allow access to the requested page
|
// Allow access to the requested page
|
||||||
return true;
|
return true;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const redirectToErrorPage = (router: Router, reason: string): UrlTree => {
|
||||||
|
return router.createUrlTree(['error'], { queryParams: { reason } });
|
||||||
|
};
|
||||||
|
|||||||
@ -31,26 +31,29 @@ export const validateRecordingAccessGuard: CanActivateFn = async (
|
|||||||
contextService.setRecordingPermissionsFromToken(response.token);
|
contextService.setRecordingPermissionsFromToken(response.token);
|
||||||
|
|
||||||
if (!contextService.canRetrieveRecordings()) {
|
if (!contextService.canRetrieveRecordings()) {
|
||||||
// If the user does not have permission to retrieve recordings, redirect to the unauthorized page
|
// If the user does not have permission to retrieve recordings, redirect to the error page
|
||||||
return redirectToUnauthorized(router, 'unauthorized-recording-access');
|
return redirectToErrorPage(router, 'unauthorized-recording-access');
|
||||||
}
|
}
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
} catch (error: any) {
|
} catch (error: any) {
|
||||||
console.error('Error generating recording token:', error);
|
console.error('Error generating recording token:', error);
|
||||||
switch (error.status) {
|
switch (error.status) {
|
||||||
|
case 400:
|
||||||
|
// Invalid secret
|
||||||
|
return redirectToErrorPage(router, 'invalid-secret');
|
||||||
case 403:
|
case 403:
|
||||||
// Recording access is configured for admins only
|
// Recording access is configured for admins only
|
||||||
return redirectToUnauthorized(router, 'unauthorized-recording-access');
|
return redirectToErrorPage(router, 'recordings-admin-only-access');
|
||||||
case 404:
|
case 404:
|
||||||
// There are no recordings in the room or the room does not exist
|
// There are no recordings in the room or the room does not exist
|
||||||
return redirectToUnauthorized(router, 'no-recordings');
|
return redirectToErrorPage(router, 'no-recordings');
|
||||||
default:
|
default:
|
||||||
return redirectToUnauthorized(router, 'invalid-room');
|
return redirectToErrorPage(router, 'internal-error');
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
const redirectToUnauthorized = (router: Router, reason: string): UrlTree => {
|
const redirectToErrorPage = (router: Router, reason: string): UrlTree => {
|
||||||
return router.createUrlTree(['unauthorized'], { queryParams: { reason } });
|
return router.createUrlTree(['error'], { queryParams: { reason } });
|
||||||
};
|
};
|
||||||
|
|||||||
@ -38,6 +38,12 @@ export const validateRoomAccessGuard: CanActivateFn = async (
|
|||||||
} catch (error: any) {
|
} catch (error: any) {
|
||||||
console.error('Error generating participant token:', error);
|
console.error('Error generating participant token:', error);
|
||||||
switch (error.status) {
|
switch (error.status) {
|
||||||
|
case 400:
|
||||||
|
// Invalid secret
|
||||||
|
return redirectToErrorPage(router, 'invalid-secret');
|
||||||
|
case 404:
|
||||||
|
// Room not found
|
||||||
|
return redirectToErrorPage(router, 'invalid-room');
|
||||||
case 409:
|
case 409:
|
||||||
// Participant already exists.
|
// Participant already exists.
|
||||||
// Send a timestamp to force update the query params and show the error message in participant name input form
|
// Send a timestamp to force update the query params and show the error message in participant name input form
|
||||||
@ -47,14 +53,12 @@ export const validateRoomAccessGuard: CanActivateFn = async (
|
|||||||
return new RedirectCommand(participantNameRoute, {
|
return new RedirectCommand(participantNameRoute, {
|
||||||
skipLocationChange: true
|
skipLocationChange: true
|
||||||
});
|
});
|
||||||
case 406:
|
|
||||||
return redirectToUnauthorized(router, 'unauthorized-participant');
|
|
||||||
default:
|
default:
|
||||||
return redirectToUnauthorized(router, 'invalid-room');
|
return redirectToErrorPage(router, 'internal-error');
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
const redirectToUnauthorized = (router: Router, reason: string): UrlTree => {
|
const redirectToErrorPage = (router: Router, reason: string): UrlTree => {
|
||||||
return router.createUrlTree(['unauthorized'], { queryParams: { reason } });
|
return router.createUrlTree(['error'], { queryParams: { reason } });
|
||||||
};
|
};
|
||||||
|
|||||||
@ -0,0 +1,10 @@
|
|||||||
|
<div class="container">
|
||||||
|
<mat-card class="card">
|
||||||
|
<mat-card-header>
|
||||||
|
<mat-card-title>{{ errorName }}</mat-card-title>
|
||||||
|
</mat-card-header>
|
||||||
|
<mat-card-content>
|
||||||
|
<p>{{ message }}</p>
|
||||||
|
</mat-card-content>
|
||||||
|
</mat-card>
|
||||||
|
</div>
|
||||||
@ -0,0 +1,33 @@
|
|||||||
|
.container {
|
||||||
|
display: flex;
|
||||||
|
justify-content: center;
|
||||||
|
align-items: center;
|
||||||
|
height: 100vh;
|
||||||
|
background-color: var(--ov-background-color);
|
||||||
|
}
|
||||||
|
|
||||||
|
.card {
|
||||||
|
width: 400px;
|
||||||
|
padding: 20px;
|
||||||
|
text-align: center;
|
||||||
|
background-color: var(--ov-surface-color);
|
||||||
|
border-radius: var(--ov-surface-radius);
|
||||||
|
}
|
||||||
|
|
||||||
|
mat-card-header {
|
||||||
|
justify-content: center;
|
||||||
|
align-items: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
mat-card-title {
|
||||||
|
font-size: 1.5em;
|
||||||
|
}
|
||||||
|
|
||||||
|
mat-card-content p {
|
||||||
|
font-size: 1em;
|
||||||
|
color: #555;
|
||||||
|
}
|
||||||
|
|
||||||
|
mat-card-actions {
|
||||||
|
margin-top: 20px;
|
||||||
|
}
|
||||||
@ -0,0 +1,21 @@
|
|||||||
|
import { ComponentFixture, TestBed } from '@angular/core/testing';
|
||||||
|
import { ErrorComponent } from './error.component';
|
||||||
|
|
||||||
|
describe('GenericErrorComponent', () => {
|
||||||
|
let component: ErrorComponent;
|
||||||
|
let fixture: ComponentFixture<ErrorComponent>;
|
||||||
|
|
||||||
|
beforeEach(async () => {
|
||||||
|
await TestBed.configureTestingModule({
|
||||||
|
imports: [ErrorComponent]
|
||||||
|
}).compileComponents();
|
||||||
|
|
||||||
|
fixture = TestBed.createComponent(ErrorComponent);
|
||||||
|
component = fixture.componentInstance;
|
||||||
|
fixture.detectChanges();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should create', () => {
|
||||||
|
expect(component).toBeTruthy();
|
||||||
|
});
|
||||||
|
});
|
||||||
@ -0,0 +1,50 @@
|
|||||||
|
import { Component, OnInit } from '@angular/core';
|
||||||
|
import { MatCardModule } from '@angular/material/card';
|
||||||
|
import { ActivatedRoute } from '@angular/router';
|
||||||
|
|
||||||
|
@Component({
|
||||||
|
selector: 'ov-error',
|
||||||
|
standalone: true,
|
||||||
|
imports: [MatCardModule],
|
||||||
|
templateUrl: './error.component.html',
|
||||||
|
styleUrl: './error.component.scss'
|
||||||
|
})
|
||||||
|
export class ErrorComponent implements OnInit {
|
||||||
|
errorName = 'Error';
|
||||||
|
message = '';
|
||||||
|
|
||||||
|
constructor(private route: ActivatedRoute) {}
|
||||||
|
|
||||||
|
ngOnInit(): void {
|
||||||
|
this.route.queryParams.subscribe((params) => {
|
||||||
|
const reason = params['reason'];
|
||||||
|
switch (reason) {
|
||||||
|
case 'invalid-secret':
|
||||||
|
this.errorName = 'Invalid secret';
|
||||||
|
this.message =
|
||||||
|
'The secret provided to join the room is neither valid for moderators nor publishers';
|
||||||
|
break;
|
||||||
|
case 'invalid-room':
|
||||||
|
this.errorName = 'Invalid room';
|
||||||
|
this.message = 'The room you are trying to join does not exist or has been deleted';
|
||||||
|
break;
|
||||||
|
case 'no-recordings':
|
||||||
|
this.errorName = 'No recordings';
|
||||||
|
this.message = 'There are no recordings in this room or the room does not exist';
|
||||||
|
break;
|
||||||
|
case 'unauthorized-recording-access':
|
||||||
|
this.errorName = 'Unauthorized recording access';
|
||||||
|
this.message = 'You are not authorized to access the recordings in this room';
|
||||||
|
break;
|
||||||
|
case 'recordings-admin-only-access':
|
||||||
|
this.errorName = 'Unauthorized recording access';
|
||||||
|
this.message = 'Recordings access is configured for admins only in this room';
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
this.errorName = 'Internal error';
|
||||||
|
this.message = 'Something went wrong...';
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -1,6 +1,5 @@
|
|||||||
import { Routes } from '@angular/router';
|
import { Routes } from '@angular/router';
|
||||||
import { UserRole } from '@lib/typings/ce';
|
import { UserRole } from '@lib/typings/ce';
|
||||||
import { RoomCreatorDisabledComponent, UnauthorizedComponent } from '../components';
|
|
||||||
import {
|
import {
|
||||||
applicationModeGuard,
|
applicationModeGuard,
|
||||||
checkParticipantNameGuard,
|
checkParticipantNameGuard,
|
||||||
@ -20,11 +19,13 @@ import {
|
|||||||
ConsoleComponent,
|
ConsoleComponent,
|
||||||
ConsoleLoginComponent,
|
ConsoleLoginComponent,
|
||||||
DisconnectedComponent,
|
DisconnectedComponent,
|
||||||
|
ErrorComponent,
|
||||||
LoginComponent,
|
LoginComponent,
|
||||||
OverviewComponent,
|
OverviewComponent,
|
||||||
ParticipantNameFormComponent,
|
ParticipantNameFormComponent,
|
||||||
RecordingsComponent,
|
RecordingsComponent,
|
||||||
RoomCreatorComponent,
|
RoomCreatorComponent,
|
||||||
|
RoomCreatorDisabledComponent,
|
||||||
RoomFormComponent,
|
RoomFormComponent,
|
||||||
RoomRecordingsComponent,
|
RoomRecordingsComponent,
|
||||||
RoomsComponent,
|
RoomsComponent,
|
||||||
@ -51,7 +52,7 @@ export const baseRoutes: Routes = [
|
|||||||
},
|
},
|
||||||
{ path: 'room-creator-disabled', component: RoomCreatorDisabledComponent },
|
{ path: 'room-creator-disabled', component: RoomCreatorDisabledComponent },
|
||||||
{ path: 'disconnected', component: DisconnectedComponent },
|
{ path: 'disconnected', component: DisconnectedComponent },
|
||||||
{ path: 'unauthorized', component: UnauthorizedComponent },
|
{ path: 'error', component: ErrorComponent },
|
||||||
{
|
{
|
||||||
path: 'console/login',
|
path: 'console/login',
|
||||||
component: ConsoleLoginComponent,
|
component: ConsoleLoginComponent,
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user