main
rs 99 lines 3.12 KB
Raw
1 use serde::{Deserialize, Serialize};
2
3 use uuid::Uuid;
4
5 /// The canonical identifier for a shared session.
6 /// A [`SessionId`] on its own cannot be used to access
7 /// a shared session; you need the corresponding [`SessionSecret`].
8 /// TODO: consider making the internal type a plain old String.
9 #[derive(Debug, Hash, Serialize, Deserialize, Eq, PartialEq, Clone, Copy)]
10 #[serde(transparent)]
11 pub struct SessionId(Uuid);
12 impl SessionId {
13 #[allow(clippy::new_without_default)]
14 pub fn new() -> Self {
15 Self(Uuid::new_v4())
16 }
17 }
18
19 impl std::fmt::Display for SessionId {
20 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
21 write!(f, "{}", self.0)
22 }
23 }
24
25 /// The `warp` server framework uses [`FromStr`] to deserialize
26 /// the string from the route.
27 impl std::str::FromStr for SessionId {
28 type Err = uuid::Error;
29 fn from_str(s: &str) -> Result<Self, Self::Err> {
30 Uuid::from_str(s).map(SessionId)
31 }
32 }
33
34 /// The secret for a shared session.
35 /// A shared session cannot be accessed without its secret.
36 /// The client should treat this as some opaque string.
37 #[derive(Hash, Serialize, Deserialize, Eq, PartialEq, Clone, Default)]
38 #[serde(transparent)]
39 pub struct SessionSecret(String);
40 impl SessionSecret {
41 #[allow(clippy::new_without_default)]
42 pub fn new() -> Self {
43 Self(Uuid::new_v4().to_string())
44 }
45 }
46
47 /// Override the Display impl for the secret to return a mask.
48 /// This makes it harder to leak the secret by accident.
49 impl std::fmt::Display for SessionSecret {
50 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
51 write!(f, "***")
52 }
53 }
54
55 /// Override the Debug impl for the secret to return a mask.
56 /// This makes it harder to leak the secret by accident.
57 impl std::fmt::Debug for SessionSecret {
58 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
59 write!(f, "***")
60 }
61 }
62
63 /// The `warp` server framework uses [`FromStr`] to deserialize
64 /// the string from the route.
65 impl std::str::FromStr for SessionSecret {
66 type Err = core::convert::Infallible;
67 fn from_str(s: &str) -> Result<Self, Self::Err> {
68 String::from_str(s).map(SessionSecret)
69 }
70 }
71
72 /// The parameters needed to attempt to join a shared session.
73 /// This is different from [`viewer::InitPayload`] which
74 /// is the state that a viewer must pass up _after_ successfully
75 /// joining a shared session.
76 #[derive(Clone)]
77 pub struct JoinSessionLinkArgs {
78 pub session_id: SessionId,
79 pub session_secret: SessionSecret,
80 }
81
82 impl JoinSessionLinkArgs {
83 // TODO: ideally, the protocol should just generate the full
84 // link for the client to consume. This will make more sense
85 // once we move away from app URIs.
86 pub fn to_join_route(&self) -> String {
87 format!(
88 "/sessions/join/{}?pwd={}",
89 self.session_id,
90 self.secret_to_string(),
91 )
92 }
93
94 /// Returns the [`SessionSecret`] as a [`String`] for joining purposes.
95 pub fn secret_to_string(&self) -> String {
96 // We can't use the [`SessionSecret`]'s display because it's overriden to be masked.
97 self.session_secret.0.to_string()
98 }
99 }