Add auth
This commit is contained in:
		
							parent
							
								
									18ed991b93
								
							
						
					
					
						commit
						533260edd8
					
				
					 6 changed files with 274 additions and 94 deletions
				
			
		
							
								
								
									
										26
									
								
								Cargo.lock
									
									
									
										generated
									
									
									
								
							
							
						
						
									
										26
									
								
								Cargo.lock
									
									
									
										generated
									
									
									
								
							|  | @ -497,6 +497,7 @@ dependencies = [ | ||||||
|  "ruma-client-api", |  "ruma-client-api", | ||||||
|  "ruma-events", |  "ruma-events", | ||||||
|  "ruma-identifiers", |  "ruma-identifiers", | ||||||
|  |  "serde_json", | ||||||
|  "sled", |  "sled", | ||||||
| ] | ] | ||||||
| 
 | 
 | ||||||
|  | @ -807,9 +808,9 @@ dependencies = [ | ||||||
| 
 | 
 | ||||||
| [[package]] | [[package]] | ||||||
| name = "ruma-api" | name = "ruma-api" | ||||||
| version = "0.15.0-dev.1" | version = "0.15.0" | ||||||
| source = "registry+https://github.com/rust-lang/crates.io-index" | source = "registry+https://github.com/rust-lang/crates.io-index" | ||||||
| checksum = "44987d5fefcf801a6fb5c5843c17f876a53852fa07e5e4d99e0dca3670f1441a" | checksum = "120f0cd8625b842423ef3a63cabb8c309ca35a02de87cc4b377fb2cdd43f1fe5" | ||||||
| dependencies = [ | dependencies = [ | ||||||
|  "http", |  "http", | ||||||
|  "percent-encoding 2.1.0", |  "percent-encoding 2.1.0", | ||||||
|  | @ -824,9 +825,9 @@ dependencies = [ | ||||||
| 
 | 
 | ||||||
| [[package]] | [[package]] | ||||||
| name = "ruma-api-macros" | name = "ruma-api-macros" | ||||||
| version = "0.12.0-dev.1" | version = "0.12.0" | ||||||
| source = "registry+https://github.com/rust-lang/crates.io-index" | source = "registry+https://github.com/rust-lang/crates.io-index" | ||||||
| checksum = "36931db94874129f9202f650d91447d8317b099bae1e12cdd5769ba4eced07d2" | checksum = "bfc523efc9c1ba7033ff17888551c1d378e12eae087cfbe4fcee938ff516759e" | ||||||
| dependencies = [ | dependencies = [ | ||||||
|  "proc-macro2 1.0.9", |  "proc-macro2 1.0.9", | ||||||
|  "quote 1.0.3", |  "quote 1.0.3", | ||||||
|  | @ -835,8 +836,9 @@ dependencies = [ | ||||||
| 
 | 
 | ||||||
| [[package]] | [[package]] | ||||||
| name = "ruma-client-api" | name = "ruma-client-api" | ||||||
| version = "0.6.0" | version = "0.7.0" | ||||||
| source = "git+https://github.com/ruma/ruma-client-api#57f5e8d66168a54128426c8e34b26fa78f739c3e" | source = "registry+https://github.com/rust-lang/crates.io-index" | ||||||
|  | checksum = "5a64241cdc0cff76038484451d7a5d2689f8ea4e59b6695cd3c8448af7bcc016" | ||||||
| dependencies = [ | dependencies = [ | ||||||
|  "http", |  "http", | ||||||
|  "js_int", |  "js_int", | ||||||
|  | @ -851,9 +853,9 @@ dependencies = [ | ||||||
| 
 | 
 | ||||||
| [[package]] | [[package]] | ||||||
| name = "ruma-events" | name = "ruma-events" | ||||||
| version = "0.17.0" | version = "0.18.0" | ||||||
| source = "registry+https://github.com/rust-lang/crates.io-index" | source = "registry+https://github.com/rust-lang/crates.io-index" | ||||||
| checksum = "11951235b25c72a82eb988aabf5af23cae883562665e0cb73954ffe4ae81f11c" | checksum = "80e34bfc20462f18d7f0beb6f1863db62d29438f2dcf390b625e9b20696cb2b3" | ||||||
| dependencies = [ | dependencies = [ | ||||||
|  "js_int", |  "js_int", | ||||||
|  "ruma-events-macros", |  "ruma-events-macros", | ||||||
|  | @ -864,9 +866,9 @@ dependencies = [ | ||||||
| 
 | 
 | ||||||
| [[package]] | [[package]] | ||||||
| name = "ruma-events-macros" | name = "ruma-events-macros" | ||||||
| version = "0.2.0" | version = "0.3.0" | ||||||
| source = "registry+https://github.com/rust-lang/crates.io-index" | source = "registry+https://github.com/rust-lang/crates.io-index" | ||||||
| checksum = "962d93056619ed61826a9d8872c863560e4892ff6a69b70f593baa5ae8b19dc8" | checksum = "ff95b6b4480c570db471b490b35ad70add5470651654e75faf0b97052b4f29e1" | ||||||
| dependencies = [ | dependencies = [ | ||||||
|  "proc-macro2 1.0.9", |  "proc-macro2 1.0.9", | ||||||
|  "quote 1.0.3", |  "quote 1.0.3", | ||||||
|  | @ -994,9 +996,9 @@ dependencies = [ | ||||||
| 
 | 
 | ||||||
| [[package]] | [[package]] | ||||||
| name = "serde_json" | name = "serde_json" | ||||||
| version = "1.0.49" | version = "1.0.50" | ||||||
| source = "registry+https://github.com/rust-lang/crates.io-index" | source = "registry+https://github.com/rust-lang/crates.io-index" | ||||||
| checksum = "02044a6a92866fd61624b3db4d2c9dccc2feabbc6be490b87611bf285edbac55" | checksum = "78a7a12c167809363ec3bd7329fc0a3369056996de43c4b37ef3cd54a6ce4867" | ||||||
| dependencies = [ | dependencies = [ | ||||||
|  "itoa", |  "itoa", | ||||||
|  "ryu", |  "ryu", | ||||||
|  |  | ||||||
|  | @ -9,12 +9,13 @@ edition = "2018" | ||||||
| [dependencies] | [dependencies] | ||||||
| rocket = { git = "https://github.com/SergioBenitez/Rocket.git", features = ["tls"] } | rocket = { git = "https://github.com/SergioBenitez/Rocket.git", features = ["tls"] } | ||||||
| http = "0.2.1" | http = "0.2.1" | ||||||
| ruma-client-api = { git = "https://github.com/ruma/ruma-client-api" } | ruma-client-api = "0.7.0" | ||||||
| pretty_env_logger = "0.4.0" | pretty_env_logger = "0.4.0" | ||||||
| log = "0.4.8" | log = "0.4.8" | ||||||
| sled = "0.31.0" | sled = "0.31.0" | ||||||
| directories = "2.0.2" | directories = "2.0.2" | ||||||
| ruma-identifiers = "0.14.1" | ruma-identifiers = "0.14.1" | ||||||
| ruma-api = "0.15.0-dev.1" | ruma-api = "0.15.0" | ||||||
| ruma-events = "0.17.0" | ruma-events = "0.18.0" | ||||||
| js_int = "0.1.3" | js_int = "0.1.3" | ||||||
|  | serde_json = "1.0.50" | ||||||
|  |  | ||||||
							
								
								
									
										110
									
								
								src/data.rs
									
									
									
									
									
								
							
							
						
						
									
										110
									
								
								src/data.rs
									
									
									
									
									
								
							|  | @ -1,16 +1,18 @@ | ||||||
|  | use crate::utils; | ||||||
| use directories::ProjectDirs; | use directories::ProjectDirs; | ||||||
| use ruma_events::collections::all::RoomEvent; | use ruma_events::collections::all::RoomEvent; | ||||||
| use ruma_identifiers::UserId; | use ruma_identifiers::UserId; | ||||||
|  | use std::convert::TryInto; | ||||||
|  | 
 | ||||||
|  | const USERID_PASSWORD: &str = "userid_password"; | ||||||
|  | const USERID_DEVICEIDS: &str = "userid_deviceids"; | ||||||
|  | const DEVICEID_TOKEN: &str = "deviceid_token"; | ||||||
|  | const TOKEN_USERID: &str = "token_userid"; | ||||||
| 
 | 
 | ||||||
| pub struct Data(sled::Db); | pub struct Data(sled::Db); | ||||||
| 
 | 
 | ||||||
| impl Data { | impl Data { | ||||||
|     pub fn set_hostname(&self, hostname: &str) { |     /// Load an existing database or create a new one.
 | ||||||
|         self.0.insert("hostname", hostname).unwrap(); |  | ||||||
|     } |  | ||||||
|     pub fn hostname(&self) -> String { |  | ||||||
|         String::from_utf8(self.0.get("hostname").unwrap().unwrap().to_vec()).unwrap() |  | ||||||
|     } |  | ||||||
|     pub fn load_or_create() -> Self { |     pub fn load_or_create() -> Self { | ||||||
|         Data( |         Data( | ||||||
|             sled::open( |             sled::open( | ||||||
|  | @ -22,21 +24,109 @@ impl Data { | ||||||
|         ) |         ) | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|  |     /// Set the hostname of the server. Warning: Hostname changes will likely break things.
 | ||||||
|  |     pub fn set_hostname(&self, hostname: &str) { | ||||||
|  |         self.0.insert("hostname", hostname).unwrap(); | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     /// Get the hostname of the server.
 | ||||||
|  |     pub fn hostname(&self) -> String { | ||||||
|  |         utils::bytes_to_string(&self.0.get("hostname").unwrap().unwrap()) | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     /// Check if a user has an account by looking for an assigned password.
 | ||||||
|     pub fn user_exists(&self, user_id: &UserId) -> bool { |     pub fn user_exists(&self, user_id: &UserId) -> bool { | ||||||
|         self.0 |         self.0 | ||||||
|             .open_tree("username_password") |             .open_tree(USERID_PASSWORD) | ||||||
|             .unwrap() |             .unwrap() | ||||||
|             .contains_key(user_id.to_string()) |             .contains_key(user_id.to_string()) | ||||||
|             .unwrap() |             .unwrap() | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     pub fn user_add(&self, user_id: UserId, password: Option<String>) { |     /// Create a new user account by assigning them a password.
 | ||||||
|  |     pub fn user_add(&self, user_id: &UserId, password: Option<String>) { | ||||||
|         self.0 |         self.0 | ||||||
|             .open_tree("username_password") |             .open_tree(USERID_PASSWORD) | ||||||
|             .unwrap() |             .unwrap() | ||||||
|             .insert(user_id.to_string(), &*password.unwrap_or_default()) |             .insert(user_id.to_string(), &*password.unwrap_or_default()) | ||||||
|             .unwrap(); |             .unwrap(); | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     pub fn room_event_add(&self, room_event: &RoomEvent) {} |     /// Find out which user an access token belongs to.
 | ||||||
|  |     pub fn user_from_token(&self, token: &str) -> Option<UserId> { | ||||||
|  |         self.0 | ||||||
|  |             .open_tree(TOKEN_USERID) | ||||||
|  |             .unwrap() | ||||||
|  |             .get(token) | ||||||
|  |             .unwrap() | ||||||
|  |             .and_then(|bytes| (*utils::bytes_to_string(&bytes)).try_into().ok()) | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     /// Checks if the given password is equal to the one in the database.
 | ||||||
|  |     pub fn password_get(&self, user_id: &UserId) -> Option<String> { | ||||||
|  |         self.0 | ||||||
|  |             .open_tree(USERID_PASSWORD) | ||||||
|  |             .unwrap() | ||||||
|  |             .get(user_id.to_string()) | ||||||
|  |             .unwrap() | ||||||
|  |             .map(|bytes| utils::bytes_to_string(&bytes)) | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     /// Add a new device to a user.
 | ||||||
|  |     pub fn device_add(&self, user_id: &UserId, device_id: &str) { | ||||||
|  |         self.0 | ||||||
|  |             .open_tree(USERID_DEVICEIDS) | ||||||
|  |             .unwrap() | ||||||
|  |             .insert(user_id.to_string(), device_id) | ||||||
|  |             .unwrap(); | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     /// Replace the access token of one device.
 | ||||||
|  |     pub fn token_replace(&self, user_id: &UserId, device_id: &String, token: String) { | ||||||
|  |         // Make sure the device id belongs to the user
 | ||||||
|  |         debug_assert!(self | ||||||
|  |             .0 | ||||||
|  |             .open_tree(USERID_DEVICEIDS) | ||||||
|  |             .unwrap() | ||||||
|  |             .get(&user_id.to_string()) // Does the user exist?
 | ||||||
|  |             .unwrap() | ||||||
|  |             .map(|bytes| utils::bytes_to_vec(&bytes)) | ||||||
|  |             .filter(|devices| devices.contains(device_id)) // Does the user have that device?
 | ||||||
|  |             .is_some()); | ||||||
|  | 
 | ||||||
|  |         // Remove old token
 | ||||||
|  |         if let Some(old_token) = self | ||||||
|  |             .0 | ||||||
|  |             .open_tree(DEVICEID_TOKEN) | ||||||
|  |             .unwrap() | ||||||
|  |             .get(device_id) | ||||||
|  |             .unwrap() | ||||||
|  |         { | ||||||
|  |             self.0 | ||||||
|  |                 .open_tree(TOKEN_USERID) | ||||||
|  |                 .unwrap() | ||||||
|  |                 .remove(old_token) | ||||||
|  |                 .unwrap(); | ||||||
|  |             // It will be removed from DEVICEID_TOKEN by the insert later
 | ||||||
|  |         } | ||||||
|  | 
 | ||||||
|  |         // Assign token to device_id
 | ||||||
|  |         self.0 | ||||||
|  |             .open_tree(DEVICEID_TOKEN) | ||||||
|  |             .unwrap() | ||||||
|  |             .insert(device_id, &*token) | ||||||
|  |             .unwrap(); | ||||||
|  | 
 | ||||||
|  |         // Assign token to user
 | ||||||
|  |         self.0 | ||||||
|  |             .open_tree(TOKEN_USERID) | ||||||
|  |             .unwrap() | ||||||
|  |             .insert(token, &*user_id.to_string()) | ||||||
|  |             .unwrap(); | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     /// Create a new room event.
 | ||||||
|  |     pub fn room_event_add(&self, _room_event: &RoomEvent) { | ||||||
|  |         todo!(); | ||||||
|  |     } | ||||||
| } | } | ||||||
|  |  | ||||||
							
								
								
									
										154
									
								
								src/main.rs
									
									
									
									
									
								
							
							
						
						
									
										154
									
								
								src/main.rs
									
									
									
									
									
								
							|  | @ -14,9 +14,10 @@ use ruma_client_api::{ | ||||||
|     }, |     }, | ||||||
|     unversioned::get_supported_versions, |     unversioned::get_supported_versions, | ||||||
| }; | }; | ||||||
| use ruma_events::room::message::MessageEvent; | use ruma_events::{room::message::MessageEvent, EventResult}; | ||||||
| use ruma_identifiers::{EventId, UserId}; | use ruma_identifiers::{EventId, UserId}; | ||||||
| use ruma_wrapper::{MatrixResult, Ruma}; | use ruma_wrapper::{MatrixResult, Ruma}; | ||||||
|  | use serde_json::map::Map; | ||||||
| use std::convert::TryFrom; | use std::convert::TryFrom; | ||||||
| use std::{collections::HashMap, convert::TryInto}; | use std::{collections::HashMap, convert::TryInto}; | ||||||
| 
 | 
 | ||||||
|  | @ -41,6 +42,7 @@ fn register_route( | ||||||
|     data: State<Data>, |     data: State<Data>, | ||||||
|     body: Ruma<register::Request>, |     body: Ruma<register::Request>, | ||||||
| ) -> MatrixResult<register::Response> { | ) -> MatrixResult<register::Response> { | ||||||
|  |     // Validate user id
 | ||||||
|     let user_id: UserId = match (*format!( |     let user_id: UserId = match (*format!( | ||||||
|         "@{}:{}", |         "@{}:{}", | ||||||
|         body.username.clone().unwrap_or("randomname".to_owned()), |         body.username.clone().unwrap_or("randomname".to_owned()), | ||||||
|  | @ -59,6 +61,7 @@ fn register_route( | ||||||
|         Ok(user_id) => user_id, |         Ok(user_id) => user_id, | ||||||
|     }; |     }; | ||||||
| 
 | 
 | ||||||
|  |     // Check if username is creative enough
 | ||||||
|     if data.user_exists(&user_id) { |     if data.user_exists(&user_id) { | ||||||
|         debug!("ID already taken"); |         debug!("ID already taken"); | ||||||
|         return MatrixResult(Err(Error { |         return MatrixResult(Err(Error { | ||||||
|  | @ -68,68 +71,115 @@ fn register_route( | ||||||
|         })); |         })); | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     data.user_add(user_id.clone(), body.password.clone()); |     // Create user
 | ||||||
|  |     data.user_add(&user_id, body.password.clone()); | ||||||
|  | 
 | ||||||
|  |     // Generate new device id if the user didn't specify one
 | ||||||
|  |     let device_id = body | ||||||
|  |         .device_id | ||||||
|  |         .clone() | ||||||
|  |         .unwrap_or_else(|| "TODO:randomdeviceid".to_owned()); | ||||||
|  | 
 | ||||||
|  |     // Add device
 | ||||||
|  |     data.device_add(&user_id, &device_id); | ||||||
|  | 
 | ||||||
|  |     // Generate new token for the device
 | ||||||
|  |     let token = "TODO:randomtoken".to_owned(); | ||||||
|  |     data.token_replace(&user_id, &device_id, token.clone()); | ||||||
| 
 | 
 | ||||||
|     MatrixResult(Ok(register::Response { |     MatrixResult(Ok(register::Response { | ||||||
|         access_token: "randomtoken".to_owned(), |         access_token: token, | ||||||
|         home_server: data.hostname(), |         home_server: data.hostname(), | ||||||
|         user_id, |         user_id, | ||||||
|         device_id: body.device_id.clone().unwrap_or("randomid".to_owned()), |         device_id, | ||||||
|     })) |     })) | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| #[post("/_matrix/client/r0/login", data = "<body>")] | #[post("/_matrix/client/r0/login", data = "<body>")] | ||||||
| fn login_route(data: State<Data>, body: Ruma<login::Request>) -> MatrixResult<login::Response> { | fn login_route(data: State<Data>, body: Ruma<login::Request>) -> MatrixResult<login::Response> { | ||||||
|     let username = if let login::UserInfo::MatrixId(mut username) = body.user.clone() { |     // Validate login method
 | ||||||
|         if !username.contains(':') { |     let user_id = | ||||||
|             username = format!("@{}:{}", username, data.hostname()); |         if let (login::UserInfo::MatrixId(mut username), login::LoginInfo::Password { password }) = | ||||||
|         } |             (body.user.clone(), body.login_info.clone()) | ||||||
|         if let Ok(user_id) = (*username).try_into() { |         { | ||||||
|             if !data.user_exists(&user_id) { |             if !username.contains(':') { | ||||||
|                 debug!("Userid does not exist. Can't log in."); |                 username = format!("@{}:{}", username, data.hostname()); | ||||||
|  |             } | ||||||
|  |             if let Ok(user_id) = (*username).try_into() { | ||||||
|  |                 if !data.user_exists(&user_id) {} | ||||||
|  | 
 | ||||||
|  |                 // Check password
 | ||||||
|  |                 if let Some(correct_password) = data.password_get(&user_id) { | ||||||
|  |                     if password == correct_password { | ||||||
|  |                         // Success!
 | ||||||
|  |                         user_id | ||||||
|  |                     } else { | ||||||
|  |                         debug!("Invalid password."); | ||||||
|  |                         return MatrixResult(Err(Error { | ||||||
|  |                             kind: ErrorKind::Unknown, | ||||||
|  |                             message: "".to_owned(), | ||||||
|  |                             status_code: http::StatusCode::FORBIDDEN, | ||||||
|  |                         })); | ||||||
|  |                     } | ||||||
|  |                 } else { | ||||||
|  |                     debug!("UserId does not exist (has no assigned password). Can't log in."); | ||||||
|  |                     return MatrixResult(Err(Error { | ||||||
|  |                         kind: ErrorKind::Forbidden, | ||||||
|  |                         message: "".to_owned(), | ||||||
|  |                         status_code: http::StatusCode::FORBIDDEN, | ||||||
|  |                     })); | ||||||
|  |                 } | ||||||
|  |             } else { | ||||||
|  |                 debug!("Invalid UserId."); | ||||||
|                 return MatrixResult(Err(Error { |                 return MatrixResult(Err(Error { | ||||||
|                     kind: ErrorKind::Forbidden, |                     kind: ErrorKind::Unknown, | ||||||
|                     message: "UserId not found.".to_owned(), |                     message: "Bad login type.".to_owned(), | ||||||
|                     status_code: http::StatusCode::BAD_REQUEST, |                     status_code: http::StatusCode::BAD_REQUEST, | ||||||
|                 })); |                 })); | ||||||
|             } |             } | ||||||
|             user_id |  | ||||||
|         } else { |         } else { | ||||||
|             debug!("Invalid UserId."); |             debug!("Bad login type"); | ||||||
|             return MatrixResult(Err(Error { |             return MatrixResult(Err(Error { | ||||||
|                 kind: ErrorKind::Unknown, |                 kind: ErrorKind::Unknown, | ||||||
|                 message: "Bad login type.".to_owned(), |                 message: "Bad login type.".to_owned(), | ||||||
|                 status_code: http::StatusCode::BAD_REQUEST, |                 status_code: http::StatusCode::BAD_REQUEST, | ||||||
|             })); |             })); | ||||||
|         } |         }; | ||||||
|     } else { | 
 | ||||||
|         debug!("Bad login type"); |     // Generate new device id if the user didn't specify one
 | ||||||
|         return MatrixResult(Err(Error { |     let device_id = body | ||||||
|             kind: ErrorKind::Unknown, |         .device_id | ||||||
|             message: "Bad login type.".to_owned(), |         .clone() | ||||||
|             status_code: http::StatusCode::BAD_REQUEST, |         .unwrap_or("TODO:randomdeviceid".to_owned()); | ||||||
|         })); | 
 | ||||||
|     }; |     // Add device (TODO: We might not want to call it when using an existing device)
 | ||||||
|  |     data.device_add(&user_id, &device_id); | ||||||
|  | 
 | ||||||
|  |     // Generate a new token for the device
 | ||||||
|  |     let token = "TODO:randomtoken".to_owned(); | ||||||
|  |     data.token_replace(&user_id, &device_id, token.clone()); | ||||||
| 
 | 
 | ||||||
|     return MatrixResult(Ok(login::Response { |     return MatrixResult(Ok(login::Response { | ||||||
|         user_id: username.try_into().unwrap(), // Unwrap is okay because the user is already registered
 |         user_id, | ||||||
|         access_token: "randomtoken".to_owned(), |         access_token: token, | ||||||
|         home_server: Some("localhost".to_owned()), |         home_server: Some(data.hostname()), | ||||||
|         device_id: body.device_id.clone().unwrap_or("randomid".to_owned()), |         device_id, | ||||||
|         well_known: None, |         well_known: None, | ||||||
|     })); |     })); | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| #[get("/_matrix/client/r0/directory/room/<room_alias>")] | #[get("/_matrix/client/r0/directory/room/<room_alias>")] | ||||||
| fn get_alias_route(room_alias: String) -> MatrixResult<get_alias::Response> { | fn get_alias_route(room_alias: String) -> MatrixResult<get_alias::Response> { | ||||||
|  |     // TODO
 | ||||||
|     let room_id = match &*room_alias { |     let room_id = match &*room_alias { | ||||||
|         "#room:localhost" => "!xclkjvdlfj:localhost", |         "#room:localhost" => "!xclkjvdlfj:localhost", | ||||||
|         _ => { |         _ => { | ||||||
|  |             debug!("Room not found."); | ||||||
|             return MatrixResult(Err(Error { |             return MatrixResult(Err(Error { | ||||||
|                 kind: ErrorKind::NotFound, |                 kind: ErrorKind::NotFound, | ||||||
|                 message: "Room not found.".to_owned(), |                 message: "Room not found.".to_owned(), | ||||||
|                 status_code: http::StatusCode::NOT_FOUND, |                 status_code: http::StatusCode::NOT_FOUND, | ||||||
|             })) |             })); | ||||||
|         } |         } | ||||||
|     } |     } | ||||||
|     .try_into() |     .try_into() | ||||||
|  | @ -146,6 +196,7 @@ fn join_room_by_id_route( | ||||||
|     _room_id: String, |     _room_id: String, | ||||||
|     body: Ruma<join_room_by_id::Request>, |     body: Ruma<join_room_by_id::Request>, | ||||||
| ) -> MatrixResult<join_room_by_id::Response> { | ) -> MatrixResult<join_room_by_id::Response> { | ||||||
|  |     // TODO
 | ||||||
|     MatrixResult(Ok(join_room_by_id::Response { |     MatrixResult(Ok(join_room_by_id::Response { | ||||||
|         room_id: body.room_id.clone(), |         room_id: body.room_id.clone(), | ||||||
|     })) |     })) | ||||||
|  | @ -162,23 +213,34 @@ fn create_message_event_route( | ||||||
|     _txn_id: String, |     _txn_id: String, | ||||||
|     body: Ruma<create_message_event::Request>, |     body: Ruma<create_message_event::Request>, | ||||||
| ) -> MatrixResult<create_message_event::Response> { | ) -> MatrixResult<create_message_event::Response> { | ||||||
|     dbg!(&body); |     // Check if content is valid
 | ||||||
|     if let Ok(content) = body.data.clone().into_result() { |     let content = match body.data.clone() { | ||||||
|         data.room_event_add( |         EventResult::Ok(content) => content, | ||||||
|             &MessageEvent { |         EventResult::Err(_) => { | ||||||
|                 content, |             debug!("No content."); | ||||||
|                 event_id: EventId::try_from("$randomeventid:localhost").unwrap(), |             return MatrixResult(Err(Error { | ||||||
|                 origin_server_ts: utils::millis_since_unix_epoch(), |                 kind: ErrorKind::NotFound, | ||||||
|                 room_id: Some(body.room_id.clone()), |                 message: "No content.".to_owned(), | ||||||
|                 sender: UserId::try_from("@TODO:localhost").unwrap(), |                 status_code: http::StatusCode::BAD_REQUEST, | ||||||
|                 unsigned: None, |             })); | ||||||
|             } |         } | ||||||
|             .into(), |     }; | ||||||
|         ); | 
 | ||||||
|     } |     let event_id = EventId::try_from("$TODOrandomeventid:localhost").unwrap(); | ||||||
|     MatrixResult(Ok(create_message_event::Response { | 
 | ||||||
|         event_id: "$randomeventid:localhost".try_into().unwrap(), |     data.room_event_add( | ||||||
|     })) |         &MessageEvent { | ||||||
|  |             content, | ||||||
|  |             event_id: event_id.clone(), | ||||||
|  |             origin_server_ts: utils::millis_since_unix_epoch(), | ||||||
|  |             room_id: Some(body.room_id.clone()), | ||||||
|  |             sender: body.user_id.expect("user is authenticated"), | ||||||
|  |             unsigned: Map::default(), | ||||||
|  |         } | ||||||
|  |         .into(), | ||||||
|  |     ); | ||||||
|  | 
 | ||||||
|  |     MatrixResult(Ok(create_message_event::Response { event_id })) | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| fn main() { | fn main() { | ||||||
|  |  | ||||||
|  | @ -10,10 +10,10 @@ use { | ||||||
|         Endpoint, Outgoing, |         Endpoint, Outgoing, | ||||||
|     }, |     }, | ||||||
|     ruma_client_api::error::Error, |     ruma_client_api::error::Error, | ||||||
|  |     ruma_identifiers::UserId, | ||||||
|     std::ops::Deref, |     std::ops::Deref, | ||||||
|     std::{ |     std::{ | ||||||
|         convert::{TryFrom, TryInto}, |         convert::{TryFrom, TryInto}, | ||||||
|         fmt, |  | ||||||
|         io::{Cursor, Read}, |         io::{Cursor, Read}, | ||||||
|     }, |     }, | ||||||
| }; | }; | ||||||
|  | @ -22,9 +22,10 @@ const MESSAGE_LIMIT: u64 = 65535; | ||||||
| 
 | 
 | ||||||
| /// This struct converts rocket requests into ruma structs by converting them into http requests
 | /// This struct converts rocket requests into ruma structs by converting them into http requests
 | ||||||
| /// first.
 | /// first.
 | ||||||
|  | #[derive(Debug)] | ||||||
| pub struct Ruma<T: Outgoing> { | pub struct Ruma<T: Outgoing> { | ||||||
|     body: T::Incoming, |     body: T::Incoming, | ||||||
|     headers: http::HeaderMap<http::header::HeaderValue>, |     pub user_id: Option<UserId>, | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| impl<T: Endpoint> FromDataSimple for Ruma<T> | impl<T: Endpoint> FromDataSimple for Ruma<T> | ||||||
|  | @ -37,9 +38,34 @@ where | ||||||
|         Error = FromHttpResponseError<<T as Endpoint>::ResponseError>, |         Error = FromHttpResponseError<<T as Endpoint>::ResponseError>, | ||||||
|     >, |     >, | ||||||
| { | { | ||||||
|     type Error = (); |     type Error = (); // TODO: Better error handling
 | ||||||
| 
 | 
 | ||||||
|     fn from_data(request: &Request, data: rocket::Data) -> Outcome<Self, Self::Error> { |     fn from_data(request: &Request, data: rocket::Data) -> Outcome<Self, Self::Error> { | ||||||
|  |         let user_id = if T::METADATA.requires_authentication { | ||||||
|  |             let data = request.guard::<State<crate::Data>>().unwrap(); | ||||||
|  | 
 | ||||||
|  |             // Get token from header or query value
 | ||||||
|  |             let token = match request | ||||||
|  |                 .headers() | ||||||
|  |                 .get_one("Authorization") | ||||||
|  |                 .map(|s| s.to_owned()) | ||||||
|  |                 .or_else(|| request.get_query_value("access_token").and_then(|r| r.ok())) | ||||||
|  |             { | ||||||
|  |                 // TODO: M_MISSING_TOKEN
 | ||||||
|  |                 None => return Failure((Status::Unauthorized, ())), | ||||||
|  |                 Some(token) => token, | ||||||
|  |             }; | ||||||
|  | 
 | ||||||
|  |             // Check if token is valid
 | ||||||
|  |             match data.user_from_token(&token) { | ||||||
|  |                 // TODO: M_UNKNOWN_TOKEN
 | ||||||
|  |                 None => return Failure((Status::Unauthorized, ())), | ||||||
|  |                 Some(user_id) => Some(user_id), | ||||||
|  |             } | ||||||
|  |         } else { | ||||||
|  |             None | ||||||
|  |         }; | ||||||
|  | 
 | ||||||
|         let mut http_request = http::Request::builder() |         let mut http_request = http::Request::builder() | ||||||
|             .uri(request.uri().to_string()) |             .uri(request.uri().to_string()) | ||||||
|             .method(&*request.method().to_string()); |             .method(&*request.method().to_string()); | ||||||
|  | @ -52,17 +78,10 @@ where | ||||||
|         handle.read_to_end(&mut body).unwrap(); |         handle.read_to_end(&mut body).unwrap(); | ||||||
| 
 | 
 | ||||||
|         let http_request = http_request.body(body).unwrap(); |         let http_request = http_request.body(body).unwrap(); | ||||||
|         let headers = http_request.headers().clone(); |  | ||||||
| 
 | 
 | ||||||
|         log::info!("{:?}", http_request); |         log::info!("{:?}", http_request); | ||||||
|         match T::Incoming::try_from(http_request) { |         match T::Incoming::try_from(http_request) { | ||||||
|             Ok(t) => { |             Ok(t) => Success(Ruma { body: t, user_id }), | ||||||
|                 if T::METADATA.requires_authentication { |  | ||||||
|                     let data = request.guard::<State<crate::Data>>(); |  | ||||||
|                     // TODO: auth
 |  | ||||||
|                 } |  | ||||||
|                 Success(Ruma { body: t, headers }) |  | ||||||
|             } |  | ||||||
|             Err(e) => { |             Err(e) => { | ||||||
|                 log::error!("{:?}", e); |                 log::error!("{:?}", e); | ||||||
|                 Failure((Status::InternalServerError, ())) |                 Failure((Status::InternalServerError, ())) | ||||||
|  | @ -79,18 +98,6 @@ impl<T: Outgoing> Deref for Ruma<T> { | ||||||
|     } |     } | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| impl<T: Outgoing> fmt::Debug for Ruma<T> |  | ||||||
| where |  | ||||||
|     T::Incoming: fmt::Debug, |  | ||||||
| { |  | ||||||
|     fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { |  | ||||||
|         f.debug_struct("Ruma") |  | ||||||
|             .field("body", &self.body) |  | ||||||
|             .field("headers", &self.headers) |  | ||||||
|             .finish() |  | ||||||
|     } |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| /// This struct converts ruma responses into rocket http responses.
 | /// This struct converts ruma responses into rocket http responses.
 | ||||||
| pub struct MatrixResult<T>(pub std::result::Result<T, Error>); | pub struct MatrixResult<T>(pub std::result::Result<T, Error>); | ||||||
| impl<T: TryInto<http::Response<Vec<u8>>>> TryInto<http::Response<Vec<u8>>> for MatrixResult<T> { | impl<T: TryInto<http::Response<Vec<u8>>>> TryInto<http::Response<Vec<u8>>> for MatrixResult<T> { | ||||||
|  |  | ||||||
							
								
								
									
										18
									
								
								src/utils.rs
									
									
									
									
									
								
							
							
						
						
									
										18
									
								
								src/utils.rs
									
									
									
									
									
								
							|  | @ -7,3 +7,21 @@ pub fn millis_since_unix_epoch() -> js_int::UInt { | ||||||
|         .as_millis() as u32) |         .as_millis() as u32) | ||||||
|         .into() |         .into() | ||||||
| } | } | ||||||
|  | 
 | ||||||
|  | pub fn bytes_to_string(bytes: &[u8]) -> String { | ||||||
|  |     String::from_utf8(bytes.to_vec()).expect("convert bytes to string") | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | pub fn vec_to_bytes(vec: Vec<String>) -> Vec<u8> { | ||||||
|  |     vec.into_iter() | ||||||
|  |         .map(|string| string.into_bytes()) | ||||||
|  |         .collect::<Vec<Vec<u8>>>() | ||||||
|  |         .join(&0) | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | pub fn bytes_to_vec(bytes: &[u8]) -> Vec<String> { | ||||||
|  |     bytes | ||||||
|  |         .split(|&b| b == 0) | ||||||
|  |         .map(|bytes_string| bytes_to_string(bytes_string)) | ||||||
|  |         .collect::<Vec<String>>() | ||||||
|  | } | ||||||
|  |  | ||||||
		Loading…
	
		Reference in a new issue