frontend: implement validate access guards for rooms and update participant name handling

This commit is contained in:
juancarmore 2025-08-05 17:53:29 +02:00
parent 6e9c1743a1
commit bdc3f599f0
5 changed files with 51 additions and 10 deletions

View File

@ -2,4 +2,4 @@ export * from './auth.guard';
export * from './extract-query-params.guard'; export * from './extract-query-params.guard';
export * from './remove-secret.guard'; export * from './remove-secret.guard';
export * from './run-serially.guard'; export * from './run-serially.guard';
export * from './validate-recording-access.guard'; export * from './validate-access.guard';

View File

@ -1,10 +1,10 @@
import { inject } from '@angular/core'; import { inject } from '@angular/core';
import { ActivatedRouteSnapshot, CanActivateFn, RouterStateSnapshot } from '@angular/router'; import { ActivatedRouteSnapshot, CanActivateFn, RouterStateSnapshot } from '@angular/router';
import { ErrorReason } from '@lib/models'; import { ErrorReason } from '@lib/models';
import { NavigationService, RecordingManagerService, RoomService } from '@lib/services'; import { NavigationService, ParticipantTokenService, RecordingManagerService, RoomService } from '@lib/services';
/** /**
* Guard to validate the access to recordings. * Guard to validate the access to recordings of a room by generating a recording token.
*/ */
export const validateRecordingAccessGuard: CanActivateFn = async ( export const validateRecordingAccessGuard: CanActivateFn = async (
_route: ActivatedRouteSnapshot, _route: ActivatedRouteSnapshot,
@ -32,7 +32,7 @@ export const validateRecordingAccessGuard: CanActivateFn = async (
switch (error.status) { switch (error.status) {
case 400: case 400:
// Invalid secret // Invalid secret
return navigationService.redirectToErrorPage(ErrorReason.INVALID_RECORDING_SECRET); return navigationService.redirectToErrorPage(ErrorReason.INVALID_ROOM_SECRET);
case 403: case 403:
// Recording access is configured for admins only // Recording access is configured for admins only
return navigationService.redirectToErrorPage(ErrorReason.RECORDINGS_ADMIN_ONLY_ACCESS); return navigationService.redirectToErrorPage(ErrorReason.RECORDINGS_ADMIN_ONLY_ACCESS);
@ -44,3 +44,38 @@ export const validateRecordingAccessGuard: CanActivateFn = async (
} }
} }
}; };
/**
* Guard to validate access to a room by generating a participant token.
*/
export const validateRoomAccessGuard: CanActivateFn = async (
_route: ActivatedRouteSnapshot,
_state: RouterStateSnapshot
) => {
const roomService = inject(RoomService);
const participantTokenService = inject(ParticipantTokenService);
const navigationService = inject(NavigationService);
const roomId = roomService.getRoomId();
const secret = roomService.getRoomSecret();
try {
await participantTokenService.generateToken({
roomId,
secret
});
return true;
} catch (error: any) {
console.error('Error generating participant token:', error);
switch (error.status) {
case 400:
// Invalid secret
return navigationService.redirectToErrorPage(ErrorReason.INVALID_ROOM_SECRET);
case 404:
// Room not found
return navigationService.redirectToErrorPage(ErrorReason.INVALID_ROOM);
default:
return navigationService.redirectToErrorPage(ErrorReason.INTERNAL_ERROR);
}
}
};

View File

@ -49,7 +49,7 @@ export const httpInterceptor: HttpInterceptorFn = (req: HttpRequest<unknown>, ne
const secret = roomService.getRoomSecret(); const secret = roomService.getRoomSecret();
const participantName = participantTokenService.getParticipantName(); const participantName = participantTokenService.getParticipantName();
return from(participantTokenService.refreshParticipantToken({ roomId, participantName, secret })).pipe( return from(participantTokenService.refreshParticipantToken({ roomId, secret, participantName })).pipe(
switchMap(() => { switchMap(() => {
console.log('Participant token refreshed'); console.log('Participant token refreshed');
return next(req); return next(req);

View File

@ -8,7 +8,8 @@ import {
extractRoomQueryParamsGuard, extractRoomQueryParamsGuard,
removeRoomSecretGuard, removeRoomSecretGuard,
runGuardsSerially, runGuardsSerially,
validateRecordingAccessGuard validateRecordingAccessGuard,
validateRoomAccessGuard
} from '@lib/guards'; } from '@lib/guards';
import { import {
ConsoleComponent, ConsoleComponent,
@ -16,13 +17,13 @@ import {
EndMeetingComponent, EndMeetingComponent,
ErrorComponent, ErrorComponent,
LoginComponent, LoginComponent,
MeetingComponent,
OverviewComponent, OverviewComponent,
RecordingsComponent, RecordingsComponent,
RoomRecordingsComponent, RoomRecordingsComponent,
RoomsComponent, RoomsComponent,
RoomWizardComponent, RoomWizardComponent,
UsersPermissionsComponent, UsersPermissionsComponent,
MeetingComponent,
ViewRecordingComponent ViewRecordingComponent
} from '@lib/pages'; } from '@lib/pages';
@ -36,7 +37,12 @@ export const baseRoutes: Routes = [
path: 'room/:room-id', path: 'room/:room-id',
component: MeetingComponent, component: MeetingComponent,
canActivate: [ canActivate: [
runGuardsSerially(extractRoomQueryParamsGuard, checkParticipantRoleAndAuthGuard, removeRoomSecretGuard) runGuardsSerially(
extractRoomQueryParamsGuard,
checkParticipantRoleAndAuthGuard,
validateRoomAccessGuard,
removeRoomSecretGuard
)
] ]
}, },
{ {

View File

@ -11,7 +11,7 @@ import { LoggerService } from 'openvidu-components-angular';
export class ParticipantTokenService { export class ParticipantTokenService {
protected readonly PARTICIPANTS_API = `${HttpService.INTERNAL_API_PATH_PREFIX}/participants`; protected readonly PARTICIPANTS_API = `${HttpService.INTERNAL_API_PATH_PREFIX}/participants`;
protected participantName: string = ''; protected participantName?: string;
protected participantRole: ParticipantRole = ParticipantRole.PUBLISHER; protected participantRole: ParticipantRole = ParticipantRole.PUBLISHER;
protected currentTokenInfo?: ParticipantTokenInfo; protected currentTokenInfo?: ParticipantTokenInfo;
@ -29,7 +29,7 @@ export class ParticipantTokenService {
this.participantName = participantName; this.participantName = participantName;
} }
getParticipantName(): string { getParticipantName(): string | undefined {
return this.participantName; return this.participantName;
} }