From 16a256bda30719ee2bff3bf645d125ae7927a00f Mon Sep 17 00:00:00 2001
From: Natalie Gaston
Date: Thu, 16 Jul 2026 11:56:32 -0700
Subject: [PATCH 1/2] feat: hide CIRA in profile when disabled on the server
---
.../profile-detail.component.html | 48 +++++++------------
.../profile-detail.component.spec.ts | 17 +++++--
2 files changed, 31 insertions(+), 34 deletions(-)
diff --git a/src/app/profiles/profile-detail/profile-detail.component.html b/src/app/profiles/profile-detail/profile-detail.component.html
index 0ca566f15..bec4e90d8 100644
--- a/src/app/profiles/profile-detail/profile-detail.component.html
+++ b/src/app/profiles/profile-detail/profile-detail.component.html
@@ -320,24 +320,23 @@
{{ 'profileDetail.connectionModeRequired.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..523346f00 100644
--- a/src/app/profiles/profile-detail/profile-detail.component.spec.ts
+++ b/src/app/profiles/profile-detail/profile-detail.component.spec.ts
@@ -27,6 +27,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 +149,7 @@ describe('ProfileDetailComponent', () => {
})
afterEach(() => {
+ environment.cloud = defaultCloudMode
TestBed.resetTestingModule()
})
@@ -174,6 +176,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)
@@ -835,9 +838,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 +853,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 +877,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 +888,7 @@ describe('ProfileDetailComponent', () => {
expect(ciraGetDataSpy).toHaveBeenCalled()
expect(enterpriseComponent.ciraEnabled()).toBeTrue()
+ expect(fixture.nativeElement.querySelector('[data-cy="radio-cira"]')).not.toBeNull()
})
})
From 190d5faa4f79fcce5f4653e16704b037a1c025cd Mon Sep 17 00:00:00 2001
From: Natalie Gaston
Date: Thu, 16 Jul 2026 15:20:03 -0700
Subject: [PATCH 2/2] feat: remove CIRA disabled warning
---
.../profile-detail.component.html | 1 -
.../profile-detail.component.spec.ts | 20 +++++++++++++++
.../profile-detail.component.ts | 25 ++++++++++++++++++-
src/app/profiles/profiles.component.html | 12 +--------
src/app/profiles/profiles.component.ts | 2 --
src/assets/i18n/ar.json | 8 ------
src/assets/i18n/de.json | 8 ------
src/assets/i18n/en.json | 8 ------
src/assets/i18n/es.json | 8 ------
src/assets/i18n/fi.json | 8 ------
src/assets/i18n/fr.json | 8 ------
src/assets/i18n/he.json | 8 ------
src/assets/i18n/it.json | 8 ------
src/assets/i18n/ja.json | 8 ------
src/assets/i18n/nl.json | 8 ------
src/assets/i18n/ru.json | 8 ------
src/assets/i18n/sv.json | 8 ------
17 files changed, 45 insertions(+), 111 deletions(-)
diff --git a/src/app/profiles/profile-detail/profile-detail.component.html b/src/app/profiles/profile-detail/profile-detail.component.html
index bec4e90d8..09d9bb096 100644
--- a/src/app/profiles/profile-detail/profile-detail.component.html
+++ b/src/app/profiles/profile-detail/profile-detail.component.html
@@ -330,7 +330,6 @@
[value]="connectionMode.cira"
[disabled]="ciraConfigurations().length === 0">
{{ 'profileDetail.ciraCloud.value' | translate }}
- {{ 'profileDetail.ciraDisabledOption.value' | translate }}
@if (ciraConfigurations().length === 0) {
{{ 'profileDetail.noCiraConfigs.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 523346f00..85c041f48 100644
--- a/src/app/profiles/profile-detail/profile-detail.component.spec.ts
+++ b/src/app/profiles/profile-detail/profile-detail.component.spec.ts
@@ -187,6 +187,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 as any).ciraAvailabilityResolved.set(true)
+
+ const profile: Profile = { ciraConfigName: 'config1' } as any
+ component.setConnectionMode(profile)
+
+ expect(component.profileForm.controls.connectionMode.value).toBe('DIRECT')
+ expect(component.profileForm.controls.ciraConfigName.value).toBeNull()
+ })
+ it('should keep CIRA connectionMode before enterprise feature availability resolves', () => {
+ component.ciraEnabled.set(false)
+ ;(component as any).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)
diff --git a/src/app/profiles/profile-detail/profile-detail.component.ts b/src/app/profiles/profile-detail/profile-detail.component.ts
index c61998aaf..18d1610bd 100644
--- a/src/app/profiles/profile-detail/profile-detail.component.ts
+++ b/src/app/profiles/profile-detail/profile-detail.component.ts
@@ -147,6 +147,9 @@ export class ProfileDetailComponent implements OnInit {
// (fetched on init). Start from cloudMode so enterprise hides CIRA until the
// API responds, avoiding a flash when the server reports CIRA disabled.
public readonly ciraEnabled = signal(this.cloudMode)
+ // Enterprise starts with ciraEnabled=false before server features return; track
+ // when availability is actually known to avoid coercing a saved CIRA profile too early.
+ private readonly ciraAvailabilityResolved = signal(this.cloudMode)
public readonly isLoading = signal(false)
public readonly errorMessages = signal([])
@@ -217,17 +220,35 @@ export class ProfileDetailComponent implements OnInit {
this.serverFeaturesService.getFeatures().subscribe({
next: (features) => {
this.ciraEnabled.set(features.ciraEnabled)
+ this.ciraAvailabilityResolved.set(true)
+ if (!features.ciraEnabled) {
+ this.coerceConnectionModeIfCiraUnavailable()
+ }
if (features.ciraEnabled) this.getCiraConfigs()
},
// 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
+ }
+
+ // Keep existing TLS profiles on TLS; otherwise fall back to DIRECT.
+ const fallbackMode =
+ this.profileForm.controls.tlsMode.value != null && this.profileForm.controls.tlsMode.value > 0
+ ? this.connectionMode.tls
+ : this.connectionMode.direct
+ this.profileForm.controls.connectionMode.setValue(fallbackMode)
+ }
+
private setupFormSubscriptions(): void {
this.profileForm.controls.activation.valueChanges.subscribe((value) => {
if (value) this.activationChange(value)
@@ -262,9 +283,11 @@ 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 {
this.profileForm.controls.connectionMode.setValue(this.connectionMode.direct)
diff --git a/src/app/profiles/profiles.component.html b/src/app/profiles/profiles.component.html
index fa24491b2..13576233d 100644
--- a/src/app/profiles/profiles.component.html
+++ b/src/app/profiles/profiles.component.html
@@ -24,17 +24,7 @@
{{ 'profiles.noData.value' | translate }}
{{ 'profiles.table.name.value' | translate }}
-
- {{ element.profileName }}
- @if (!ciraEnabled() && element.ciraConfigName) {
- warning
- }
-
+ {{ element.profileName }}
diff --git a/src/app/profiles/profiles.component.ts b/src/app/profiles/profiles.component.ts
index 24cc02ce0..d3247e212 100644
--- a/src/app/profiles/profiles.component.ts
+++ b/src/app/profiles/profiles.component.ts
@@ -33,7 +33,6 @@ import { MatProgressBar } from '@angular/material/progress-bar'
import { MatIcon } from '@angular/material/icon'
import { MatButton, MatIconButton } from '@angular/material/button'
import { MatToolbar } from '@angular/material/toolbar'
-import { MatTooltip } from '@angular/material/tooltip'
import { environment } from '../../environments/environment'
import { KeyDisplayDialogComponent } from './key-display-dialog/key-display-dialog.component'
import { TranslatePipe, TranslateService } from '@ngx-translate/core'
@@ -63,7 +62,6 @@ import { ServerFeaturesService } from '../server-features.service'
MatRowDef,
MatRow,
MatPaginator,
- MatTooltip,
ToolkitPipe,
TranslatePipe
]
diff --git a/src/assets/i18n/ar.json b/src/assets/i18n/ar.json
index 5948cfa5e..508e4a963 100644
--- a/src/assets/i18n/ar.json
+++ b/src/assets/i18n/ar.json
@@ -2145,14 +2145,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."
- },
"profileDetail.connectionConfiguration": {
"description": "تسمية قسم تكوين الاتصال",
"value": "تكوين الاتصال المُوفر"
diff --git a/src/assets/i18n/de.json b/src/assets/i18n/de.json
index 61fc2afed..a1c091c1f 100644
--- a/src/assets/i18n/de.json
+++ b/src/assets/i18n/de.json
@@ -2145,14 +2145,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."
- },
"profileDetail.connectionConfiguration": {
"description": "Bezeichnung für den Abschnitt zur Verbindungskonfiguration",
"value": "Konfiguration der bereitgestellten Verbindung"
diff --git a/src/assets/i18n/en.json b/src/assets/i18n/en.json
index 5ff3098ee..4d2375c65 100644
--- a/src/assets/i18n/en.json
+++ b/src/assets/i18n/en.json
@@ -2615,14 +2615,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."
- },
"profileDetail.connectionConfiguration": {
"description": "Label for connection configuration section",
"value": "Provisioned Connection Configuration"
diff --git a/src/assets/i18n/es.json b/src/assets/i18n/es.json
index e753b9518..7bfb0f23b 100644
--- a/src/assets/i18n/es.json
+++ b/src/assets/i18n/es.json
@@ -2145,14 +2145,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."
- },
"profileDetail.connectionConfiguration": {
"description": "Etiqueta para la sección de configuración de la conexión.",
"value": "Configuración de la conexión aprovisionada"
diff --git a/src/assets/i18n/fi.json b/src/assets/i18n/fi.json
index 59f8fbe9b..2261c50b5 100644
--- a/src/assets/i18n/fi.json
+++ b/src/assets/i18n/fi.json
@@ -2145,14 +2145,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."
- },
"profileDetail.connectionConfiguration": {
"description": "Yhteysasetusten osion nimike",
"value": "Provisioitu yhteyden konfigurointi"
diff --git a/src/assets/i18n/fr.json b/src/assets/i18n/fr.json
index bd89f9294..cd8b15784 100644
--- a/src/assets/i18n/fr.json
+++ b/src/assets/i18n/fr.json
@@ -2145,14 +2145,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é."
- },
"profileDetail.connectionConfiguration": {
"description": "Étiquette pour la section de configuration de la connexion",
"value": "Configuration de la connexion provisionnée"
diff --git a/src/assets/i18n/he.json b/src/assets/i18n/he.json
index 57671944c..d686c8dc5 100644
--- a/src/assets/i18n/he.json
+++ b/src/assets/i18n/he.json
@@ -2145,14 +2145,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 יופעל מחדש."
- },
"profileDetail.connectionConfiguration": {
"description": "תווית לקטע תצורת חיבור",
"value": "תצורת חיבור אספקת"
diff --git a/src/assets/i18n/it.json b/src/assets/i18n/it.json
index ac782e109..b47221614 100644
--- a/src/assets/i18n/it.json
+++ b/src/assets/i18n/it.json
@@ -2145,14 +2145,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."
- },
"profileDetail.connectionConfiguration": {
"description": "Etichetta per la sezione di configurazione della connessione",
"value": "Configurazione della connessione fornita"
diff --git a/src/assets/i18n/ja.json b/src/assets/i18n/ja.json
index 77211108c..f0ea8df60 100644
--- a/src/assets/i18n/ja.json
+++ b/src/assets/i18n/ja.json
@@ -2145,14 +2145,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 が再度有効になるまで機能しません。"
- },
"profileDetail.connectionConfiguration": {
"description": "接続設定セクションのラベル",
"value": "プロビジョニング済み接続設定"
diff --git a/src/assets/i18n/nl.json b/src/assets/i18n/nl.json
index 7158d8bc8..dce66b70e 100644
--- a/src/assets/i18n/nl.json
+++ b/src/assets/i18n/nl.json
@@ -2145,14 +2145,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."
- },
"profileDetail.connectionConfiguration": {
"description": "Label voor het gedeelte over verbindingsconfiguratie",
"value": "Configuratie van de geleverde verbinding"
diff --git a/src/assets/i18n/ru.json b/src/assets/i18n/ru.json
index fbd1baf70..1f91df96e 100644
--- a/src/assets/i18n/ru.json
+++ b/src/assets/i18n/ru.json
@@ -2145,14 +2145,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 не будет снова включён."
- },
"profileDetail.connectionConfiguration": {
"description": "Метка для раздела настройки подключения",
"value": "Настройка предоставленного соединения"
diff --git a/src/assets/i18n/sv.json b/src/assets/i18n/sv.json
index 62266b08e..faca3974f 100644
--- a/src/assets/i18n/sv.json
+++ b/src/assets/i18n/sv.json
@@ -2145,14 +2145,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."
- },
"profileDetail.connectionConfiguration": {
"description": "Etikett för avsnittet om anslutningskonfiguration",
"value": "Konfiguration av tillhandahållen anslutning"