diff --git a/src/app/profiles/profile-detail/profile-detail.component.html b/src/app/profiles/profile-detail/profile-detail.component.html index 0ca566f15..bdb7adddd 100644 --- a/src/app/profiles/profile-detail/profile-detail.component.html +++ b/src/app/profiles/profile-detail/profile-detail.component.html @@ -320,24 +320,25 @@ {{ 'profileDetail.connectionModeRequired.value' | translate }}

} -
- {{ 'profileDetail.internetModeLegend.value' | translate }} -

{{ 'profileDetail.internetModeDescription.value' | translate }}

-

- - {{ 'profileDetail.ciraCloud.value' | translate }} - @if (!ciraEnabled()) { - {{ 'profileDetail.ciraDisabledOption.value' | translate }} - } @else if (ciraConfigurations().length === 0) { - {{ 'profileDetail.noCiraConfigs.value' | translate }} - } - -

- @if (profileForm.get('connectionMode')?.value === 'CIRA') { - @if (ciraEnabled()) { + + @if (ciraEnabled() || !ciraAvailabilityResolved()) { +
+ {{ 'profileDetail.internetModeLegend.value' | translate }} +

{{ 'profileDetail.internetModeDescription.value' | translate }}

+

+ + {{ 'profileDetail.ciraCloud.value' | translate }} + @if (ciraAvailabilityResolved() && ciraConfigurations().length === 0) { + {{ 'profileDetail.noCiraConfigs.value' | translate }} + } + +

+ @if (profileForm.get('connectionMode')?.value === 'CIRA') { {{ 'profileDetail.ciraConfiguration.value' | translate }} @@ -352,20 +353,9 @@ {{ 'profileDetail.ciraConfigHint.value' | translate }} - } @else { - -

- warning - {{ 'profileDetail.ciraDisabledWarning.value' | translate }} -

- - {{ 'profileDetail.ciraConfiguration.value' | translate }} - - } - } -
+
+ }
{{ 'profileDetail.directConnectLegend.value' | translate }}

@@ -395,6 +385,7 @@ } {{ 'profileDetail.tlsModeHint.value' | translate }} + {{ 'fieldRequired.short.value' | translate }} diff --git a/src/app/profiles/profile-detail/profile-detail.component.spec.ts b/src/app/profiles/profile-detail/profile-detail.component.spec.ts index e03dde026..055243f1d 100644 --- a/src/app/profiles/profile-detail/profile-detail.component.spec.ts +++ b/src/app/profiles/profile-detail/profile-detail.component.spec.ts @@ -8,7 +8,7 @@ import { BrowserAnimationsModule } from '@angular/platform-browser/animations' import { ActivatedRoute, RouterModule } from '@angular/router' import { MatDialog } from '@angular/material/dialog' import { Validators } from '@angular/forms' -import { of, throwError } from 'rxjs' +import { NEVER, of, throwError } from 'rxjs' import { ConfigsService } from '../../configs/configs.service' import { WirelessService } from '../../wireless/wireless.service' import { ProfilesService } from '../profiles.service' @@ -16,6 +16,7 @@ import { IEEE8021xService } from '../../ieee8021x/ieee8021x.service' import { ProxyConfigsService } from '../../proxy-configs/proxy-configs.service' import { ServerFeaturesService } from '../../server-features.service' import { ProfileDetailComponent } from './profile-detail.component' +import { NoCIRAWarningComponent } from '../../shared/no-cira-warning/no-cira-warning.component' import { Profile } from '../profiles.constants' import { MatChipInputEvent } from '@angular/material/chips' import { MatAutocompleteSelectedEvent } from '@angular/material/autocomplete' @@ -27,6 +28,7 @@ import { provideHttpClientTesting } from '@angular/common/http/testing' import { provideTranslateHttpLoader, TRANSLATE_HTTP_LOADER_CONFIG } from '@ngx-translate/http-loader' describe('ProfileDetailComponent', () => { + const defaultCloudMode = environment.cloud let component: ProfileDetailComponent let fixture: ComponentFixture let profileSpy: jasmine.Spy @@ -148,6 +150,7 @@ describe('ProfileDetailComponent', () => { }) afterEach(() => { + environment.cloud = defaultCloudMode TestBed.resetTestingModule() }) @@ -174,6 +177,7 @@ describe('ProfileDetailComponent', () => { expect(wirelessGetDataSpy).toHaveBeenCalled() expect(proxyGetDataSpy).toHaveBeenCalled() }) + it('should set connectionMode to TLS when tlsMode is a TLS mode (1-4)', () => { const profile: Profile = { tlsMode: 4, ciraConfigName: 'config1' } as any component.setConnectionMode(profile) @@ -184,6 +188,26 @@ describe('ProfileDetailComponent', () => { component.setConnectionMode(profile) expect(component.profileForm.controls.connectionMode.value).toBe('CIRA') }) + it('should not set connectionMode to CIRA when CIRA is disabled and availability is resolved', () => { + component.profileForm.controls.ciraConfigName.setValue('config1') + component.ciraEnabled.set(false) + component.ciraAvailabilityResolved.set(true) + + const profile: Profile = { ciraConfigName: 'config1' } as any + component.setConnectionMode(profile) + + expect(component.profileForm.controls.connectionMode.value).toBe('TLS') + expect(component.profileForm.controls.ciraConfigName.value).toBeNull() + }) + it('should keep CIRA connectionMode before enterprise feature availability resolves', () => { + component.ciraEnabled.set(false) + component.ciraAvailabilityResolved.set(false) + + const profile: Profile = { ciraConfigName: 'config1' } as any + component.setConnectionMode(profile) + + expect(component.profileForm.controls.connectionMode.value).toBe('CIRA') + }) it('should set connectionMode to DIRECT when tlsMode is 0 and no CIRA config', () => { const profile: Profile = { tlsMode: 0, ciraConfigName: null } as any component.setConnectionMode(profile) @@ -693,6 +717,18 @@ describe('ProfileDetailComponent', () => { expect(component.profileForm.controls.tlsSigningAuthority.value).toEqual(component.tlsDefaultSigningAuthority) expect(component.profileForm.controls.tlsSigningAuthority.valid).toBeTrue() }) + it('should clear a non-TLS tlsMode of 0 when TLS is selected', () => { + component.profileForm.controls.tlsMode.setValue(0) + component.connectionModeChange('TLS') + expect(component.profileForm.controls.tlsMode.value).toEqual(null) + expect(component.profileForm.controls.tlsMode.valid).toBeFalse() + }) + it('should keep an already selected tlsMode when TLS is selected', () => { + component.profileForm.controls.tlsMode.setValue(2) + component.connectionModeChange('TLS') + expect(component.profileForm.controls.tlsMode.value).toEqual(2) + expect(component.profileForm.controls.tlsMode.valid).toBeTrue() + }) it('should set the tlsMode property to null when CIRA Selected', () => { component.connectionModeChange('CIRA') expect(component.profileForm.controls.tlsMode.value).toEqual(null) @@ -835,9 +871,11 @@ describe('ProfileDetailComponent', () => { // server-features branch instead of the cloud branch. const createEnterpriseComponent = (): ProfileDetailComponent => { environment.cloud = false - const enterpriseFixture = TestBed.createComponent(ProfileDetailComponent) - enterpriseFixture.detectChanges() - return enterpriseFixture.componentInstance + fixture.destroy() + fixture = TestBed.createComponent(ProfileDetailComponent) + component = fixture.componentInstance + fixture.detectChanges() + return component } afterEach(() => { @@ -848,10 +886,12 @@ describe('ProfileDetailComponent', () => { serverFeaturesGetFeaturesSpy.and.returnValue(of({ ciraEnabled: false })) ciraGetDataSpy.calls.reset() - createEnterpriseComponent() + const enterpriseComponent = createEnterpriseComponent() expect(serverFeaturesGetFeaturesSpy).toHaveBeenCalled() expect(ciraGetDataSpy).not.toHaveBeenCalled() + expect(enterpriseComponent.ciraEnabled()).toBeFalse() + expect(fixture.nativeElement.querySelector('[data-cy="radio-cira"]')).toBeNull() }) it('should expose ciraEnabled() === false after the features call resolves with CIRA disabled', () => { @@ -870,6 +910,7 @@ describe('ProfileDetailComponent', () => { expect(ciraGetDataSpy).toHaveBeenCalled() expect(enterpriseComponent.ciraEnabled()).toBeTrue() + expect(fixture.nativeElement.querySelector('[data-cy="radio-cira"]')).not.toBeNull() }) it('should fail open and fetch CIRA configs when the features call errors', () => { @@ -880,6 +921,99 @@ describe('ProfileDetailComponent', () => { expect(ciraGetDataSpy).toHaveBeenCalled() expect(enterpriseComponent.ciraEnabled()).toBeTrue() + expect(fixture.nativeElement.querySelector('[data-cy="radio-cira"]')).not.toBeNull() + }) + + // Mike's review feedback on #3445: hiding the section while /server/features is still in + // flight leaves a saved CIRA profile with no visible selection at all, which reads as a bug + // on a server where CIRA is actually enabled. + it('should keep the CIRA option visible but disabled while the features call is in flight', () => { + serverFeaturesGetFeaturesSpy.and.returnValue(NEVER) + + const enterpriseComponent = createEnterpriseComponent() + loadProfileForEdit({ ciraConfigName: 'config1' }) + fixture.detectChanges() + + const radio = fixture.nativeElement.querySelector('[data-cy="radio-cira"]') + expect(enterpriseComponent.ciraAvailabilityResolved()).toBeFalse() + expect(radio).not.toBeNull() + expect(radio.classList).toContain('mat-mdc-radio-disabled') + // The saved CIRA selection stays visible rather than rendering an empty radio group. + expect(enterpriseComponent.profileForm.controls.connectionMode.value).toBe('CIRA') + }) + + it('should warn before saving an edited profile whose stored CIRA config is about to be dropped', () => { + serverFeaturesGetFeaturesSpy.and.returnValue(of({ ciraEnabled: false })) + const enterpriseComponent = createEnterpriseComponent() + + // Stored profile still references CIRA, so setConnectionMode() coerces the form to TLS. + loadProfileForEdit({ generateRandomPassword: true, generateRandomMEBxPassword: true, ciraConfigName: 'config1' }) + expect(enterpriseComponent.profileForm.controls.connectionMode.value).toBe('TLS') + + spyOn(enterpriseComponent.router, 'navigate') + const dialogSpy = spyOn(TestBed.inject(MatDialog), 'open').and.returnValue({ + afterClosed: () => of(true) + } as any) + enterpriseComponent.profileForm.patchValue({ profileName: 'profile', dhcpEnabled: true, tlsMode: 1 }) + enterpriseComponent.confirm() + + expect(dialogSpy).toHaveBeenCalledWith(NoCIRAWarningComponent, jasmine.anything()) + expect(profileUpdateSpy).toHaveBeenCalled() + }) + + // A Console that answers 200 without the flag must not read as "CIRA disabled", or the + // save flow would offer to drop a stored CIRA config on a server where CIRA still works. + it('should fail open when the features response omits ciraEnabled', () => { + serverFeaturesGetFeaturesSpy.and.returnValue(of({} as any)) + ciraGetDataSpy.calls.reset() + + const enterpriseComponent = createEnterpriseComponent() + + expect(ciraGetDataSpy).toHaveBeenCalled() + expect(enterpriseComponent.ciraEnabled()).toBeTrue() + expect(fixture.nativeElement.querySelector('[data-cy="radio-cira"]')).not.toBeNull() + }) + + it('should not warn about a dropped CIRA config while the features call is in flight', () => { + serverFeaturesGetFeaturesSpy.and.returnValue(NEVER) + const enterpriseComponent = createEnterpriseComponent() + + loadProfileForEdit({ generateRandomPassword: true, generateRandomMEBxPassword: true, ciraConfigName: 'config1' }) + expect(enterpriseComponent.ciraAvailabilityResolved()).toBeFalse() + + spyOn(enterpriseComponent.router, 'navigate') + const dialogSpy = spyOn(TestBed.inject(MatDialog), 'open') + // User switches the profile to TLS themselves before the server answers. + enterpriseComponent.profileForm.patchValue({ + profileName: 'profile', + dhcpEnabled: true, + connectionMode: 'TLS', + tlsMode: 1 + }) + enterpriseComponent.confirm() + + expect(dialogSpy).not.toHaveBeenCalled() + expect(profileUpdateSpy).toHaveBeenCalled() + }) + + it('should not warn when saving a non-CIRA profile that never had a CIRA config', () => { + serverFeaturesGetFeaturesSpy.and.returnValue(of({ ciraEnabled: false })) + const enterpriseComponent = createEnterpriseComponent() + + loadProfileForEdit({ generateRandomPassword: true, generateRandomMEBxPassword: true }) + + spyOn(enterpriseComponent.router, 'navigate') + const dialogSpy = spyOn(TestBed.inject(MatDialog), 'open') + enterpriseComponent.profileForm.patchValue({ + profileName: 'profile', + dhcpEnabled: true, + connectionMode: 'TLS', + tlsMode: 1 + }) + enterpriseComponent.confirm() + + expect(dialogSpy).not.toHaveBeenCalled() + expect(profileUpdateSpy).toHaveBeenCalled() }) }) diff --git a/src/app/profiles/profile-detail/profile-detail.component.ts b/src/app/profiles/profile-detail/profile-detail.component.ts index c61998aaf..53830ccde 100644 --- a/src/app/profiles/profile-detail/profile-detail.component.ts +++ b/src/app/profiles/profile-detail/profile-detail.component.ts @@ -8,8 +8,8 @@ import { FormBuilder, FormControl, Validators, ReactiveFormsModule } from '@angu import { MatSnackBar } from '@angular/material/snack-bar' import { MatDialog, MatDialogConfig } from '@angular/material/dialog' import { ActivatedRoute, Router } from '@angular/router' -import { finalize, map, startWith } from 'rxjs/operators' -import { forkJoin, Observable } from 'rxjs' +import { concatMap, finalize, map, startWith, takeWhile, toArray } from 'rxjs/operators' +import { from, Observable } from 'rxjs' import { COMMA, ENTER } from '@angular/cdk/keycodes' import { CdkDragDrop, moveItemInArray, CdkDropList, CdkDrag } from '@angular/cdk/drag-drop' import { NgClass, AsyncPipe } from '@angular/common' @@ -44,6 +44,7 @@ import { ServerFeaturesService } from '../../server-features.service' // Shared components import { RandomPassAlertComponent } from '../../shared/random-pass-alert/random-pass-alert.component' import { StaticCIRAWarningComponent } from '../../shared/static-cira-warning/static-cira-warning.component' +import { NoCIRAWarningComponent } from '../../shared/no-cira-warning/no-cira-warning.component' // Models and constants import { CIRAConfig, IEEE8021xConfig } from '../../../models/models' @@ -144,9 +145,12 @@ export class ProfileDetailComponent implements OnInit { public readonly cloudMode = environment.cloud // CIRA connection-mode availability. Cloud (MPS+RPS) always supports it; in // enterprise it is driven by the Console server's APP_DISABLE_CIRA setting - // (fetched on init). Start from cloudMode so enterprise hides CIRA until the - // API responds, avoiding a flash when the server reports CIRA disabled. + // (fetched on init). public readonly ciraEnabled = signal(this.cloudMode) + // Enterprise starts with ciraEnabled=false before server features return; track + // when availability is actually known so the template can keep CIRA visible (but + // disabled) while the call is in flight, and so a saved CIRA profile isn't coerced too early. + public readonly ciraAvailabilityResolved = signal(this.cloudMode) public readonly isLoading = signal(false) public readonly errorMessages = signal([]) @@ -177,6 +181,7 @@ export class ProfileDetailComponent implements OnInit { private originalGenerateRandomPassword = true private originalGenerateRandomMEBxPassword = true private originalActivation = '' + private originalCiraConfigName: string | null = null // Computed properties public readonly showIEEE8021xConfigurations = computed(() => this.iee8021xConfigurations().length > 0) @@ -216,18 +221,36 @@ export class ProfileDetailComponent implements OnInit { // otherwise the CIRA-configs endpoint 404s. this.serverFeaturesService.getFeatures().subscribe({ next: (features) => { - this.ciraEnabled.set(features.ciraEnabled) - if (features.ciraEnabled) this.getCiraConfigs() + // Fail open like the error handler: only an explicit false disables CIRA, so a + // response without the flag can never coerce the form or offer to drop a config. + const ciraEnabled = features?.ciraEnabled !== false + this.ciraEnabled.set(ciraEnabled) + this.ciraAvailabilityResolved.set(true) + if (ciraEnabled) { + this.getCiraConfigs() + } else { + this.coerceConnectionModeIfCiraUnavailable() + } }, // Fail open: if the features call fails, assume CIRA is enabled. error: () => { this.ciraEnabled.set(true) + this.ciraAvailabilityResolved.set(true) this.getCiraConfigs() } }) } } + private coerceConnectionModeIfCiraUnavailable(): void { + if (this.profileForm.controls.connectionMode.value !== this.connectionMode.cira) { + return + } + + // CIRA is unavailable, so prefer secure direct TLS over unsecured DIRECT. + this.profileForm.controls.connectionMode.setValue(this.connectionMode.tls) + } + private setupFormSubscriptions(): void { this.profileForm.controls.activation.valueChanges.subscribe((value) => { if (value) this.activationChange(value) @@ -262,10 +285,15 @@ export class ProfileDetailComponent implements OnInit { } setConnectionMode(data: Profile): void { + const canUseCira = this.ciraEnabled() || !this.ciraAvailabilityResolved() + if (data.tlsMode != null && data.tlsMode > 0) { this.profileForm.controls.connectionMode.setValue(this.connectionMode.tls) - } else if (data.ciraConfigName != null) { + } else if (data.ciraConfigName != null && canUseCira) { this.profileForm.controls.connectionMode.setValue(this.connectionMode.cira) + } else if (data.ciraConfigName != null && !canUseCira) { + // Existing CIRA profiles should default to TLS when CIRA is disabled. + this.profileForm.controls.connectionMode.setValue(this.connectionMode.tls) } else { this.profileForm.controls.connectionMode.setValue(this.connectionMode.direct) } @@ -327,6 +355,7 @@ export class ProfileDetailComponent implements OnInit { this.originalGenerateRandomPassword = data.generateRandomPassword !== false this.originalGenerateRandomMEBxPassword = data.generateRandomMEBxPassword !== false this.originalActivation = data.activation ?? '' + this.originalCiraConfigName = data.ciraConfigName ?? null this.profileForm.patchValue(data as any) this.selectedWifiConfigs.set(data.wifiConfigs ?? []) // Ensure proxy configs have proper priorities @@ -480,6 +509,12 @@ export class ProfileDetailComponent implements OnInit { if (value === this.connectionMode.tls) { this.profileForm.controls.ciraConfigName.clearValidators() this.profileForm.controls.ciraConfigName.setValue(null) + // A stored non-TLS profile comes back with tlsMode 0, which Validators.required accepts + // (it only rejects null/empty) while the select renders blank. Blank it out so the user + // has to pick a real mode instead of silently saving a TLS profile with TLS off. + if (!TlsModes.some((mode) => mode.value === this.profileForm.controls.tlsMode.value)) { + this.profileForm.controls.tlsMode.setValue(null) + } this.profileForm.controls.tlsMode.setValidators(Validators.required) // set a default value if not set already if (!this.profileForm.controls.tlsSigningAuthority.value) { @@ -660,6 +695,11 @@ export class ProfileDetailComponent implements OnInit { return dialog.afterClosed() } + private noCIRAWarning(): Observable { + const dialog = this.dialog.open(NoCIRAWarningComponent, { width: '750px' }) + return dialog.afterClosed() + } + private randPasswordWarning(): Observable { const dialog = this.dialog.open(RandomPassAlertComponent, this.matDialogConfig) return dialog.afterClosed() @@ -670,23 +710,42 @@ export class ProfileDetailComponent implements OnInit { // Warn user of risk if CIRA configuration and static network are selected simultaneously if (this.profileForm.valid) { const result: any = Object.assign({}, this.profileForm.getRawValue()) - const dialogs = [] - if (!this.isEdit() && (result.generateRandomPassword || result.generateRandomMEBxPassword)) { - dialogs.push(this.randPasswordWarning()) + const dialogs: (() => Observable)[] = [] + // Only warn when saving actually drops a stored CIRA config, i.e. the profile was + // created with one and the server has confirmed CIRA is disabled. Checking + // ciraAvailabilityResolved() keeps the dialog from claiming CIRA is off while the + // features call is still in flight, matching setConnectionMode() and the template. + if ( + this.isEdit() && + this.ciraAvailabilityResolved() && + !this.ciraEnabled() && + this.originalCiraConfigName != null && + result.connectionMode !== this.connectionMode.cira + ) { + dialogs.push(() => this.noCIRAWarning()) } if (result.connectionMode === this.connectionMode.cira && result.dhcpEnabled === false) { - dialogs.push(this.CIRAStaticWarning()) + dialogs.push(() => this.CIRAStaticWarning()) + } + if (!this.isEdit() && (result.generateRandomPassword || result.generateRandomMEBxPassword)) { + dialogs.push(() => this.randPasswordWarning()) } if (dialogs.length === 0) { this.onSubmit() return } - forkJoin(dialogs).subscribe((data) => { - if (data.every((x) => x === true)) { - this.onSubmit() - } - }) + from(dialogs) + .pipe( + concatMap((factory) => factory()), + takeWhile((result) => result === true), + toArray() + ) + .subscribe((results) => { + if (results.length === dialogs.length) { + this.onSubmit() + } + }) } else { this.profileForm.markAllAsTouched() } diff --git a/src/app/shared/no-cira-warning/no-cira-warning.component.html b/src/app/shared/no-cira-warning/no-cira-warning.component.html new file mode 100644 index 000000000..6521263f6 --- /dev/null +++ b/src/app/shared/no-cira-warning/no-cira-warning.component.html @@ -0,0 +1,16 @@ +

+

+ warning + {{ 'noCira.title.value' | translate }} +

+ +

{{ 'noCira.message.value' | translate }}

+

{{ 'noCira.hint.value' | translate }}

+

{{ 'noCira.confirm.value' | translate }}

+
+ + + + diff --git a/src/app/shared/no-cira-warning/no-cira-warning.component.scss b/src/app/shared/no-cira-warning/no-cira-warning.component.scss new file mode 100644 index 000000000..de743bd4b --- /dev/null +++ b/src/app/shared/no-cira-warning/no-cira-warning.component.scss @@ -0,0 +1,22 @@ +.dialog-top-spacer { + height: 20px; +} + +h2[mat-dialog-title] { + display: flex; + align-items: center; + gap: 8px; +} + +mat-dialog-content { + padding: 0 24px; + color: #616161; + + p { + color: #616161; + } +} + +mat-dialog-content.mat-typography p { + color: #616161; +} diff --git a/src/app/shared/no-cira-warning/no-cira-warning.component.spec.ts b/src/app/shared/no-cira-warning/no-cira-warning.component.spec.ts new file mode 100644 index 000000000..8436c766b --- /dev/null +++ b/src/app/shared/no-cira-warning/no-cira-warning.component.spec.ts @@ -0,0 +1,39 @@ +/********************************************************************* + * Copyright (c) Intel Corporation 2022 + * SPDX-License-Identifier: Apache-2.0 + **********************************************************************/ + +import { ComponentFixture, TestBed } from '@angular/core/testing' +import { MatDialogModule } from '@angular/material/dialog' + +import { NoCIRAWarningComponent } from './no-cira-warning.component' +import { provideTranslateService } from '@ngx-translate/core' + +describe('NoCIRAWarningComponent', () => { + let component: NoCIRAWarningComponent + let fixture: ComponentFixture + + beforeEach(() => { + TestBed.configureTestingModule({ + imports: [ + MatDialogModule, + NoCIRAWarningComponent + ], + providers: [provideTranslateService()] + }) + }) + + beforeEach(() => { + fixture = TestBed.createComponent(NoCIRAWarningComponent) + component = fixture.componentInstance + fixture.detectChanges() + }) + + afterEach(() => { + TestBed.resetTestingModule() + }) + + it('should create', () => { + expect(component).toBeTruthy() + }) +}) diff --git a/src/app/shared/no-cira-warning/no-cira-warning.component.ts b/src/app/shared/no-cira-warning/no-cira-warning.component.ts new file mode 100644 index 000000000..9297760d0 --- /dev/null +++ b/src/app/shared/no-cira-warning/no-cira-warning.component.ts @@ -0,0 +1,28 @@ +/********************************************************************* + * Copyright (c) Intel Corporation 2022 + * SPDX-License-Identifier: Apache-2.0 + **********************************************************************/ + +import { Component } from '@angular/core' +import { MatButton } from '@angular/material/button' +import { CdkScrollable } from '@angular/cdk/scrolling' +import { MatDialogTitle, MatDialogContent, MatDialogActions, MatDialogClose } from '@angular/material/dialog' +import { TranslatePipe } from '@ngx-translate/core' +import { MatIcon } from '@angular/material/icon' + +@Component({ + selector: 'app-no-cira-warning', + templateUrl: './no-cira-warning.component.html', + styleUrls: ['./no-cira-warning.component.scss'], + imports: [ + MatDialogTitle, + CdkScrollable, + MatDialogContent, + MatDialogActions, + MatButton, + TranslatePipe, + MatDialogClose, + MatIcon + ] +}) +export class NoCIRAWarningComponent {} diff --git a/src/assets/i18n/ar.json b/src/assets/i18n/ar.json index b5ee316f8..de08542c7 100644 --- a/src/assets/i18n/ar.json +++ b/src/assets/i18n/ar.json @@ -2176,6 +2176,22 @@ "description": "عنوان بطاقة الشبكات اللاسلكية", "value": "الشبكات اللاسلكية" }, + "noCira.confirm": { + "description": "سؤال تأكيد مربع الحوار لتحذير بدون CIRA", + "value": "هل تريد المتابعة؟" + }, + "noCira.hint": { + "description": "تلميح مربع الحوار لشرح كيفية إعادة تمكين CIRA على الخادم", + "value": "للحفاظ على تكوين CIRA، قم بتمكين CIRA على الخادم عن طريق تعيين APP_DISABLE_CIRA إلى false." + }, + "noCira.message": { + "description": "رسالة مربع الحوار تحذر من إزالة CIRA من الملف الشخصي", + "value": "يحتوي هذا الملف الشخصي على تكوين CIRA موجود، ولكن CIRA معطّل حاليًا على الخادم. سيؤدي حفظ هذا الملف الشخصي إلى إزالة تكوين CIRA." + }, + "noCira.title": { + "description": "عنوان مربع الحوار لتحذير إزالة CIRA", + "value": "سيتم إزالة تكوين CIRA" + }, "pba.label": { "description": "التسمية الخاصة بخانة الاختيار لتمكين أو تعطيل تطبيق الإقلاع الآمن أو تعطيله", "value": "فرض التمهيد الآمن" @@ -2293,10 +2309,6 @@ "description": "تسمية حقل تكوين CIRA", "value": "تكوين CIRA" }, - "profileDetail.ciraDisabledOption": { - "description": "Inline note on the CIRA radio when CIRA is disabled on the server", - "value": "(CIRA معطّل على هذا الخادم)" - }, "profileDetail.ciraDisabledWarning": { "description": "Warning shown when editing a profile that uses CIRA while CIRA is disabled on the server", "value": "CIRA معطّل على هذا الخادم. يستخدم هذا الملف الشخصي CIRA لاتصال الإدارة الخاص به ولن يعمل حتى تتم إعادة تمكين CIRA." diff --git a/src/assets/i18n/de.json b/src/assets/i18n/de.json index 9e671b31f..0fade9a0c 100644 --- a/src/assets/i18n/de.json +++ b/src/assets/i18n/de.json @@ -2172,6 +2172,22 @@ "description": "Titel der Karte „Drahtlose Netzwerke”", "value": "Drahtlose Netzwerke" }, + "noCira.confirm": { + "description": "Bestätigungsfrage im Dialog für die CIRA-Warnung", + "value": "Möchten Sie fortfahren?" + }, + "noCira.hint": { + "description": "Hinweis im Dialog zur Wiederaktivierung von CIRA auf dem Server", + "value": "Um die CIRA-Konfiguration beizubehalten, aktivieren Sie CIRA auf dem Server, indem Sie APP_DISABLE_CIRA auf false setzen." + }, + "noCira.message": { + "description": "Dialogmeldung mit Warnung, dass CIRA aus dem Profil entfernt wird", + "value": "Dieses Profil verfügt über eine vorhandene CIRA-Konfiguration, aber CIRA ist derzeit auf dem Server deaktiviert. Das Speichern dieses Profils entfernt die CIRA-Konfiguration." + }, + "noCira.title": { + "description": "Dialog-Titel für CIRA-Entfernungswarnung", + "value": "CIRA-Konfiguration wird entfernt" + }, "pba.label": { "description": "Bezeichnung für das Kontrollkästchen zum Aktivieren oder Deaktivieren der sicheren Boot-Erzwingung", "value": "Secure Boot erzwingen" @@ -2289,10 +2305,6 @@ "description": "Bezeichnung für das CIRA-Konfigurationsfeld", "value": "CIRA-Konfiguration" }, - "profileDetail.ciraDisabledOption": { - "description": "Inline note on the CIRA radio when CIRA is disabled on the server", - "value": "(CIRA ist auf diesem Server deaktiviert)" - }, "profileDetail.ciraDisabledWarning": { "description": "Warning shown when editing a profile that uses CIRA while CIRA is disabled on the server", "value": "CIRA ist auf diesem Server deaktiviert. Dieses Profil verwendet CIRA für seine Verwaltungsverbindung und funktioniert erst, wenn CIRA wieder aktiviert wird." diff --git a/src/assets/i18n/en.json b/src/assets/i18n/en.json index ab3c01b2c..a19e76dec 100644 --- a/src/assets/i18n/en.json +++ b/src/assets/i18n/en.json @@ -2777,6 +2777,22 @@ "description": "Warning message shown when adding a profile while WiFi is disabled", "value": "WiFi is currently disabled. This profile will not take effect until WiFi is enabled." }, + "noCira.confirm": { + "description": "Dialog confirmation question for no CIRA warning", + "value": "Do you want to continue?" + }, + "noCira.hint": { + "description": "Dialog hint explaining how to re-enable CIRA on the server", + "value": "To keep the CIRA configuration, enable CIRA on the server by setting APP_DISABLE_CIRA to false." + }, + "noCira.message": { + "description": "Dialog message warning that CIRA will be removed from the profile", + "value": "This profile has an existing CIRA configuration, but CIRA is currently disabled on the server. Saving this profile will remove the CIRA configuration." + }, + "noCira.title": { + "description": "Dialog title for CIRA removal warning", + "value": "CIRA configuration will be removed" + }, "pba.enforceCCMMessage": { "value": "Secure Boot is always enforced in Client Control Mode (CCM)" }, @@ -2897,10 +2913,6 @@ "description": "Label for CIRA configuration field", "value": "CIRA Configuration" }, - "profileDetail.ciraDisabledOption": { - "description": "Inline note on the CIRA radio when CIRA is disabled on the server", - "value": "(CIRA is disabled on this server)" - }, "profileDetail.ciraDisabledWarning": { "description": "Warning shown when editing a profile that uses CIRA while CIRA is disabled on the server", "value": "CIRA is disabled on this server. This profile uses CIRA for its management connection and won't work until CIRA is re-enabled." diff --git a/src/assets/i18n/es.json b/src/assets/i18n/es.json index eca95d58a..12b610279 100644 --- a/src/assets/i18n/es.json +++ b/src/assets/i18n/es.json @@ -2172,6 +2172,22 @@ "description": "Título de la tarjeta de redes inalámbricas", "value": "Redes inalámbricas" }, + "noCira.confirm": { + "description": "Pregunta de confirmación del cuadro de diálogo para la advertencia sin CIRA", + "value": "¿Desea continuar?" + }, + "noCira.hint": { + "description": "Sugerencia del cuadro de diálogo para explicar cómo volver a habilitar CIRA en el servidor", + "value": "Para mantener la configuración CIRA, habilite CIRA en el servidor estableciendo APP_DISABLE_CIRA en false." + }, + "noCira.message": { + "description": "Mensaje del cuadro de diálogo que advierte que CIRA se eliminará del perfil", + "value": "Este perfil tiene una configuración CIRA existente, pero CIRA está actualmente deshabilitado en el servidor. Guardar este perfil eliminará la configuración CIRA." + }, + "noCira.title": { + "description": "Título del cuadro de diálogo para la advertencia de eliminación de CIRA", + "value": "Se eliminará la configuración CIRA" + }, "pba.label": { "description": "Etiqueta para la casilla de verificación para activar o desactivar la aplicación de arranque seguro", "value": "Aplicar Secure Boot" @@ -2289,10 +2305,6 @@ "description": "Etiqueta para el campo de configuración CIRA.", "value": "Configuración de CIRA" }, - "profileDetail.ciraDisabledOption": { - "description": "Inline note on the CIRA radio when CIRA is disabled on the server", - "value": "(CIRA está deshabilitado en este servidor)" - }, "profileDetail.ciraDisabledWarning": { "description": "Warning shown when editing a profile that uses CIRA while CIRA is disabled on the server", "value": "CIRA está deshabilitado en este servidor. Este perfil usa CIRA para su conexión de administración y no funcionará hasta que se vuelva a habilitar CIRA." diff --git a/src/assets/i18n/fi.json b/src/assets/i18n/fi.json index b6e8b1e5c..601fe9c74 100644 --- a/src/assets/i18n/fi.json +++ b/src/assets/i18n/fi.json @@ -2172,6 +2172,22 @@ "description": "Langattomien verkkojen kortin otsikko", "value": "Langattomat verkot" }, + "noCira.confirm": { + "description": "Vahvistuskysymys valintaikkunassa CIRA-varoitukselle", + "value": "Haluatko jatkaa?" + }, + "noCira.hint": { + "description": "Valintaikkunan vihje CIRA:n uudelleenaktivoimiseksi palvelimella", + "value": "Säilytä CIRA-kokoonpano ottamalla CIRA käyttöön palvelimella asettamalla APP_DISABLE_CIRA arvoon false." + }, + "noCira.message": { + "description": "Valintaikkunan viesti, joka varoittaa CIRA:n poistamisesta profiilista", + "value": "Tällä profiililla on olemassa oleva CIRA-kokoonpano, mutta CIRA on tällä hetkellä poistettu käytöstä palvelimella. Tämän profiilin tallentaminen poistaa CIRA-kokoonpanon." + }, + "noCira.title": { + "description": "Valintaikkunan otsikko CIRA:n poistovaroitukselle", + "value": "CIRA-kokoonpano poistetaan" + }, "pba.label": { "description": "Merkintä valintaruudulle, jolla suojatun käynnistyksen valvonta otetaan käyttöön tai poistetaan käytöstä.", "value": "Secure Bootin käyttöönotto" @@ -2289,10 +2305,6 @@ "description": "CIRA-määrityskentän nimi", "value": "CIRA-konfiguraatio" }, - "profileDetail.ciraDisabledOption": { - "description": "Inline note on the CIRA radio when CIRA is disabled on the server", - "value": "(CIRA on poistettu käytöstä tällä palvelimella)" - }, "profileDetail.ciraDisabledWarning": { "description": "Warning shown when editing a profile that uses CIRA while CIRA is disabled on the server", "value": "CIRA on poistettu käytöstä tällä palvelimella. Tämä profiili käyttää CIRAa hallintayhteyteensä eikä toimi, ennen kuin CIRA otetaan uudelleen käyttöön." diff --git a/src/assets/i18n/fr.json b/src/assets/i18n/fr.json index 7c248c9e9..161d5ad0d 100644 --- a/src/assets/i18n/fr.json +++ b/src/assets/i18n/fr.json @@ -2176,6 +2176,22 @@ "description": "Titre de la carte des réseaux sans fil", "value": "Réseaux sans fil" }, + "noCira.confirm": { + "description": "Question de confirmation de la boîte de dialogue pour l'avertissement sans CIRA", + "value": "Voulez-vous continuer ?" + }, + "noCira.hint": { + "description": "Indication de la boîte de dialogue pour expliquer comment réactiver CIRA sur le serveur", + "value": "Pour conserver la configuration CIRA, activez CIRA sur le serveur en définissant APP_DISABLE_CIRA sur false." + }, + "noCira.message": { + "description": "Message de la boîte de dialogue avertissant que la configuration CIRA sera supprimée du profil", + "value": "Ce profil dispose d'une configuration CIRA existante, mais CIRA est actuellement désactivé sur le serveur. L'enregistrement de ce profil supprimera la configuration CIRA." + }, + "noCira.title": { + "description": "Titre de la boîte de dialogue pour l'avertissement de suppression de CIRA", + "value": "La configuration CIRA sera supprimée" + }, "pba.label": { "description": "Étiquette de la case à cocher permettant d'activer ou de désactiver la mise en œuvre du démarrage sécurisé", "value": "Mise en œuvre de l'amorçage sécurisé" @@ -2293,10 +2309,6 @@ "description": "Étiquette pour le champ de configuration CIRA", "value": "Configuration CIRA" }, - "profileDetail.ciraDisabledOption": { - "description": "Inline note on the CIRA radio when CIRA is disabled on the server", - "value": "(CIRA est désactivé sur ce serveur)" - }, "profileDetail.ciraDisabledWarning": { "description": "Warning shown when editing a profile that uses CIRA while CIRA is disabled on the server", "value": "CIRA est désactivé sur ce serveur. Ce profil utilise CIRA pour sa connexion de gestion et ne fonctionnera pas tant que CIRA n'est pas réactivé." diff --git a/src/assets/i18n/he.json b/src/assets/i18n/he.json index f8f972955..9bde55f65 100644 --- a/src/assets/i18n/he.json +++ b/src/assets/i18n/he.json @@ -2168,6 +2168,22 @@ "description": "כותרת כרטיס הרשתות האלחוטיות", "value": "רשתות אלחוטיות" }, + "noCira.confirm": { + "description": "שאלת אישור בתיבת הדו-שיח לאזהרה ללא CIRA", + "value": "האם ברצונך להמשיך?" + }, + "noCira.hint": { + "description": "רמז בתיבת הדו-שיח להסבר כיצד להפעיל מחדש את CIRA בשרת", + "value": "כדי לשמור את תצורת ה-CIRA, הפעל את CIRA בשרת על ידי הגדרת APP_DISABLE_CIRA ל-false." + }, + "noCira.message": { + "description": "הודעת תיבת הדו-שיח המזהירה שתצורת CIRA תוסר מהפרופיל", + "value": "לפרופיל זה יש תצורת CIRA קיימת, אך CIRA כרגע מושבת בשרת. שמירת פרופיל זה תסיר את תצורת ה-CIRA." + }, + "noCira.title": { + "description": "כותרת תיבת הדו-שיח לאזהרת הסרת CIRA", + "value": "תצורת CIRA תוסר" + }, "pba.label": { "description": "תווית לתיבת הסימון כדי להפעיל או להשבית אכיפת אתחול מאובטחת", "value": "אוכף אתחול מאובטח" @@ -2285,10 +2301,6 @@ "description": "תווית לשדה תצורת CIRA", "value": "תצורת CIRA" }, - "profileDetail.ciraDisabledOption": { - "description": "Inline note on the CIRA radio when CIRA is disabled on the server", - "value": "(CIRA מושבת בשרת זה)" - }, "profileDetail.ciraDisabledWarning": { "description": "Warning shown when editing a profile that uses CIRA while CIRA is disabled on the server", "value": "CIRA מושבת בשרת זה. פרופיל זה משתמש ב-CIRA לחיבור הניהול שלו ולא יעבוד עד ש-CIRA יופעל מחדש." diff --git a/src/assets/i18n/it.json b/src/assets/i18n/it.json index a385a6dc0..2fc13d98e 100644 --- a/src/assets/i18n/it.json +++ b/src/assets/i18n/it.json @@ -2168,6 +2168,22 @@ "description": "Titolo della scheda delle reti wireless", "value": "Reti wireless" }, + "noCira.confirm": { + "description": "Domanda di conferma della finestra di dialogo per l'avviso senza CIRA", + "value": "Vuoi continuare?" + }, + "noCira.hint": { + "description": "Suggerimento della finestra di dialogo per spiegare come riabilitare CIRA sul server", + "value": "Per mantenere la configurazione CIRA, abilita CIRA sul server impostando APP_DISABLE_CIRA su false." + }, + "noCira.message": { + "description": "Messaggio della finestra di dialogo che avvisa che la configurazione CIRA verrà rimossa dal profilo", + "value": "Questo profilo ha una configurazione CIRA esistente, ma CIRA è attualmente disabilitato sul server. Il salvataggio di questo profilo rimuoverà la configurazione CIRA." + }, + "noCira.title": { + "description": "Titolo della finestra di dialogo per l'avviso di rimozione CIRA", + "value": "La configurazione CIRA verrà rimossa" + }, "pba.label": { "description": "Etichetta per la casella di controllo per abilitare o disabilitare l'applicazione del secure boot", "value": "Applicare l'avvio sicuro" @@ -2285,10 +2301,6 @@ "description": "Etichetta per il campo di configurazione CIRA", "value": "Configurazione CIRA" }, - "profileDetail.ciraDisabledOption": { - "description": "Inline note on the CIRA radio when CIRA is disabled on the server", - "value": "(CIRA è disabilitato su questo server)" - }, "profileDetail.ciraDisabledWarning": { "description": "Warning shown when editing a profile that uses CIRA while CIRA is disabled on the server", "value": "CIRA è disabilitato su questo server. Questo profilo utilizza CIRA per la sua connessione di gestione e non funzionerà finché CIRA non viene riabilitato." diff --git a/src/assets/i18n/ja.json b/src/assets/i18n/ja.json index 52f10579b..9e1007124 100644 --- a/src/assets/i18n/ja.json +++ b/src/assets/i18n/ja.json @@ -2168,6 +2168,22 @@ "description": "無線ネットワークカードの見出し", "value": "ワイヤレスネットワーク" }, + "noCira.confirm": { + "description": "CIRA警告ダイアログの確認質問", + "value": "続行しますか?" + }, + "noCira.hint": { + "description": "サーバーでCIRAを再有効化する方法を説明するダイアログのヒント", + "value": "CIRA設定を維持するには、APP_DISABLE_CIRAをfalseに設定してサーバーでCIRAを有効にしてください。" + }, + "noCira.message": { + "description": "CIRA設定がプロファイルから削除されることを警告するダイアログメッセージ", + "value": "このプロファイルには既存のCIRA設定がありますが、CIRAは現在サーバーで無効になっています。このプロファイルを保存すると、CIRA設定が削除されます。" + }, + "noCira.title": { + "description": "CIRA削除警告のダイアログタイトル", + "value": "CIRA設定が削除されます" + }, "pba.label": { "description": "セキュアブートの実施を有効または無効にするチェックボックスのラベル", "value": "セキュアブートの強制" @@ -2285,10 +2301,6 @@ "description": "CIRA設定フィールドのラベル", "value": "CIRA設定" }, - "profileDetail.ciraDisabledOption": { - "description": "Inline note on the CIRA radio when CIRA is disabled on the server", - "value": "(このサーバーでは CIRA が無効になっています)" - }, "profileDetail.ciraDisabledWarning": { "description": "Warning shown when editing a profile that uses CIRA while CIRA is disabled on the server", "value": "このサーバーでは CIRA が無効になっています。このプロファイルは管理接続に CIRA を使用しているため、CIRA が再度有効になるまで機能しません。" diff --git a/src/assets/i18n/nl.json b/src/assets/i18n/nl.json index cc91c08f7..439175801 100644 --- a/src/assets/i18n/nl.json +++ b/src/assets/i18n/nl.json @@ -2176,6 +2176,22 @@ "description": "Titel van de kaart voor draadloze netwerken", "value": "Draadloze netwerken" }, + "noCira.confirm": { + "description": "Bevestigingsvraag van het dialoogvenster voor de CIRA-waarschuwing", + "value": "Wilt u doorgaan?" + }, + "noCira.hint": { + "description": "Hint in het dialoogvenster over het opnieuw inschakelen van CIRA op de server", + "value": "Om de CIRA-configuratie te behouden, schakel CIRA in op de server door APP_DISABLE_CIRA in te stellen op false." + }, + "noCira.message": { + "description": "Bericht in het dialoogvenster dat waarschuwt dat de CIRA-configuratie uit het profiel wordt verwijderd", + "value": "Dit profiel heeft een bestaande CIRA-configuratie, maar CIRA is momenteel uitgeschakeld op de server. Het opslaan van dit profiel verwijdert de CIRA-configuratie." + }, + "noCira.title": { + "description": "Titel van het dialoogvenster voor de CIRA-verwijderingswaarschuwing", + "value": "CIRA-configuratie wordt verwijderd" + }, "pba.label": { "description": "Label voor het selectievakje om veilige opstart te activeren of deactiveren", "value": "Secure Boot afdwingen" @@ -2293,10 +2309,6 @@ "description": "Label voor CIRA-configuratieveld", "value": "CIRA-configuratie" }, - "profileDetail.ciraDisabledOption": { - "description": "Inline note on the CIRA radio when CIRA is disabled on the server", - "value": "(CIRA is uitgeschakeld op deze server)" - }, "profileDetail.ciraDisabledWarning": { "description": "Warning shown when editing a profile that uses CIRA while CIRA is disabled on the server", "value": "CIRA is uitgeschakeld op deze server. Dit profiel gebruikt CIRA voor zijn beheerverbinding en werkt pas als CIRA opnieuw is ingeschakeld." diff --git a/src/assets/i18n/ru.json b/src/assets/i18n/ru.json index 5bd3c6a10..1e303836c 100644 --- a/src/assets/i18n/ru.json +++ b/src/assets/i18n/ru.json @@ -2168,6 +2168,22 @@ "description": "Название карты беспроводных сетей", "value": "Беспроводные сети" }, + "noCira.confirm": { + "description": "Вопрос подтверждения в диалоге предупреждения без CIRA", + "value": "Хотите продолжить?" + }, + "noCira.hint": { + "description": "Подсказка в диалоге о повторном включении CIRA на сервере", + "value": "Чтобы сохранить конфигурацию CIRA, включите CIRA на сервере, установив APP_DISABLE_CIRA в значение false." + }, + "noCira.message": { + "description": "Сообщение диалога с предупреждением об удалении конфигурации CIRA из профиля", + "value": "Этот профиль имеет существующую конфигурацию CIRA, но CIRA в настоящее время отключён на сервере. Сохранение этого профиля удалит конфигурацию CIRA." + }, + "noCira.title": { + "description": "Заголовок диалога для предупреждения об удалении CIRA", + "value": "Конфигурация CIRA будет удалена" + }, "pba.label": { "description": "Метка для флажка, позволяющего включить или отключить применение безопасной загрузки", "value": "Обеспечение безопасной загрузки" @@ -2285,10 +2301,6 @@ "description": "Метка для поля конфигурации CIRA", "value": "Конфигурация CIRA" }, - "profileDetail.ciraDisabledOption": { - "description": "Inline note on the CIRA radio when CIRA is disabled on the server", - "value": "(CIRA отключён на этом сервере)" - }, "profileDetail.ciraDisabledWarning": { "description": "Warning shown when editing a profile that uses CIRA while CIRA is disabled on the server", "value": "CIRA отключён на этом сервере. Этот профиль использует CIRA для подключения управления и не будет работать, пока CIRA не будет снова включён." diff --git a/src/assets/i18n/sv.json b/src/assets/i18n/sv.json index 3c4ae1052..7b5999397 100644 --- a/src/assets/i18n/sv.json +++ b/src/assets/i18n/sv.json @@ -2040,6 +2040,22 @@ "description": "Titel på kortet för trådlösa nätverk", "value": "Trådlösa nätverk" }, + "noCira.confirm": { + "description": "Bekräftelsefråga i dialogen för CIRA-varningen", + "value": "Vill du fortsätta?" + }, + "noCira.hint": { + "description": "Tips i dialogen om hur man återaktiverar CIRA på servern", + "value": "För att behålla CIRA-konfigurationen, aktivera CIRA på servern genom att ange APP_DISABLE_CIRA till false." + }, + "noCira.message": { + "description": "Dialogmeddelande som varnar för att CIRA-konfigurationen tas bort från profilen", + "value": "Den här profilen har en befintlig CIRA-konfiguration, men CIRA är för närvarande inaktiverat på servern. Om du sparar den här profilen tas CIRA-konfigurationen bort." + }, + "noCira.title": { + "description": "Dialogtitel för CIRA-borttagningsvarningen", + "value": "CIRA-konfigurationen kommer att tas bort" + }, "pba.label": { "description": "Etikett för kryssrutan för att aktivera eller inaktivera säker start", "value": "Genomdriva säker start" @@ -2157,10 +2173,6 @@ "description": "Etikett för CIRA-konfigurationsfält", "value": "CIRA-konfiguration" }, - "profileDetail.ciraDisabledOption": { - "description": "Inline note on the CIRA radio when CIRA is disabled on the server", - "value": "(CIRA är inaktiverat på den här servern)" - }, "profileDetail.ciraDisabledWarning": { "description": "Warning shown when editing a profile that uses CIRA while CIRA is disabled on the server", "value": "CIRA är inaktiverat på den här servern. Den här profilen använder CIRA för sin hanteringsanslutning och fungerar inte förrän CIRA återaktiveras."