handshakes

This commit is contained in:
DecDuck
2024-10-08 18:08:52 +11:00
parent ae4c65b7ab
commit 4bb33c8223
9 changed files with 285 additions and 43 deletions

16
app.vue
View File

@ -3,6 +3,8 @@
<NuxtPage /> <NuxtPage />
</NuxtLayout> </NuxtLayout>
{{ state }} {{ state }}
<input type="text" v-model="debugLocation" />
<NuxtLink :to="debugLocation">Go</NuxtLink>
</template> </template>
<script setup lang="ts"> <script setup lang="ts">
@ -23,7 +25,17 @@ switch (state.status) {
break; break;
} }
listen("auth/connecting", (event) => { listen("auth/processing", () => {
router.push("/auth/connecting"); router.push("/auth/processing");
}); });
listen("auth/failed", () => {
router.push("/auth/failed");
});
listen("auth/finished", () => {
router.push("/");
});
const debugLocation = ref("");
</script> </script>

View File

@ -1,5 +1,6 @@
<template> <template>
<div class="inline-flex items-center gap-x-2"> <div class="inline-flex items-center gap-x-2">
<!--
<HeaderButton @click="() => window.minimize()"> <HeaderButton @click="() => window.minimize()">
<MinusIcon /> <MinusIcon />
</HeaderButton> </HeaderButton>
@ -17,6 +18,7 @@
/> />
</svg> </svg>
</HeaderButton> </HeaderButton>
-->
<HeaderButton @click="() => window.close()"> <HeaderButton @click="() => window.close()">
<XMarkIcon /> <XMarkIcon />
</HeaderButton> </HeaderButton>
@ -24,10 +26,7 @@
</template> </template>
<script setup lang="ts"> <script setup lang="ts">
import { import { MinusIcon, XMarkIcon } from "@heroicons/vue/16/solid";
MinusIcon,
XMarkIcon,
} from "@heroicons/vue/16/solid";
import { getCurrentWindow } from "@tauri-apps/api/window"; import { getCurrentWindow } from "@tauri-apps/api/window";
const window = getCurrentWindow(); const window = getCurrentWindow();

30
pages/auth/failed.vue Normal file
View File

@ -0,0 +1,30 @@
<template>
<div class="min-h-full w-full flex items-center justify-center">
<div class="flex flex-col items-center">
<XCircleIcon class="h-12 w-12 text-red-600" aria-hidden="true" />
<div class="mt-3 text-center sm:mt-5">
<h1 class="text-3xl font-semibold font-display leading-6 text-zinc-100">
Connection failed
</h1>
<div class="mt-4">
<p class="text-sm text-zinc-400 max-w-sm">
Drop encountered an error while connecting to your instance.
</p>
</div>
<div class="mt-10 flex items-center justify-center gap-x-6">
<NuxtLink href="/auth" class="text-sm font-semibold text-zinc-100"
><span aria-hidden="true">&larr;</span> Back to authentication
</NuxtLink>
</div>
</div>
</div>
</div>
</template>
<script setup lang="ts">
import { XCircleIcon } from "@heroicons/vue/16/solid";
definePageMeta({
layout: "mini",
});
</script>

161
src-tauri/Cargo.lock generated
View File

