frontend: update users permissions component for improved admin credential handling and access settings

This commit is contained in:
juancarmore 2025-07-02 19:04:27 +02:00
parent 66140468be
commit 9dc2796d24
2 changed files with 90 additions and 105 deletions

View File

@ -15,7 +15,7 @@
<div class="loading-content"> <div class="loading-content">
<div class="loading-header"> <div class="loading-header">
<div class="loading-title"> <div class="loading-title">
<mat-icon class="ov-settings-icon loading-icon"> settings </mat-icon> <mat-icon class="ov-settings-icon loading-icon">settings</mat-icon>
<h1>Loading Settings</h1> <h1>Loading Settings</h1>
</div> </div>
<p class="loading-subtitle">Please wait while we fetch your settings...</p> <p class="loading-subtitle">Please wait while we fetch your settings...</p>
@ -61,7 +61,7 @@
<h3>User Authentication</h3> <h3>User Authentication</h3>
<ov-pro-feature-badge class="users-pro-badge"></ov-pro-feature-badge> <ov-pro-feature-badge class="users-pro-badge"></ov-pro-feature-badge>
</div> </div>
<p class="field-description">Choose how users will authenticate to join rooms.</p> <p class="field-description">Choose how users will authenticate to access OpenVidu Meet.</p>
<mat-form-field <mat-form-field
subscriptSizing="dynamic" subscriptSizing="dynamic"
appearance="outline" appearance="outline"
@ -70,26 +70,25 @@
<mat-select [value]="'single'"> <mat-select [value]="'single'">
<mat-option value="single">Single user</mat-option> <mat-option value="single">Single user</mat-option>
<mat-option value="multiusers" [disabled]="true"> <mat-option value="multiusers" [disabled]="true">
Multi-user authentication (OAuth & credentials) Multi-user (credentials & optional OAuth)
</mat-option> </mat-option>
<mat-option value="oauth" [disabled]="true">Only OAuth </mat-option> <mat-option value="oauth" [disabled]="true">Multi-user (only OAuth)</mat-option>
</mat-select> </mat-select>
</mat-form-field> </mat-form-field>
</div> </div>
<!-- Change Admin Password --> <!-- Change Admin Password -->
<form [formGroup]="adminCredentialsForm" (ngSubmit)="onSaveAdminCredentials()">
<div class="form-section"> <div class="form-section">
<div class="form-field-header"> <div class="form-field-header">
<h3>Admin Authentication</h3> <h3>Admin Credentials</h3>
<p class="field-description"> <p class="field-description">Change the password for the admin user.</p>
Change the password for the admin user for OpenVidu Meet access.
</p>
</div> </div>
<div class="admin-auth-form"> <div class="admin-auth-form">
<mat-form-field subscriptSizing="dynamic" appearance="outline" class="username-field"> <mat-form-field subscriptSizing="dynamic" appearance="outline" class="username-field">
<mat-label>Username</mat-label> <mat-label>Username</mat-label>
<input matInput type="text" value="Admin (TODO)" disabled /> <input matInput type="text" formControlName="adminUsername" placeholder="admin" />
</mat-form-field> </mat-form-field>
<mat-form-field subscriptSizing="dynamic" appearance="outline" class="password-field"> <mat-form-field subscriptSizing="dynamic" appearance="outline" class="password-field">
@ -97,20 +96,27 @@
<input <input
matInput matInput
type="password" type="password"
[formControl]="adminPasswordControl" formControlName="adminPassword"
placeholder="••••••••" placeholder="••••••••"
/> />
<mat-hint>Minimum of 8 characters</mat-hint> <mat-hint>Minimum of 4 characters</mat-hint>
@if (getAdminPasswordError()) { @if (getAdminPasswordError()) {
<mat-error>{{ getAdminPasswordError() }}</mat-error> <mat-error>{{ getAdminPasswordError() }}</mat-error>
} }
</mat-form-field> </mat-form-field>
</div> </div>
</div> </div>
</form>
</mat-card-content> </mat-card-content>
<mat-card-actions> <mat-card-actions>
<button mat-raised-button color="primary" type="submit" (click)="onSaveAccess()"> <button
Save Admin password mat-raised-button
color="primary"
type="submit"
[disabled]="adminCredentialsForm.invalid"
(click)="onSaveAdminCredentials()"
>
Save admin password
</button> </button>
</mat-card-actions> </mat-card-actions>
</mat-card> </mat-card>
@ -127,7 +133,7 @@
<mat-divider class="section-divider"></mat-divider> <mat-divider class="section-divider"></mat-divider>
<mat-card-content> <mat-card-content>
<form [formGroup]="authForm" (ngSubmit)="onSaveAccess()"> <form [formGroup]="accessSettingsForm" (ngSubmit)="onSaveAccessSettings()">
<!-- Authentication for joining room --> <!-- Authentication for joining room -->
<div class="form-section"> <div class="form-section">
<div class="form-field-header"> <div class="form-field-header">
@ -142,16 +148,13 @@
<mat-option [value]="option.value">{{ option.label }}</mat-option> <mat-option [value]="option.value">{{ option.label }}</mat-option>
} }
</mat-select> </mat-select>
@if (getFieldError(authForm, 'authModeToAccessRoom')) {
<mat-error>{{ getFieldError(authForm, 'authModeToAccessRoom') }}</mat-error>
}
</mat-form-field> </mat-form-field>
</div> </div>
</form> </form>
</mat-card-content> </mat-card-content>
<mat-card-actions> <mat-card-actions>
<button mat-raised-button color="primary" type="submit" (click)="onSaveAccess()"> <button mat-raised-button color="primary" type="submit" (click)="onSaveAccessSettings()">
Save Configuration Save configuration
</button> </button>
</mat-card-actions> </mat-card-actions>
</mat-card> </mat-card>

