frontend: update users permissions component for improved admin credential handling and access settings
This commit is contained in:
parent
66140468be
commit
9dc2796d24
@ -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>
|
||||||
|
|||||||
@ -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;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user