@ -931,13 +931,14 @@ version = "0.1.0"
dependencies = [ dependencies = [
"ciborium", "ciborium",
"directories", "directories",
"futures", "log",
"os_info", "os_info",
"rayon", "rayon",
"reqwest", "reqwest",
"rustbreak", "rustbreak",
"serde", "serde",
"serde_json", "serde_json",
"structured-logger",
"tauri", "tauri",
"tauri-build", "tauri-build",
"tauri-plugin-deep-link", "tauri-plugin-deep-link",
@ -1195,21 +1196,6 @@ dependencies = [
"new_debug_unreachable", "new_debug_unreachable",
] ]
[[package]]
name = "futures"
version = "0.3.31"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "65bc07b1a8bc7c85c5f2e110c476c7389b4554ba72af57d8445ea63a576b0876"
dependencies = [
"futures-channel",
"futures-core",
"futures-executor",
"futures-io",
"futures-sink",
"futures-task",
"futures-util",
]
[[package]] [[package]]
name = "futures-channel" name = "futures-channel"
version = "0.3.31" version = "0.3.31"
@ -1285,7 +1271,6 @@ version = "0.3.31"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9fa08315bb612088cc391249efdc3bc77536f16c91f6cf495e6fbe85b20a4a81" checksum = "9fa08315bb612088cc391249efdc3bc77536f16c91f6cf495e6fbe85b20a4a81"
dependencies = [ dependencies = [
"futures-channel",
"futures-core", "futures-core",
"futures-io", "futures-io",
"futures-macro", "futures-macro",
@ -2116,6 +2101,10 @@ name = "log"
version = "0.4.22" version = "0.4.22"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a7a70ba024b9dc04c27ea2f0c0548feb474ec5c54bba33a7f72f873a39d07b24" checksum = "a7a70ba024b9dc04c27ea2f0c0548feb474ec5c54bba33a7f72f873a39d07b24"
dependencies = [
"serde",
"value-bag",
]
[[package]] [[package]]
name = "mac" name = "mac"
@ -3108,6 +3097,7 @@ dependencies = [
"base64 0.22.1", "base64 0.22.1",
"bytes", "bytes",
"encoding_rs", "encoding_rs",
"futures-channel",
"futures-core", "futures-core",
"futures-util", "futures-util",
"h2", "h2",
@ -3401,6 +3391,15 @@ dependencies = [
"syn 2.0.79", "syn 2.0.79",
] ]
[[package]]
name = "serde_fmt"
version = "1.0.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e1d4ddca14104cd60529e8c7f7ba71a2c8acd8f7f5cfcdc2faf97eeb7c3010a4"
dependencies = [
"serde",
]
[[package]] [[package]]
name = "serde_json" name = "serde_json"
version = "1.0.128" version = "1.0.128"
@ -3689,12 +3688,103 @@ version = "0.11.1"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7da8b5736845d9f2fcb837ea5d9e2628564b3b043a70948a3f0b778838c5fb4f" checksum = "7da8b5736845d9f2fcb837ea5d9e2628564b3b043a70948a3f0b778838c5fb4f"
[[package]]
name = "structured-logger"
version = "1.0.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "16524b1ef57fd2e253216ab20ec44f0dc32b29155a4b3e6bef0a857d8c9f5f08"
dependencies = [
"log",
"parking_lot",
"serde",
"serde_json",
"tokio",
]
[[package]] [[package]]
name = "subtle" name = "subtle"
version = "2.6.1" version = "2.6.1"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "13c2bddecc57b384dee18652358fb23172facb8a2c51ccc10d74c157bdea3292" checksum = "13c2bddecc57b384dee18652358fb23172facb8a2c51ccc10d74c157bdea3292"
[[package]]
name = "sval"
version = "2.13.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "eaf38d1fa2ce984086ea42fb856a9f374d94680a4f796831a7fc868d7f2af1b9"
[[package]]
name = "sval_buffer"
version = "2.13.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "81682ff859964ca1d7cf3d3d0f9ec7204ea04c2c32acb8cc2cf68ecbd3127354"
dependencies = [
"sval",
"sval_ref",
]
[[package]]
name = "sval_dynamic"
version = "2.13.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "2a213b93bb4c6f4c9f9b17f2e740e077fd18746bbf7c80c72bbadcac68fa7ee4"
dependencies = [
"sval",
]
[[package]]
name = "sval_fmt"
version = "2.13.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "6902c6d3fb52c89206fe0dc93546c0123f7d48b5997fd14e61c9e64ff0b63275"
dependencies = [
"itoa 1.0.11",
"ryu",
"sval",
]
[[package]]
name = "sval_json"
version = "2.13.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "11a28041ea78cdc394b930ae6b897d36246dc240a29a6edf82d76562487fb0b4"
dependencies = [
"itoa 1.0.11",
"ryu",
"sval",
]
[[package]]
name = "sval_nested"
version = "2.13.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "850346e4b0742a7f2fd2697d703ff80084d0b658f0f2e336d71b8a06abf9b68e"
dependencies = [
"sval",
"sval_buffer",
"sval_ref",
]
[[package]]
name = "sval_ref"
version = "2.13.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "824afd97a8919f28a35b0fdea979845cc2ae461a8a3aaa129455cb89c88bb77a"
dependencies = [
"sval",
]
[[package]]
name = "sval_serde"
version = "2.13.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8ada7520dd719ed672c786c7db7de4f5230f4d504b0821bd8305cd30ca442315"
dependencies = [
"serde",
"sval",
"sval_nested",
]
[[package]] [[package]]
name = "swift-rs" name = "swift-rs"
version = "1.0.7" version = "1.0.7"
@ -4220,6 +4310,7 @@ dependencies = [
"bytes", "bytes",
"libc", "libc",
"mio", "mio",
"parking_lot",
"pin-project-lite", "pin-project-lite",
"socket2", "socket2",
"windows-sys 0.52.0", "windows-sys 0.52.0",
@ -4525,6 +4616,42 @@ dependencies = [
"serde", "serde",
] ]
[[package]]
name = "value-bag"
version = "1.9.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5a84c137d37ab0142f0f2ddfe332651fdbf252e7b7dbb4e67b6c1f1b2e925101"
dependencies = [
"value-bag-serde1",
"value-bag-sval2",
]
[[package]]
name = "value-bag-serde1"
version = "1.9.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ccacf50c5cb077a9abb723c5bcb5e0754c1a433f1e1de89edc328e2760b6328b"
dependencies = [
"erased-serde",
"serde",
"serde_fmt",
]
[[package]]
name = "value-bag-sval2"
version = "1.9.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1785bae486022dfb9703915d42287dcb284c1ee37bd1080eeba78cc04721285b"
dependencies = [
"sval",
"sval_buffer",
"sval_dynamic",
"sval_fmt",
"sval_json",
"sval_ref",
"sval_serde",
]
[[package]] [[package]]
name = "vcpkg" name = "vcpkg"
version = "0.2.15" version = "0.2.15"

View File

@ -33,7 +33,8 @@ webbrowser = "1.0.2"
url = "2.5.2" url = "2.5.2"
os_info = "3.8.2" os_info = "3.8.2"
tauri-plugin-deep-link = "2" tauri-plugin-deep-link = "2"
futures = "0.3.31" log = "0.4.22"
structured-logger = "1.0.3"
[dependencies.rustbreak] [dependencies.rustbreak]
version = "2" version = "2"
@ -41,5 +42,5 @@ features = ["bin_enc"] # You can also use "yaml_enc" or "bin_enc"
[dependencies.reqwest] [dependencies.reqwest]
version = "0.12" version = "0.12"
features = ["json"] features = ["json", "blocking"]

View File

@ -3,11 +3,12 @@ use std::{
sync::Mutex, sync::Mutex,
}; };
use log::info;
use serde::{Deserialize, Serialize}; use serde::{Deserialize, Serialize};
use tauri::{App, AppHandle, Emitter, Error, EventLoopMessage, Wry}; use tauri::{App, AppHandle, Emitter, Error, EventLoopMessage, Manager, Wry};
use url::Url; use url::Url;
use crate::{AppStatus, User, DB}; use crate::{data::DatabaseCerts, AppState, AppStatus, User, DB};
#[derive(Serialize)] #[derive(Serialize)]
struct InitiateRequestBody { struct InitiateRequestBody {
@ -15,11 +16,78 @@ struct InitiateRequestBody {
platform: String, platform: String,
} }
pub async fn recieve_handshake(app: AppHandle, path: String) { #[derive(Serialize)]
// Tell the app we're connecting struct HandshakeRequestBody {
app.emit("auth/connecting", ()).unwrap(); clientId: String,
token: String,
}
// TODO #[derive(Deserialize)]
struct HandshakeResponse {
private: String,
public: String,
certificate: String,
id: String,
}
macro_rules! unwrap_or_return {
( $e:expr, $app:expr ) => {
match $e {
Ok(x) => x,
Err(_) => {
$app.emit("auth/failed", ()).unwrap();
return;
}
}
};
}
pub fn recieve_handshake(app: AppHandle, path: String) {
// Tell the app we're processing
app.emit("auth/processing", ()).unwrap();
let path_chunks: Vec<&str> = path.split("/").collect();
if path_chunks.len() != 3 {
app.emit("auth/failed", ()).unwrap();
return;
}
let base_url = {
let handle = DB.borrow_data().unwrap();
Url::parse(handle.base_url.as_str()).unwrap()
};
let client_id = path_chunks.get(1).unwrap();
let token = path_chunks.get(2).unwrap();
let body = HandshakeRequestBody {
clientId: client_id.to_string(),
token: token.to_string(),
};
let endpoint = unwrap_or_return!(base_url.join("/api/v1/client/handshake"), app);
let client = reqwest::blocking::Client::new();
let response = unwrap_or_return!(client.post(endpoint).json(&body).send(), app);
info!("server responded with {}", response.status());
let response_struct = unwrap_or_return!(response.json::<HandshakeResponse>(), app);
{
let mut handle = DB.borrow_data_mut().unwrap();
handle.certs = Some(DatabaseCerts {
private: response_struct.private,
public: response_struct.public,
cert: response_struct.certificate,
});
drop(handle);
DB.save().unwrap();
}
{
let app_state = app.state::<Mutex<AppState>>();
let mut app_state_handle = app_state.lock().unwrap();
app_state_handle.status = AppStatus::SignedIn;
}
app.emit("auth/finished", ()).unwrap();
} }
#[tauri::command] #[tauri::command]
@ -48,6 +116,7 @@ pub async fn auth_initiate<'a>() -> Result<(), String> {
let redir_url = response.text().await.unwrap(); let redir_url = response.text().await.unwrap();
let complete_redir_url = base_url.join(&redir_url).unwrap(); let complete_redir_url = base_url.join(&redir_url).unwrap();
info!("opening web browser to continue authentication");
webbrowser::open(&complete_redir_url.to_string()).unwrap(); webbrowser::open(&complete_redir_url.to_string()).unwrap();
return Ok(()); return Ok(());
@ -59,12 +128,11 @@ pub fn setup() -> Result<(AppStatus, Option<User>), Error> {
// If we have certs, exit for now // If we have certs, exit for now
if data.certs.is_some() { if data.certs.is_some() {
// TODO: check if it's still valid, and fetch user information // TODO: check if it's still valid, and fetch user information
info!("have existing certs, assuming logged in...");
return Ok((AppStatus::SignedInNeedsReauth, None)); return Ok((AppStatus::SignedInNeedsReauth, None));
} }
drop(data); drop(data);
auth_initiate();
return Ok((AppStatus::SignedOut, None)); return Ok((AppStatus::SignedOut, None));
} }

View File

@ -4,15 +4,17 @@ mod remote;
mod unpacker; mod unpacker;
use std::{ use std::{
io,
sync::{LazyLock, Mutex}, sync::{LazyLock, Mutex},
task, thread, task, thread,
}; };
use auth::{auth_initiate, recieve_handshake}; use auth::{auth_initiate, recieve_handshake};
use data::DatabaseInterface; use data::DatabaseInterface;
use futures::executor; use log::info;
use remote::use_remote; use remote::use_remote;
use serde::Serialize; use serde::Serialize;
use structured_logger::{json::new_writer, Builder};
use tauri_plugin_deep_link::DeepLinkExt; use tauri_plugin_deep_link::DeepLinkExt;
#[derive(Clone, Copy, Serialize)] #[derive(Clone, Copy, Serialize)]
@ -40,6 +42,10 @@ fn fetch_state<'a>(state: tauri::State<'_, Mutex<AppState>>) -> Result<AppState,
} }
fn setup<'a>() -> AppState { fn setup<'a>() -> AppState {
Builder::with_level("info")
.with_target_writer("*", new_writer(io::stdout()))
.init();
let is_set_up = data::is_set_up(); let is_set_up = data::is_set_up();
if !is_set_up { if !is_set_up {
return AppState { return AppState {
@ -60,13 +66,13 @@ pub static DB: LazyLock<DatabaseInterface> = LazyLock::new(|| data::setup());
#[cfg_attr(mobile, tauri::mobile_entry_point)] #[cfg_attr(mobile, tauri::mobile_entry_point)]
pub fn run() { pub fn run() {
let state = setup(); let state = setup();
info!("Initialized drop client");
let mut builder = tauri::Builder::default(); let mut builder = tauri::Builder::default();
#[cfg(desktop)] #[cfg(desktop)]
{ {
builder = builder.plugin(tauri_plugin_single_instance::init(|_app, argv, _cwd| { builder = builder.plugin(tauri_plugin_single_instance::init(|_app, argv, _cwd| {
println!("a new app instance was opened with {argv:?} and the deep link event was already triggered");
// when defining deep link schemes at runtime, you must also check `argv` here // when defining deep link schemes at runtime, you must also check `argv` here
})); }));
} }
@ -85,20 +91,17 @@ pub fn run() {
{ {
use tauri_plugin_deep_link::DeepLinkExt; use tauri_plugin_deep_link::DeepLinkExt;
app.deep_link().register_all()?; app.deep_link().register_all()?;
info!("registered all pre-defined deep links");
} }
let handle = app.handle().clone(); let handle = app.handle().clone();
app.deep_link().on_open_url(move |event| { app.deep_link().on_open_url(move |event| {
info!("handling drop:// url");
let binding = event.urls(); let binding = event.urls();
let url = binding.get(0).unwrap(); let url = binding.get(0).unwrap();
match url.host_str().unwrap() { match url.host_str().unwrap() {
"handshake" => { "handshake" => recieve_handshake(handle.clone(), url.path().to_string()),
executor::block_on(recieve_handshake(
handle.clone(),
url.path().to_string(),
));
}
_ => (), _ => (),
} }
}); });

View File

@ -3,6 +3,7 @@ use std::{
sync::Mutex, sync::Mutex,
}; };
use log::{info, warn};
use serde::Deserialize; use serde::Deserialize;
use url::Url; use url::Url;
@ -32,7 +33,7 @@ pub async fn use_remote<'a>(
url: String, url: String,
state: tauri::State<'_, Mutex<AppState>>, state: tauri::State<'_, Mutex<AppState>>,
) -> Result<(), String> { ) -> Result<(), String> {
println!("connecting to url {}", url); info!("connecting to url {}", url);
let base_url = unwrap_or_return!(Url::parse(&url)); let base_url = unwrap_or_return!(Url::parse(&url));
// Test Drop url // Test Drop url
@ -42,6 +43,7 @@ pub async fn use_remote<'a>(
let result = response.json::<DropHealthcheck>().await.unwrap(); let result = response.json::<DropHealthcheck>().await.unwrap();
if result.appName != "Drop" { if result.appName != "Drop" {
warn!("user entered drop endpoint that connected, but wasn't identified as Drop");
return Err("Not a valid Drop endpoint".to_string()); return Err("Not a valid Drop endpoint".to_string());
} }