View File

@ -1,4 +1,3 @@
import { CommonModule } from '@angular/common';
import { Component, OnInit, signal } from '@angular/core'; import { Component, OnInit, signal } from '@angular/core';
import { FormControl, FormGroup, ReactiveFormsModule, Validators } from '@angular/forms'; import { FormControl, FormGroup, ReactiveFormsModule, Validators } from '@angular/forms';
import { MatButtonModule } from '@angular/material/button'; import { MatButtonModule } from '@angular/material/button';
@ -9,9 +8,7 @@ import { MatIconModule } from '@angular/material/icon';
import { MatInputModule } from '@angular/material/input'; import { MatInputModule } from '@angular/material/input';
import { MatProgressSpinnerModule } from '@angular/material/progress-spinner'; import { MatProgressSpinnerModule } from '@angular/material/progress-spinner';
import { MatSelectModule } from '@angular/material/select'; import { MatSelectModule } from '@angular/material/select';
import { MatSnackBarModule } from '@angular/material/snack-bar'; import { ProFeatureBadgeComponent } from '@lib/components';
import { MatTooltipModule } from '@angular/material/tooltip';
import { LogoSelectorComponent, ProFeatureBadgeComponent } from '@lib/components';
import { AuthService, GlobalPreferencesService, NotificationService } from '@lib/services'; import { AuthService, GlobalPreferencesService, NotificationService } from '@lib/services';
import { AuthMode } from '@lib/typings/ce'; import { AuthMode } from '@lib/typings/ce';
@ -19,41 +16,36 @@ import { AuthMode } from '@lib/typings/ce';
selector: 'ov-preferences', selector: 'ov-preferences',
standalone: true, standalone: true,
imports: [ imports: [
CommonModule,
MatCardModule, MatCardModule,
MatButtonModule, MatButtonModule,
MatIconModule, MatIconModule,
MatInputModule, MatInputModule,
MatFormFieldModule, MatFormFieldModule,
MatSelectModule, MatSelectModule,
MatSnackBarModule,
MatTooltipModule,
MatProgressSpinnerModule, MatProgressSpinnerModule,
MatDividerModule, MatDividerModule,
ReactiveFormsModule, ReactiveFormsModule,
LogoSelectorComponent, ProFeatureBadgeComponent
ProFeatureBadgeComponent,
MatSelectModule
], ],
templateUrl: './users-permissions.component.html', templateUrl: './users-permissions.component.html',
styleUrl: './users-permissions.component.scss' styleUrl: './users-permissions.component.scss'
}) })
export class UsersPermissionsComponent implements OnInit { export class UsersPermissionsComponent implements OnInit {
isLoading = signal(false); isLoading = signal(false);
isSavingBranding = signal(false);
isSavingAccess = signal(false);
authForm = new FormGroup({ adminCredentialsForm = new FormGroup({
adminUsername: new FormControl({ value: '', disabled: true }, [Validators.required]),
adminPassword: new FormControl('', [Validators.required, Validators.minLength(4)])
});
accessSettingsForm = new FormGroup({
authModeToAccessRoom: new FormControl(AuthMode.NONE, [Validators.required]) authModeToAccessRoom: new FormControl(AuthMode.NONE, [Validators.required])
}); });
adminPasswordControl = new FormControl('', [Validators.required, Validators.minLength(8)]);
// Auth mode options for the select dropdown // Auth mode options for the select dropdown
authModeOptions = [ authModeOptions = [
{ value: AuthMode.ALL_USERS, label: 'Everyone' }, { value: AuthMode.NONE, label: 'Nobody' },
{ value: AuthMode.MODERATORS_ONLY, label: 'Only Moderators' }, { value: AuthMode.MODERATORS_ONLY, label: 'Only moderators' },
{ value: AuthMode.NONE, label: 'Nobody' } { value: AuthMode.ALL_USERS, label: 'Everyone' }
]; ];
constructor( constructor(
@ -63,52 +55,72 @@ export class UsersPermissionsComponent implements OnInit {
) {} ) {}
async ngOnInit() { async ngOnInit() {
await this.loadSettings(); this.isLoading.set(true);
await this.loadAdminUsername();
await this.loadAccessSettings();
this.isLoading.set(false);
} }
private async loadSettings() { private async loadAdminUsername() {
this.isLoading.set(true); const username = await this.authService.getUsername();
if (!username) {
console.error('Admin username not found');
this.notificationService.showSnackbar('Failed to load admin username');
return;
}
this.adminCredentialsForm.get('adminUsername')?.setValue(username);
}
private async loadAccessSettings() {
try { try {
const authMode = await this.preferencesService.getAuthModeToAccessRoom(); const authMode = await this.preferencesService.getAuthModeToAccessRoom();
this.authForm.get('authModeToAccessRoom')?.setValue(authMode); this.accessSettingsForm.get('authModeToAccessRoom')?.setValue(authMode);
} catch (error) { } catch (error) {
console.error('Error loading security preferences:', error); console.error('Error loading security preferences:', error);
this.notificationService.showSnackbar('Failed to load security preferences'); this.notificationService.showSnackbar('Failed to load security preferences');
} }
this.isLoading.set(false);
} }
async onSaveAccess() { async onSaveAdminCredentials() {
if (this.authForm.invalid || this.adminPasswordControl.invalid) { if (this.adminCredentialsForm.invalid) {
return; return;
} }
this.isSavingAccess.set(true); const formData = this.adminCredentialsForm.value;
const formData = this.authForm.value; const adminPassword = formData.adminPassword!;
const adminPassword = this.adminPasswordControl.value;
try {
await this.authService.changePassword(adminPassword);
this.notificationService.showSnackbar('Admin credentials updated successfully');
} catch (error) {
console.error('Error saving admin credentials:', error);
this.notificationService.showSnackbar('Failed to save admin credentials');
}
}
async onSaveAccessSettings() {
if (this.accessSettingsForm.invalid) {
return;
}
const formData = this.accessSettingsForm.value;
try { try {
const securityPrefs = await this.preferencesService.getSecurityPreferences(); const securityPrefs = await this.preferencesService.getSecurityPreferences();
securityPrefs.authentication.authModeToAccessRoom = formData.authModeToAccessRoom!; securityPrefs.authentication.authModeToAccessRoom = formData.authModeToAccessRoom!;
await this.preferencesService.saveSecurityPreferences(securityPrefs); await this.preferencesService.saveSecurityPreferences(securityPrefs);
if (adminPassword) {
await this.authService.changePassword(adminPassword);
}
this.notificationService.showSnackbar('Access & Permissions settings saved successfully'); this.notificationService.showSnackbar('Access & Permissions settings saved successfully');
} catch (error) { } catch (error) {
console.error('Error saving access permissions:', error); console.error('Error saving access permissions:', error);
this.notificationService.showSnackbar('Failed to save Access & Permissions settings'); this.notificationService.showSnackbar('Failed to save Access & Permissions settings');
} finally {
this.isSavingAccess.set(false);
} }
} }
// Utility methods for form validation
getAdminPasswordError(): string { getAdminPasswordError(): string {
const control = this.adminPasswordControl; const control = this.adminCredentialsForm.get('adminPassword')!;
if (!control.touched || !control.errors) { if (!control.touched || !control.errors) {
return ''; return '';
} }
@ -123,34 +135,4 @@ export class UsersPermissionsComponent implements OnInit {
return ''; return '';
} }
// Utility methods for form validation
getFieldError(formGroup: FormGroup, fieldName: string): string {
const field = formGroup.get(fieldName);
if (!field || !field.touched || !field.errors) {
return '';
}
const errors = field.errors;
if (errors['required']) {
return `${this.getFieldLabel(fieldName)} is required`;
}
if (errors['minlength']) {
return `${this.getFieldLabel(fieldName)} must be at least ${errors['minlength'].requiredLength} characters`;
}
if (errors['invalidUrl']) {
return `${this.getFieldLabel(fieldName)} must be a valid URL`;
}
return '';
}
private getFieldLabel(fieldName: string): string {
const labels: Record<string, string> = {
logoUrl: 'Logo URL',
authModeToAccessRoom: 'Authentication mode to access room',
adminPassword: 'Admin password'
};
return labels[fieldName] || fieldName;
}
} }