Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
105 changes: 102 additions & 3 deletions src-tauri/src/enterprise/periodic/config.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -269,11 +277,102 @@ fn build_request(instance: &Instance<Id>) -> Result<InstanceInfoRequest, Error>
})
}

#[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<Mutex<HashSet<Id>>> =
static NOTIFIED_INSTANCES: LazyLock<Mutex<HashSet<(Id, NotificationType)>>> =
LazyLock::new(|| Mutex::new(HashSet::new()));

#[derive(Clone, Serialize)]
struct UuidMismatchPayload {
instance_name: String,
}

fn check_uuid_mismatch(
response: &InstanceInfoResponse,
instance: &Instance<Id>,
handle: &AppHandle,
) -> Result<bool, Error> {
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";

Expand All @@ -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
Expand Down Expand Up @@ -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));
}
}

Expand Down
2 changes: 2 additions & 0 deletions src-tauri/src/events.rs
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ pub enum EventKey {
AddInstance,
MfaTrigger,
VersionMismatch,
UuidMismatch,
}

impl From<EventKey> for &'static str {
Expand All @@ -34,6 +35,7 @@ impl From<EventKey> for &'static str {
EventKey::AddInstance => "add-instance",
EventKey::MfaTrigger => "mfa-trigger",
EventKey::VersionMismatch => "version-mismatch",
EventKey::UuidMismatch => "uuid-mismatch",
}
}
}
Expand Down
3 changes: 3 additions & 0 deletions src/i18n/en/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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: {
Expand Down
9 changes: 9 additions & 0 deletions src/i18n/i18n-types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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: {
Expand Down Expand Up @@ -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: {
Expand Down
15 changes: 15 additions & 0 deletions src/pages/client/ClientPage.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -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) => {
Expand Down Expand Up @@ -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
}, []);
Expand Down
1 change: 1 addition & 0 deletions src/pages/client/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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',
}