mirror of
https://github.com/outbackdingo/firezone.git
synced 2026-01-27 18:18:55 +00:00
fix(telemetry): always use hex-encoded ID as user ID (#9781)
We are currently in the process of transitioning the Firezone Clients away from always hashing the ID before sending it to the portal. This will make lookups and correlation of data between our systems much easier. The way we are performing this migration is that new installations of Firezone will directly generate a 64 char hex-string as the Firezone ID. If the ID looks like a UUID (which is the old format), we still hash it and send it to the portal, otherwise we send it as-is. Presently, the telemetry integration with Sentry and PostHog do the opposite. They always sets the Firezone ID as-is and includes an `external_id` that is the hashed form if it detects that it is a UUID (or in the case of PostHog, create an alias). It is much better to flip this around and always set the hex-string as the user id. That way, we can simply always filter by the `user.id` attribute in Sentry and always refer to the ID that we are seeing in the portal.
This commit is contained in:
@@ -6,7 +6,13 @@ use sha2::Digest as _;
|
||||
|
||||
use crate::{Env, posthog::RUNTIME};
|
||||
|
||||
pub fn new_session(distinct_id: String, api_url: String) {
|
||||
pub fn new_session(maybe_legacy_id: String, api_url: String) {
|
||||
let distinct_id = if uuid::Uuid::from_str(&maybe_legacy_id).is_ok() {
|
||||
hex::encode(sha2::Sha256::digest(&maybe_legacy_id))
|
||||
} else {
|
||||
maybe_legacy_id
|
||||
};
|
||||
|
||||
RUNTIME.spawn(async move {
|
||||
if let Err(e) = capture(
|
||||
"new_session",
|
||||
@@ -23,11 +29,19 @@ pub fn new_session(distinct_id: String, api_url: String) {
|
||||
|
||||
/// Associate several properties with a particular "distinct_id" in PostHog.
|
||||
pub fn identify(
|
||||
distinct_id: String,
|
||||
maybe_legacy_id: String,
|
||||
api_url: String,
|
||||
release: String,
|
||||
account_slug: Option<String>,
|
||||
) {
|
||||
let is_legacy_id = uuid::Uuid::from_str(&maybe_legacy_id).is_ok();
|
||||
|
||||
let distinct_id = if is_legacy_id {
|
||||
hex::encode(sha2::Sha256::digest(&maybe_legacy_id))
|
||||
} else {
|
||||
maybe_legacy_id.clone()
|
||||
};
|
||||
|
||||
RUNTIME.spawn({
|
||||
let distinct_id = distinct_id.clone();
|
||||
let api_url = api_url.clone();
|
||||
@@ -53,14 +67,14 @@ pub fn identify(
|
||||
});
|
||||
|
||||
// Create an alias ID for the user so we can also find them under the "external ID" used in the portal.
|
||||
if uuid::Uuid::from_str(&distinct_id).is_ok() {
|
||||
if is_legacy_id {
|
||||
RUNTIME.spawn(async move {
|
||||
if let Err(e) = capture(
|
||||
"$create_alias",
|
||||
distinct_id.clone(),
|
||||
api_url,
|
||||
CreateAliasProperties {
|
||||
alias: hex::encode(sha2::Sha256::digest(&distinct_id)),
|
||||
alias: maybe_legacy_id,
|
||||
distinct_id,
|
||||
},
|
||||
)
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
use std::{
|
||||
str::FromStr as _,
|
||||
sync::{
|
||||
LazyLock,
|
||||
atomic::{AtomicBool, Ordering},
|
||||
@@ -8,6 +9,7 @@ use std::{
|
||||
|
||||
use anyhow::{Context as _, Result, bail};
|
||||
use serde::{Deserialize, Serialize};
|
||||
use sha2::Digest as _;
|
||||
|
||||
use crate::{
|
||||
Env,
|
||||
@@ -88,7 +90,13 @@ pub(crate) async fn reeval_timer() {
|
||||
}
|
||||
}
|
||||
|
||||
async fn decide(distinct_id: String, api_key: String) -> Result<FeatureFlagsResponse> {
|
||||
async fn decide(maybe_legacy_id: String, api_key: String) -> Result<FeatureFlagsResponse> {
|
||||
let distinct_id = if uuid::Uuid::from_str(&maybe_legacy_id).is_ok() {
|
||||
hex::encode(sha2::Sha256::digest(&maybe_legacy_id))
|
||||
} else {
|
||||
maybe_legacy_id
|
||||
};
|
||||
|
||||
let response = reqwest::ClientBuilder::new()
|
||||
.connection_verbose(true)
|
||||
.build()?
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
#![cfg_attr(test, allow(clippy::unwrap_used))]
|
||||
|
||||
use std::{borrow::Cow, fmt, str::FromStr, sync::Arc, time::Duration};
|
||||
use std::{borrow::Cow, collections::BTreeMap, fmt, str::FromStr, sync::Arc, time::Duration};
|
||||
|
||||
use anyhow::{Result, bail};
|
||||
use sentry::{
|
||||
@@ -173,10 +173,7 @@ impl Telemetry {
|
||||
scope.set_context("os", ctx);
|
||||
}
|
||||
|
||||
scope.set_user(Some(User {
|
||||
id: Some(firezone_id),
|
||||
..User::default()
|
||||
}));
|
||||
scope.set_user(Some(compute_user(firezone_id)));
|
||||
});
|
||||
self.inner.replace(inner);
|
||||
sentry::start_session();
|
||||
@@ -216,31 +213,31 @@ impl Telemetry {
|
||||
user.other.insert("account_slug".to_owned(), slug.into());
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
pub fn set_firezone_id(id: String) {
|
||||
update_user({
|
||||
let id = id.clone();
|
||||
move |user| {
|
||||
user.id = Some(id.clone());
|
||||
/// Computes the [`User`] scope based on the contents of `firezone_id`.
|
||||
///
|
||||
/// If `firezone_id` looks like a UUID, we hash and hex-encode it.
|
||||
/// This will align the ID with what we see in the portal.
|
||||
///
|
||||
/// If it is not a UUID, it is already from a newer installation of Firezone
|
||||
/// where the ID is sent as-is.
|
||||
///
|
||||
/// As a result, this will allow us to always filter the user by the hex-encoded ID.
|
||||
fn compute_user(firezone_id: String) -> User {
|
||||
if uuid::Uuid::from_str(&firezone_id).is_ok() {
|
||||
let encoded_id = hex::encode(sha2::Sha256::digest(firezone_id));
|
||||
|
||||
if uuid::Uuid::from_str(&id).is_ok() {
|
||||
user.other.insert(
|
||||
"external_id".to_owned(),
|
||||
serde_json::Value::String(hex::encode(sha2::Sha256::digest(&id))),
|
||||
);
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
let Some(client) = sentry::Hub::main().client() else {
|
||||
return;
|
||||
return User {
|
||||
id: Some(encoded_id.clone()),
|
||||
other: BTreeMap::from([("uuid".to_owned(), serde_json::Value::String(encoded_id))]),
|
||||
..User::default()
|
||||
};
|
||||
}
|
||||
|
||||
let Some(env) = client.options().environment.as_ref() else {
|
||||
return; // Nothing to do if we don't have an environment set.
|
||||
};
|
||||
|
||||
feature_flags::reevaluate(id, env);
|
||||
User {
|
||||
id: Some(firezone_id),
|
||||
..User::default()
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user