pubky_common/
session.rs

1//! Pubky homeserver session struct.
2
3use pkarr::PublicKey;
4use postcard::{from_bytes, to_allocvec};
5use serde::{Deserialize, Serialize};
6
7extern crate alloc;
8use alloc::vec::Vec;
9
10use crate::{capabilities::Capability, timestamp::Timestamp};
11
12// TODO: add IP address?
13// TODO: use https://crates.io/crates/user-agent-parser to parse the session
14// and get more informations from the user-agent.
15#[derive(Clone, Serialize, Deserialize, Debug, Eq, PartialEq)]
16/// Pubky homeserver session struct.
17pub struct Session {
18    version: usize,
19    pubky: PublicKey,
20    created_at: u64,
21    /// User specified name, defaults to the user-agent.
22    name: String,
23    user_agent: String,
24    capabilities: Vec<Capability>,
25}
26
27impl Session {
28    /// Create a new session.
29    pub fn new(pubky: &PublicKey, capabilities: &[Capability], user_agent: Option<String>) -> Self {
30        Self {
31            version: 0,
32            pubky: pubky.clone(),
33            created_at: Timestamp::now().as_u64(),
34            capabilities: capabilities.to_vec(),
35            user_agent: user_agent.as_deref().unwrap_or("").to_string(),
36            name: user_agent.as_deref().unwrap_or("").to_string(),
37        }
38    }
39
40    // === Getters ===
41
42    /// Returns the pubky of this session authorizes for.
43    pub fn pubky(&self) -> &PublicKey {
44        &self.pubky
45    }
46
47    /// Returns the capabilities this session provide on this session's pubky's resources.
48    pub fn capabilities(&self) -> &Vec<Capability> {
49        &self.capabilities
50    }
51
52    // === Setters ===
53
54    /// Set this session user agent.
55    pub fn set_user_agent(&mut self, user_agent: String) -> &mut Self {
56        self.user_agent = user_agent;
57
58        if self.name.is_empty() {
59            self.name.clone_from(&self.user_agent)
60        }
61
62        self
63    }
64
65    /// Set this session's capabilities.
66    pub fn set_capabilities(&mut self, capabilities: Vec<Capability>) -> &mut Self {
67        self.capabilities = capabilities;
68
69        self
70    }
71
72    // === Public Methods ===
73
74    /// Serialize this session to its canonical binary representation.
75    pub fn serialize(&self) -> Vec<u8> {
76        to_allocvec(self).expect("Session::serialize")
77    }
78
79    /// Deserialize this session from its canonical binary representation.
80    pub fn deserialize(bytes: &[u8]) -> Result<Self, Error> {
81        if bytes.is_empty() {
82            return Err(Error::EmptyPayload);
83        }
84
85        if bytes[0] > 0 {
86            return Err(Error::UnknownVersion);
87        }
88
89        Ok(from_bytes(bytes)?)
90    }
91
92    // TODO: add `can_read()`, `can_write()` and `is_root()` methods
93}
94
95#[derive(thiserror::Error, Debug, PartialEq)]
96/// Error deserializing a [Session].
97pub enum Error {
98    #[error("Empty payload")]
99    /// Empty payload
100    EmptyPayload,
101    #[error("Unknown version")]
102    /// Unknown version
103    UnknownVersion,
104    #[error(transparent)]
105    /// Error parsing the binary representation.
106    Parsing(#[from] postcard::Error),
107}
108
109#[cfg(test)]
110mod tests {
111    use crate::crypto::Keypair;
112
113    use super::*;
114
115    #[test]
116    fn serialize() {
117        let keypair = Keypair::from_secret_key(&[0; 32]);
118        let pubky = keypair.public_key();
119
120        let session = Session {
121            user_agent: "foo".to_string(),
122            capabilities: vec![Capability::root()],
123            created_at: 0,
124            pubky,
125            version: 0,
126            name: "".to_string(),
127        };
128
129        let serialized = session.serialize();
130
131        assert_eq!(
132            serialized,
133            [
134                0, 59, 106, 39, 188, 206, 182, 164, 45, 98, 163, 168, 208, 42, 111, 13, 115, 101,
135                50, 21, 119, 29, 226, 67, 166, 58, 192, 72, 161, 139, 89, 218, 41, 0, 0, 3, 102,
136                111, 111, 1, 4, 47, 58, 114, 119
137            ]
138        );
139
140        let deseiralized = Session::deserialize(&serialized).unwrap();
141
142        assert_eq!(deseiralized, session)
143    }
144
145    #[test]
146    fn deserialize() {
147        let result = Session::deserialize(&[]);
148
149        assert_eq!(result, Err(Error::EmptyPayload));
150    }
151}