Skip to content

Commit

Permalink
feat(backend): use hashed id for user id
Browse files Browse the repository at this point in the history
  • Loading branch information
IgnisDa committed Jul 18, 2023
1 parent 1886e8c commit f941c4c
Show file tree
Hide file tree
Showing 4 changed files with 34 additions and 9 deletions.
4 changes: 4 additions & 0 deletions apps/backend/src/config.rs
Original file line number Diff line number Diff line change
Expand Up @@ -324,6 +324,9 @@ pub struct IntegrationConfig {
/// every `n` hours.
#[setting(default = 2)]
pub pull_every: i32,
/// The salt used to hash user IDs.
#[setting(default = format!("{}", PROJECT_NAME))]
pub hasher_salt: String,
}

impl IsFeatureEnabled for FileStorageConfig {
Expand Down Expand Up @@ -453,6 +456,7 @@ impl AppConfig {
cl.file_storage.s3_access_key_id = gt();
cl.file_storage.s3_secret_access_key = gt();
cl.file_storage.s3_url = gt();
cl.integration.hasher_salt = gt();
cl.movies.tmdb.access_token = gt();
cl.podcasts.listennotes.api_token = gt();
cl.shows.tmdb.access_token = gt();
Expand Down
6 changes: 3 additions & 3 deletions apps/backend/src/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -214,7 +214,7 @@ async fn main() -> Result<()> {
)
.allow_credentials(true);

let webhook_routes = Router::new().route("/jellyfin/:integration_slug", post(jellyfin_webhook));
let webhook_routes = Router::new().route("/jellyfin/:user_hash_id", post(jellyfin_webhook));

let app_routes = Router::new()
.nest("/webhooks", webhook_routes)
Expand Down Expand Up @@ -486,14 +486,14 @@ async fn export(
}

async fn jellyfin_webhook(
Path(integration_slug): Path<String>,
Path(user_hash_id): Path<String>,
Extension(media_service): Extension<Arc<MiscellaneousService>>,
// DEV: jellyfin does not send the `Content-Type: application/json` header,
// so we consume the body as a string
payload: String,
) -> std::result::Result<StatusCode, StatusCode> {
media_service
.process_jellyfin_event(payload, integration_slug)
.process_jellyfin_event(user_hash_id, payload)
.await
.map_err(|e| {
tracing::error!("{:?}", e);
Expand Down
31 changes: 25 additions & 6 deletions apps/backend/src/miscellaneous/resolver.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,14 +3,15 @@ use std::{
sync::Arc,
};

use anyhow::Result as AnyhowResult;
use anyhow::{anyhow, Result as AnyhowResult};
use apalis::{prelude::Storage as ApalisStorage, sqlite::SqliteStorage};
use argon2::{Argon2, PasswordHash, PasswordVerifier};
use async_graphql::{Context, Enum, Error, InputObject, Object, Result, SimpleObject, Union};
use chrono::{NaiveDate, Utc};
use cookie::{time::Duration as CookieDuration, time::OffsetDateTime, Cookie};
use enum_meta::Meta;
use futures::TryStreamExt;
use harsh::Harsh;
use http::header::SET_COOKIE;
use itertools::Itertools;
use markdown::{
Expand Down Expand Up @@ -450,10 +451,14 @@ fn create_cookie(
Ok(())
}

fn get_hasher() -> Argon2<'static> {
fn get_password_hasher() -> Argon2<'static> {
Argon2::default()
}

fn get_id_hasher(salt: &str) -> Harsh {
Harsh::builder().length(10).salt(salt).build().unwrap()
}

#[derive(Default)]
pub struct MiscellaneousQuery;

Expand Down Expand Up @@ -2842,7 +2847,7 @@ impl MiscellaneousService {
};
let user = user.unwrap();
let parsed_hash = PasswordHash::new(&user.password).unwrap();
if get_hasher()
if get_password_hasher()
.verify_password(password.as_bytes(), &parsed_hash)
.is_err()
{
Expand Down Expand Up @@ -3197,7 +3202,9 @@ impl MiscellaneousService {
timestamp: Utc::now(),
settings: match input.lot {
UserSinkIntegrationLot::Jellyfin => {
UserSinkIntegrationSetting::Jellyfin { slug: nanoid!(10) }
let slug = get_id_hasher(&self.config.integration.hasher_salt)
.encode(&[user_id.try_into().unwrap()]);
UserSinkIntegrationSetting::Jellyfin { slug }
}
},
};
Expand Down Expand Up @@ -3510,9 +3517,16 @@ impl MiscellaneousService {

pub async fn process_jellyfin_event(
&self,
user_hash_id: String,
payload: String,
integration_slug: String,
) -> AnyhowResult<()> {
let user_id = get_id_hasher(&self.config.integration.hasher_salt).decode(user_hash_id)?;
let user_id: i32 = user_id
.get(0)
.ok_or(anyhow!("Incorrect hash id provided"))?
.to_owned()
.try_into()?;
dbg!(user_id);
#[derive(Serialize, Deserialize, Debug, Clone)]
#[serde(rename_all = "PascalCase")]
struct JellyfinWebhookItemUserDataPayload {
Expand All @@ -3533,8 +3547,13 @@ impl MiscellaneousService {
item: JellyfinWebhookItemPayload,
}

// std::fs::write(
// "tmp/output.json",
// serde_json::to_string_pretty(&payload).unwrap(),
// )
// .unwrap();

let payload = serde_json::from_str::<JellyfinWebhookPayload>(&payload)?;
dbg!(&payload, &integration_slug);
Ok(())
}
}
Expand Down
2 changes: 2 additions & 0 deletions docs/includes/backend-config-schema.ts
Original file line number Diff line number Diff line change
Expand Up @@ -92,6 +92,8 @@ export interface FileStorageConfig {
}

export interface IntegrationConfig {
/** The salt used to hash user IDs. */
hasher_salt: string;
/**
* Sync data from [yank](/docs/guides/integrations.md) based integrations
* every `n` hours.
Expand Down

0 comments on commit f941c4c

Please sign in to comment.