diff --git a/src-tauri/src/enterprise/periodic/config.rs b/src-tauri/src/enterprise/periodic/config.rs index 5cb32286..a4436921 100644 --- a/src-tauri/src/enterprise/periodic/config.rs +++ b/src-tauri/src/enterprise/periodic/config.rs @@ -206,6 +206,14 @@ pub async fn poll_instance( return Ok(()); } + if check_uuid_mismatch(&response, instance, handle)? { + warn!("Instance {}({}) has UUID mismatch, it's config won't be automatically updated. Remove \ + the instance and add it again, or contact your administrator.", + instance.name, instance.id + ); + return Ok(()); + } + debug!( "Config for instance {}({}) changed", instance.name, instance.id @@ -269,11 +277,102 @@ fn build_request(instance: &Instance) -> Result }) } +#[derive(PartialEq, Eq, Hash)] +enum NotificationType { + VersionMismatch, + UuidMismatch, +} + /// Tracks instance IDs that for which we already sent notification about version mismatches /// to prevent duplicate notifications in the app's lifetime. -static NOTIFIED_INSTANCES: LazyLock>> = +static NOTIFIED_INSTANCES: LazyLock>> = LazyLock::new(|| Mutex::new(HashSet::new())); +#[derive(Clone, Serialize)] +struct UuidMismatchPayload { + instance_name: String, +} + +fn check_uuid_mismatch( + response: &InstanceInfoResponse, + instance: &Instance, + handle: &AppHandle, +) -> Result { + debug!( + "Checking UUID mismatch for instance {}({})", + instance.name, instance.id + ); + + let mut notified_instances = NOTIFIED_INSTANCES.lock().unwrap(); + + debug!( + "Instance {}({}) local UUID: {}, checking against response...", + instance.name, instance.id, instance.uuid + ); + + if let Some(device_config) = &response.device_config { + debug!( + "Found device_config for instance {}({})", + instance.name, instance.id + ); + + if let Some(info) = &device_config.instance { + debug!( + "Found instance info in device_config for instance {}({}), core UUID: {}", + instance.name, instance.id, info.id + ); + + if info.id != instance.uuid { + error!( + "Instance {}({}) has mismatching UUIDs: local {}, remote {}", + instance.name, instance.id, instance.uuid, info.id + ); + + if !notified_instances.contains(&(instance.id, NotificationType::UuidMismatch)) { + if let Err(err) = handle.emit( + EventKey::UuidMismatch.into(), + UuidMismatchPayload { + instance_name: instance.name.clone(), + }, + ) { + error!("Failed to emit UUID mismatch event to the frontend: {err}"); + } else { + debug!( + "Successfully emitted UUID mismatch event for instance {}({})", + instance.name, instance.id + ); + notified_instances.insert((instance.id, NotificationType::UuidMismatch)); + } + } else { + debug!( + "Instance {}({}) already notified about UUID mismatch, skipping", + instance.name, instance.id + ); + } + + return Ok(true); + } else { + debug!( + "UUIDs match for instance {}({}): {}", + instance.name, instance.id, instance.uuid + ); + } + } else { + debug!( + "No instance info found in device_config for instance {}({})", + instance.name, instance.id + ); + } + } else { + debug!( + "No device_config found in response for instance {}({})", + instance.name, instance.id + ); + } + + Ok(false) +} + const CORE_VERSION_HEADER: &str = "defguard-core-version"; const PROXY_VERSION_HEADER: &str = "defguard-component-version"; @@ -295,7 +394,7 @@ fn check_min_version( handle: &AppHandle, ) -> Result<(), Error> { let mut notified_instances = NOTIFIED_INSTANCES.lock().unwrap(); - if notified_instances.contains(&instance.id) { + if notified_instances.contains(&(instance.id, NotificationType::VersionMismatch)) { debug!( "Instance {}({}) already notified about version mismatch, skipping", instance.name, instance.id @@ -391,7 +490,7 @@ fn check_min_version( if let Err(err) = handle.emit(EventKey::VersionMismatch.into(), payload) { error!("Failed to emit version mismatch event to the frontend: {err}"); } else { - notified_instances.insert(instance.id); + notified_instances.insert((instance.id, NotificationType::VersionMismatch)); } } diff --git a/src-tauri/src/events.rs b/src-tauri/src/events.rs index e628aade..68be593b 100644 --- a/src-tauri/src/events.rs +++ b/src-tauri/src/events.rs @@ -18,6 +18,7 @@ pub enum EventKey { AddInstance, MfaTrigger, VersionMismatch, + UuidMismatch, } impl From for &'static str { @@ -34,6 +35,7 @@ impl From for &'static str { EventKey::AddInstance => "add-instance", EventKey::MfaTrigger => "mfa-trigger", EventKey::VersionMismatch => "version-mismatch", + EventKey::UuidMismatch => "uuid-mismatch", } } } diff --git a/src/i18n/en/index.ts b/src/i18n/en/index.ts index 31a0c1bf..2dca9b4f 100644 --- a/src/i18n/en/index.ts +++ b/src/i18n/en/index.ts @@ -64,6 +64,9 @@ const en = { 'Your Defguard instance "{instance_name: string}" version is not supported by your Defguard Client version. \ Defguard Core version: {core_version: string} (required: {core_required_version: string}), Defguard Proxy version: {proxy_version: string} (required: {proxy_required_version: string}). \ Please contact your administrator.', + uuidMismatch: + 'The identifier (UUID) of the remote Defguard instance "{instance_name: string}" does not match the one stored locally. \ + Because of this, some features may not work correctly. To resolve this issue, remove the instance and add it again, or contact your administrator.', }, }, components: { diff --git a/src/i18n/i18n-types.ts b/src/i18n/i18n-types.ts index 1ee57158..088f2ae0 100644 --- a/src/i18n/i18n-types.ts +++ b/src/i18n/i18n-types.ts @@ -193,6 +193,11 @@ type RootTranslation = { * @param {string} proxy_version */ versionMismatch: RequiredParams<'core_required_version' | 'core_version' | 'instance_name' | 'proxy_required_version' | 'proxy_version'> + /** + * T​h​e​ ​i​d​e​n​t​i​f​i​e​r​ ​(​U​U​I​D​)​ ​o​f​ ​t​h​e​ ​r​e​m​o​t​e​ ​D​e​f​g​u​a​r​d​ ​i​n​s​t​a​n​c​e​ ​"​{​i​n​s​t​a​n​c​e​_​n​a​m​e​}​"​ ​d​o​e​s​ ​n​o​t​ ​m​a​t​c​h​ ​t​h​e​ ​o​n​e​ ​s​t​o​r​e​d​ ​l​o​c​a​l​l​y​.​ ​ ​ ​ ​ ​ ​ ​ ​ ​B​e​c​a​u​s​e​ ​o​f​ ​t​h​i​s​,​ ​s​o​m​e​ ​f​e​a​t​u​r​e​s​ ​m​a​y​ ​n​o​t​ ​w​o​r​k​ ​c​o​r​r​e​c​t​l​y​.​ ​T​o​ ​r​e​s​o​l​v​e​ ​t​h​i​s​ ​i​s​s​u​e​,​ ​r​e​m​o​v​e​ ​t​h​e​ ​i​n​s​t​a​n​c​e​ ​a​n​d​ ​a​d​d​ ​i​t​ ​a​g​a​i​n​,​ ​o​r​ ​c​o​n​t​a​c​t​ ​y​o​u​r​ ​a​d​m​i​n​i​s​t​r​a​t​o​r​. + * @param {string} instance_name + */ + uuidMismatch: RequiredParams<'instance_name'> } } components: { @@ -1884,6 +1889,10 @@ export type TranslationFunctions = { * Your Defguard instance "{instance_name}" version is not supported by your Defguard Client version. Defguard Core version: {core_version} (required: {core_required_version}), Defguard Proxy version: {proxy_version} (required: {proxy_required_version}). Please contact your administrator. */ versionMismatch: (arg: { core_required_version: string, core_version: string, instance_name: string, proxy_required_version: string, proxy_version: string }) => LocalizedString + /** + * The identifier (UUID) of the remote Defguard instance "{instance_name}" does not match the one stored locally. Because of this, some features may not work correctly. To resolve this issue, remove the instance and add it again, or contact your administrator. + */ + uuidMismatch: (arg: { instance_name: string }) => LocalizedString } } components: { diff --git a/src/pages/client/ClientPage.tsx b/src/pages/client/ClientPage.tsx index c6a9c76f..2bbdc31c 100644 --- a/src/pages/client/ClientPage.tsx +++ b/src/pages/client/ClientPage.tsx @@ -110,6 +110,20 @@ export const ClientPage = () => { ); }); + const uuidMismatch = listen<{ + instance_name: string; + local_uuid: string; + core_uuid: string; + }>(TauriEventKey.UUID_MISMATCH, (data) => { + const payload = data.payload; + toaster.error( + LL.common.messages.uuidMismatch({ + instance_name: payload.instance_name, + }), + { lifetime: -1 }, + ); + }); + const locationUpdate = listen(TauriEventKey.LOCATION_UPDATE, () => { const invalidate = [clientQueryKeys.getLocations, clientQueryKeys.getTunnels]; invalidate.forEach((key) => { @@ -180,6 +194,7 @@ export const ClientPage = () => { appConfigChanged.then((cleanup) => cleanup()); mfaTrigger.then((cleanup) => cleanup()); verionMismatch.then((cleanup) => cleanup()); + uuidMismatch.then((cleanup) => cleanup()); }; // eslint-disable-next-line react-hooks/exhaustive-deps }, []); diff --git a/src/pages/client/types.ts b/src/pages/client/types.ts index 0d866443..9600fda7 100644 --- a/src/pages/client/types.ts +++ b/src/pages/client/types.ts @@ -114,4 +114,5 @@ export enum TauriEventKey { APPLICATION_CONFIG_CHANGED = 'application-config-changed', MFA_TRIGGER = 'mfa-trigger', VERSION_MISMATCH = 'version-mismatch', + UUID_MISMATCH = 'uuid-mismatch', }