From 2577225ba52fe6dda30232d0df00bfc5de52cefe Mon Sep 17 00:00:00 2001 From: Adam <13720823+Frinksy@users.noreply.github.com> Date: Wed, 23 Jun 2021 15:11:43 +0100 Subject: [PATCH] Add client secret and session ID types --- .../src/r0/account/add_3pid.rs | 7 +- .../src/r0/account/bind_3pid.rs | 9 +- ...request_3pid_management_token_via_email.rs | 9 +- ...equest_3pid_management_token_via_msisdn.rs | 9 +- ...request_password_change_token_via_email.rs | 9 +- ...equest_password_change_token_via_msisdn.rs | 9 +- .../request_registration_token_via_email.rs | 9 +- .../request_registration_token_via_msisdn.rs | 9 +- .../request_contact_verification_token.rs | 5 +- .../src/client_secret.rs | 13 ++ .../ruma-identifiers-validation/src/error.rs | 4 + crates/ruma-identifiers-validation/src/lib.rs | 2 + .../src/session_id.rs | 13 ++ crates/ruma-identifiers/src/client_secret.rs | 23 +++ crates/ruma-identifiers/src/lib.rs | 4 + crates/ruma-identifiers/src/macros.rs | 169 ++++++++++++++++++ crates/ruma-identifiers/src/server_name.rs | 153 +--------------- crates/ruma-identifiers/src/session_id.rs | 11 ++ .../src/association/bind_3pid/v2.rs | 8 +- .../src/association/check_3pid_validity/v2.rs | 7 +- .../create_email_validation_session/v2.rs | 9 +- .../association/email/validate_email/v2.rs | 7 +- .../email/validate_email_by_end_user/v2.rs | 7 +- .../create_msisdn_validation_session/v2.rs | 9 +- .../association/msisdn/validate_msisdn/v2.rs | 7 +- .../validate_msisdn_by_phone_number/v2.rs | 7 +- crates/ruma-serde-macros/src/outgoing.rs | 4 +- crates/ruma/src/lib.rs | 11 +- 28 files changed, 327 insertions(+), 216 deletions(-) create mode 100644 crates/ruma-identifiers-validation/src/client_secret.rs create mode 100644 crates/ruma-identifiers-validation/src/session_id.rs create mode 100644 crates/ruma-identifiers/src/client_secret.rs create mode 100644 crates/ruma-identifiers/src/session_id.rs diff --git a/crates/ruma-client-api/src/r0/account/add_3pid.rs b/crates/ruma-client-api/src/r0/account/add_3pid.rs index 1759ee70..c9502eb2 100644 --- a/crates/ruma-client-api/src/r0/account/add_3pid.rs +++ b/crates/ruma-client-api/src/r0/account/add_3pid.rs @@ -1,6 +1,7 @@ //! [POST /_matrix/client/r0/account/3pid/add](https://matrix.org/docs/spec/client_server/r0.6.1#post-matrix-client-r0-account-3pid-add) use ruma_api::ruma_api; +use ruma_identifiers::{ClientSecret, SessionId}; use crate::r0::uiaa::{AuthData, IncomingAuthData, UiaaResponse}; @@ -20,10 +21,10 @@ ruma_api! { pub auth: Option>, /// Client-generated secret string used to protect this session. - pub client_secret: &'a str, + pub client_secret: &'a ClientSecret, /// The session identifier given by the identity server. - pub sid: &'a str, + pub sid: &'a SessionId, } #[derive(Default)] @@ -34,7 +35,7 @@ ruma_api! { impl<'a> Request<'a> { /// Creates a new `Request` with the given client secret and session identifier. - pub fn new(client_secret: &'a str, sid: &'a str) -> Self { + pub fn new(client_secret: &'a ClientSecret, sid: &'a SessionId) -> Self { Self { auth: None, client_secret, sid } } } diff --git a/crates/ruma-client-api/src/r0/account/bind_3pid.rs b/crates/ruma-client-api/src/r0/account/bind_3pid.rs index 13ee6476..44e6d788 100644 --- a/crates/ruma-client-api/src/r0/account/bind_3pid.rs +++ b/crates/ruma-client-api/src/r0/account/bind_3pid.rs @@ -1,6 +1,7 @@ //! [POST /_matrix/client/r0/account/3pid/bind](https://matrix.org/docs/spec/client_server/r0.6.0#post-matrix-client-r0-account-3pid-bind) use ruma_api::ruma_api; +use ruma_identifiers::{ClientSecret, SessionId}; use super::{IdentityServerInfo, IncomingIdentityServerInfo}; @@ -16,7 +17,7 @@ ruma_api! { request: { /// Client-generated secret string used to protect this session. - pub client_secret: &'a str, + pub client_secret: &'a ClientSecret, /// The ID server to send the onward request to as a hostname with an /// appended colon and port number if the port is not the default. @@ -24,7 +25,7 @@ ruma_api! { pub identity_server_info: IdentityServerInfo<'a>, /// The session identifier given by the identity server. - pub sid: &'a str, + pub sid: &'a SessionId, } #[derive(Default)] @@ -37,9 +38,9 @@ impl<'a> Request<'a> { /// Creates a new `Request` with the given client secret, identity server information and /// session identifier. pub fn new( - client_secret: &'a str, + client_secret: &'a ClientSecret, identity_server_info: IdentityServerInfo<'a>, - sid: &'a str, + sid: &'a SessionId, ) -> Self { Self { client_secret, identity_server_info, sid } } diff --git a/crates/ruma-client-api/src/r0/account/request_3pid_management_token_via_email.rs b/crates/ruma-client-api/src/r0/account/request_3pid_management_token_via_email.rs index 853f22bf..fc925f98 100644 --- a/crates/ruma-client-api/src/r0/account/request_3pid_management_token_via_email.rs +++ b/crates/ruma-client-api/src/r0/account/request_3pid_management_token_via_email.rs @@ -2,6 +2,7 @@ use js_int::UInt; use ruma_api::ruma_api; +use ruma_identifiers::{ClientSecret, SessionIdBox}; use super::{IdentityServerInfo, IncomingIdentityServerInfo}; @@ -17,7 +18,7 @@ ruma_api! { request: { /// Client-generated secret string used to protect this session. - pub client_secret: &'a str, + pub client_secret: &'a ClientSecret, /// The email address. pub email: &'a str, @@ -37,7 +38,7 @@ ruma_api! { response: { /// The session identifier given by the identity server. - pub sid: String, + pub sid: SessionIdBox, /// URL to submit validation token to. If omitted, verification happens without client. /// @@ -56,14 +57,14 @@ ruma_api! { impl<'a> Request<'a> { /// Creates a new `Request` with the client secret, email and send-attempt counter. - pub fn new(client_secret: &'a str, email: &'a str, send_attempt: UInt) -> Self { + pub fn new(client_secret: &'a ClientSecret, email: &'a str, send_attempt: UInt) -> Self { Self { client_secret, email, send_attempt, next_link: None, identity_server_info: None } } } impl Response { /// Creates a new `Response` with the given session identifier. - pub fn new(sid: String) -> Self { + pub fn new(sid: SessionIdBox) -> Self { Self { sid, submit_url: None } } } diff --git a/crates/ruma-client-api/src/r0/account/request_3pid_management_token_via_msisdn.rs b/crates/ruma-client-api/src/r0/account/request_3pid_management_token_via_msisdn.rs index 33143c20..6157d633 100644 --- a/crates/ruma-client-api/src/r0/account/request_3pid_management_token_via_msisdn.rs +++ b/crates/ruma-client-api/src/r0/account/request_3pid_management_token_via_msisdn.rs @@ -2,6 +2,7 @@ use js_int::UInt; use ruma_api::ruma_api; +use ruma_identifiers::{ClientSecret, SessionIdBox}; use super::{IdentityServerInfo, IncomingIdentityServerInfo}; @@ -17,7 +18,7 @@ ruma_api! { request: { /// Client-generated secret string used to protect this session. - pub client_secret: &'a str, + pub client_secret: &'a ClientSecret, /// Two-letter ISO 3166 country code for the phone number. pub country: &'a str, @@ -40,7 +41,7 @@ ruma_api! { response: { /// The session identifier given by the identity server. - pub sid: String, + pub sid: SessionIdBox, /// URL to submit validation token to. If omitted, verification happens without client. /// @@ -61,7 +62,7 @@ impl<'a> Request<'a> { /// Creates a new `Request` with the given client secret, country code, phone number and /// send-attempt counter. pub fn new( - client_secret: &'a str, + client_secret: &'a ClientSecret, country: &'a str, phone_number: &'a str, send_attempt: UInt, @@ -79,7 +80,7 @@ impl<'a> Request<'a> { impl Response { /// Creates a new `Response` with the given session identifier. - pub fn new(sid: String) -> Self { + pub fn new(sid: SessionIdBox) -> Self { Self { sid, submit_url: None } } } diff --git a/crates/ruma-client-api/src/r0/account/request_password_change_token_via_email.rs b/crates/ruma-client-api/src/r0/account/request_password_change_token_via_email.rs index 8030d2b3..9cfe78c5 100644 --- a/crates/ruma-client-api/src/r0/account/request_password_change_token_via_email.rs +++ b/crates/ruma-client-api/src/r0/account/request_password_change_token_via_email.rs @@ -2,6 +2,7 @@ use js_int::UInt; use ruma_api::ruma_api; +use ruma_identifiers::{ClientSecret, SessionIdBox}; use super::{IdentityServerInfo, IncomingIdentityServerInfo}; @@ -17,7 +18,7 @@ ruma_api! { request: { /// Client-generated secret string used to protect this session. - pub client_secret: &'a str, + pub client_secret: &'a ClientSecret, /// The email address. pub email: &'a str, @@ -37,7 +38,7 @@ ruma_api! { response: { /// The session identifier given by the identity server. - pub sid: String, + pub sid: SessionIdBox, /// URL to submit validation token to. If omitted, verification happens without client. /// @@ -57,14 +58,14 @@ ruma_api! { impl<'a> Request<'a> { /// Creates a new `Request` with the given client secret, email address and send-attempt /// counter. - pub fn new(client_secret: &'a str, email: &'a str, send_attempt: UInt) -> Self { + pub fn new(client_secret: &'a ClientSecret, email: &'a str, send_attempt: UInt) -> Self { Self { client_secret, email, send_attempt, next_link: None, identity_server_info: None } } } impl Response { /// Creates a new `Response` with the given session identifier. - pub fn new(sid: String) -> Self { + pub fn new(sid: SessionIdBox) -> Self { Self { sid, submit_url: None } } } diff --git a/crates/ruma-client-api/src/r0/account/request_password_change_token_via_msisdn.rs b/crates/ruma-client-api/src/r0/account/request_password_change_token_via_msisdn.rs index 5a2c0c15..24fb6524 100644 --- a/crates/ruma-client-api/src/r0/account/request_password_change_token_via_msisdn.rs +++ b/crates/ruma-client-api/src/r0/account/request_password_change_token_via_msisdn.rs @@ -2,6 +2,7 @@ use js_int::UInt; use ruma_api::ruma_api; +use ruma_identifiers::{ClientSecret, SessionIdBox}; ruma_api! { metadata: { @@ -15,7 +16,7 @@ ruma_api! { request: { /// Client-generated secret string used to protect this session. - pub client_secret: &'a str, + pub client_secret: &'a ClientSecret, /// Two-letter ISO 3166 country code for the phone number. pub country: &'a str, @@ -33,7 +34,7 @@ ruma_api! { response: { /// The session identifier given by the identity server. - pub sid: String, + pub sid: SessionIdBox, /// URL to submit validation token to. If omitted, verification happens without client. /// @@ -54,7 +55,7 @@ impl<'a> Request<'a> { /// Creates a new `Request` with the given client secret, country code, phone number and /// send-attempt counter. pub fn new( - client_secret: &'a str, + client_secret: &'a ClientSecret, country: &'a str, phone_number: &'a str, send_attempt: UInt, @@ -65,7 +66,7 @@ impl<'a> Request<'a> { impl Response { /// Creates a new `Response` with the given session identifier. - pub fn new(sid: String) -> Self { + pub fn new(sid: SessionIdBox) -> Self { Self { sid, submit_url: None } } } diff --git a/crates/ruma-client-api/src/r0/account/request_registration_token_via_email.rs b/crates/ruma-client-api/src/r0/account/request_registration_token_via_email.rs index d336be98..7e8d5c24 100644 --- a/crates/ruma-client-api/src/r0/account/request_registration_token_via_email.rs +++ b/crates/ruma-client-api/src/r0/account/request_registration_token_via_email.rs @@ -2,6 +2,7 @@ use js_int::UInt; use ruma_api::ruma_api; +use ruma_identifiers::{ClientSecret, SessionIdBox}; use super::{IdentityServerInfo, IncomingIdentityServerInfo}; @@ -17,7 +18,7 @@ ruma_api! { request: { /// Client-generated secret string used to protect this session. - pub client_secret: &'a str, + pub client_secret: &'a ClientSecret, /// The email address. pub email: &'a str, @@ -37,7 +38,7 @@ ruma_api! { response: { /// The session identifier given by the identity server. - pub sid: String, + pub sid: SessionIdBox, /// URL to submit validation token to. If omitted, verification happens without client. /// @@ -57,14 +58,14 @@ ruma_api! { impl<'a> Request<'a> { /// Creates a new `Request` with the given client secret, email address and send-attempt /// counter. - pub fn new(client_secret: &'a str, email: &'a str, send_attempt: UInt) -> Self { + pub fn new(client_secret: &'a ClientSecret, email: &'a str, send_attempt: UInt) -> Self { Self { client_secret, email, send_attempt, next_link: None, identity_server_info: None } } } impl Response { /// Creates a new `Response` with the given session identifier. - pub fn new(sid: String) -> Self { + pub fn new(sid: SessionIdBox) -> Self { Self { sid, submit_url: None } } } diff --git a/crates/ruma-client-api/src/r0/account/request_registration_token_via_msisdn.rs b/crates/ruma-client-api/src/r0/account/request_registration_token_via_msisdn.rs index 26e2b966..3d9e7387 100644 --- a/crates/ruma-client-api/src/r0/account/request_registration_token_via_msisdn.rs +++ b/crates/ruma-client-api/src/r0/account/request_registration_token_via_msisdn.rs @@ -2,6 +2,7 @@ use js_int::UInt; use ruma_api::ruma_api; +use ruma_identifiers::{ClientSecret, SessionIdBox}; use super::{IdentityServerInfo, IncomingIdentityServerInfo}; @@ -17,7 +18,7 @@ ruma_api! { request: { /// Client-generated secret string used to protect this session. - pub client_secret: &'a str, + pub client_secret: &'a ClientSecret, /// Two-letter ISO 3166 country code for the phone number. pub country: &'a str, @@ -40,7 +41,7 @@ ruma_api! { response: { /// The session identifier given by the identity server. - pub sid: String, + pub sid: SessionIdBox, /// URL to submit validation token to. If omitted, verification happens without client. /// @@ -61,7 +62,7 @@ impl<'a> Request<'a> { /// Creates a new `Request` with the given client secret, country code, phone number and /// send-attempt counter. pub fn new( - client_secret: &'a str, + client_secret: &'a ClientSecret, country: &'a str, phone_number: &'a str, send_attempt: UInt, @@ -79,7 +80,7 @@ impl<'a> Request<'a> { impl Response { /// Creates a new `Response` with the given session identifier. - pub fn new(sid: String) -> Self { + pub fn new(sid: SessionIdBox) -> Self { Self { sid, submit_url: None } } } diff --git a/crates/ruma-client-api/src/r0/contact/request_contact_verification_token.rs b/crates/ruma-client-api/src/r0/contact/request_contact_verification_token.rs index b9ee0a69..58768329 100644 --- a/crates/ruma-client-api/src/r0/contact/request_contact_verification_token.rs +++ b/crates/ruma-client-api/src/r0/contact/request_contact_verification_token.rs @@ -2,6 +2,7 @@ use js_int::UInt; use ruma_api::ruma_api; +use ruma_identifiers::ClientSecret; ruma_api! { metadata: { @@ -15,7 +16,7 @@ ruma_api! { request: { /// Client-generated secret string used to protect this session. - pub client_secret: &'a str, + pub client_secret: &'a ClientSecret, /// The email address. pub email: &'a str, @@ -49,7 +50,7 @@ ruma_api! { impl<'a> Request<'a> { /// Creates a new `Request` with the given client secret, email and send-attempt counter. - pub fn new(client_secret: &'a str, email: &'a str, send_attempt: UInt) -> Self { + pub fn new(client_secret: &'a ClientSecret, email: &'a str, send_attempt: UInt) -> Self { Self { client_secret, email, diff --git a/crates/ruma-identifiers-validation/src/client_secret.rs b/crates/ruma-identifiers-validation/src/client_secret.rs new file mode 100644 index 00000000..6f3a9fb1 --- /dev/null +++ b/crates/ruma-identifiers-validation/src/client_secret.rs @@ -0,0 +1,13 @@ +use crate::Error; + +pub fn validate(s: &str) -> Result<(), Error> { + if s.len() > 255 { + return Err(Error::MaximumLengthExceeded); + } else if !s.chars().all(|c| c.is_alphanumeric() || ".=_-".contains(c)) { + return Err(Error::InvalidCharacters); + } else if s.is_empty() { + return Err(Error::EmptyClientSecret); + } + + Ok(()) +} diff --git a/crates/ruma-identifiers-validation/src/error.rs b/crates/ruma-identifiers-validation/src/error.rs index a6a2801f..ec7ec75f 100644 --- a/crates/ruma-identifiers-validation/src/error.rs +++ b/crates/ruma-identifiers-validation/src/error.rs @@ -5,6 +5,9 @@ use std::fmt; /// An error encountered when trying to parse an invalid ID string. #[derive(Copy, Clone, Debug, Eq, Hash, PartialEq)] pub enum Error { + /// The client secret is empty. + EmptyClientSecret, + /// The room version ID is empty. EmptyRoomVersionId, @@ -39,6 +42,7 @@ pub enum Error { impl fmt::Display for Error { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { let message = match self { + Error::EmptyClientSecret => "client secret is empty", Error::EmptyRoomVersionId => "room version ID is empty", Error::InvalidCharacters => "localpart contains invalid characters", Error::InvalidKeyAlgorithm => "invalid key algorithm specified", diff --git a/crates/ruma-identifiers-validation/src/lib.rs b/crates/ruma-identifiers-validation/src/lib.rs index 81350f8f..553426a5 100644 --- a/crates/ruma-identifiers-validation/src/lib.rs +++ b/crates/ruma-identifiers-validation/src/lib.rs @@ -1,6 +1,7 @@ #![doc(html_favicon_url = "https://www.ruma.io/favicon.ico")] #![doc(html_logo_url = "https://www.ruma.io/images/logo.png")] +pub mod client_secret; pub mod device_key_id; pub mod error; pub mod event_id; @@ -11,6 +12,7 @@ pub mod room_id; pub mod room_id_or_alias_id; pub mod room_version_id; pub mod server_name; +pub mod session_id; pub mod user_id; use std::num::NonZeroU8; diff --git a/crates/ruma-identifiers-validation/src/session_id.rs b/crates/ruma-identifiers-validation/src/session_id.rs new file mode 100644 index 00000000..6f3a9fb1 --- /dev/null +++ b/crates/ruma-identifiers-validation/src/session_id.rs @@ -0,0 +1,13 @@ +use crate::Error; + +pub fn validate(s: &str) -> Result<(), Error> { + if s.len() > 255 { + return Err(Error::MaximumLengthExceeded); + } else if !s.chars().all(|c| c.is_alphanumeric() || ".=_-".contains(c)) { + return Err(Error::InvalidCharacters); + } else if s.is_empty() { + return Err(Error::EmptyClientSecret); + } + + Ok(()) +} diff --git a/crates/ruma-identifiers/src/client_secret.rs b/crates/ruma-identifiers/src/client_secret.rs new file mode 100644 index 00000000..f81c0ee9 --- /dev/null +++ b/crates/ruma-identifiers/src/client_secret.rs @@ -0,0 +1,23 @@ +//! Client secret identifier. + +use ruma_identifiers_validation::client_secret::validate; + +opaque_identifier_validated! { + /// A client secret. + /// + /// Client secrets in Matrix are opaque character sequences of `[0-9a-zA-Z.=_-]`. Their length must + /// must not exceed 255 characters. + pub type ClientSecret [ validate ]; +} + +#[cfg(test)] +mod tests { + use std::convert::TryFrom; + + use super::ClientSecret; + + #[test] + fn valid_secret() { + assert!(<&ClientSecret>::try_from("this_=_a_valid_secret_1337").is_ok()) + } +} diff --git a/crates/ruma-identifiers/src/lib.rs b/crates/ruma-identifiers/src/lib.rs index c4b40eef..fdc68547 100644 --- a/crates/ruma-identifiers/src/lib.rs +++ b/crates/ruma-identifiers/src/lib.rs @@ -20,6 +20,7 @@ use serde::de::{self, Deserializer, Unexpected}; #[doc(inline)] pub use crate::{ + client_secret::{ClientSecret, ClientSecretBox}, crypto_algorithms::{DeviceKeyAlgorithm, EventEncryptionAlgorithm, SigningKeyAlgorithm}, device_key_id::DeviceKeyId, event_id::EventId, @@ -31,6 +32,7 @@ pub use crate::{ room_id_or_room_alias_id::RoomIdOrAliasId, room_version_id::RoomVersionId, server_name::{ServerName, ServerNameBox}, + session_id::{SessionId, SessionIdBox}, signatures::{DeviceSignatures, EntitySignatures, ServerSignatures, Signatures}, user_id::UserId, }; @@ -42,6 +44,7 @@ mod macros; pub mod user_id; +mod client_secret; mod crypto_algorithms; mod device_key_id; mod event_id; @@ -53,6 +56,7 @@ mod room_id; mod room_id_or_room_alias_id; mod room_version_id; mod server_name; +mod session_id; mod signatures; /// Check whether a given string is a valid server name according to [the specification][]. diff --git a/crates/ruma-identifiers/src/macros.rs b/crates/ruma-identifiers/src/macros.rs index d3c27eb6..fd353ecf 100644 --- a/crates/ruma-identifiers/src/macros.rs +++ b/crates/ruma-identifiers/src/macros.rs @@ -288,3 +288,172 @@ macro_rules! opaque_identifier { partial_eq_string!(Box<$id>); }; } + +macro_rules! opaque_identifier_validated { + ( + $( #[doc = $docs:literal] )* + $vis:vis type $id:ident [ $validate_id:ident ]; + ) => { + $( #[doc = $docs] )* + #[repr(transparent)] + #[derive(PartialEq, Eq, PartialOrd, Ord, Hash)] + #[cfg_attr(feature = "serde", derive(serde::Serialize), serde(transparent, crate = "serde"))] + pub struct $id(str); + + paste::paste! { + doc_concat! { + #[doc = concat!("An owned [", stringify!($id), "].")] + pub type [<$id Box>] = Box<$id>; + } + } + + impl $id { + #[allow(clippy::transmute_ptr_to_ptr)] + fn from_borrowed(s: &str) -> &Self { + unsafe { std::mem::transmute(s) } + } + + pub(super) fn from_owned(s: Box) -> Box { + unsafe { Box::from_raw(Box::into_raw(s) as _) } + } + + fn into_owned(self: Box) -> Box { + unsafe { Box::from_raw(Box::into_raw(self) as _) } + } + + doc_concat! { + #[doc = concat!("Creates a string slice from this `", stringify!($id), "`.")] + pub fn as_str(&self) -> &str { + &self.0 + } + } + + doc_concat! { + #[doc = concat!("Creates a byte slice from this `", stringify!($id), "`.")] + pub fn as_bytes(&self) -> &[u8] { + self.0.as_bytes() + } + } + } + + impl std::fmt::Debug for $id { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + f.write_str(self.as_ref()) + } + } + + impl Clone for Box<$id> { + fn clone(&self) -> Self { + (**self).to_owned() + } + } + + impl ToOwned for $id { + type Owned = Box<$id>; + + fn to_owned(&self) -> Self::Owned { + Self::from_owned(self.0.into()) + } + } + + impl From<&$id> for Box<$id> { + fn from(id: &$id) -> Self { + id.to_owned() + } + } + + impl AsRef for $id { + fn as_ref(&self) -> &str { + self.as_str() + } + } + + impl AsRef for Box<$id> { + fn as_ref(&self) -> &str { + self.as_str() + } + } + + impl From<&$id> for std::rc::Rc<$id> { + fn from(s: &$id) -> std::rc::Rc<$id> { + let rc = std::rc::Rc::::from(s.as_str()); + unsafe { std::rc::Rc::from_raw(std::rc::Rc::into_raw(rc) as *const $id) } + } + } + + impl From<&$id> for std::sync::Arc<$id> { + fn from(s: &$id) -> std::sync::Arc<$id> { + let arc = std::sync::Arc::::from(s.as_str()); + unsafe { std::sync::Arc::from_raw(std::sync::Arc::into_raw(arc) as *const $id) } + } + } + + + + impl From> for String { + fn from(id: Box<$id>) -> Self { + id.into_owned().into() + } + } + + #[cfg(feature = "serde")] + impl<'de> serde::Deserialize<'de> for Box<$id> { + fn deserialize(deserializer: D) -> Result + where + D: serde::Deserializer<'de>, + { + Box::::deserialize(deserializer).map($id::from_owned) + } + } + + fn try_from(s: S) -> Result, crate::Error> + where + S: AsRef + Into>, + { + $validate_id(s.as_ref())?; + Ok($id::from_owned(s.into())) + } + + impl<'a> std::convert::TryFrom<&'a str> for &'a $id { + type Error = crate::Error; + + fn try_from(s: &'a str) -> Result { + $validate_id(s)?; + Ok($id::from_borrowed(s)) + } + } + + impl std::str::FromStr for Box<$id> { + type Err = crate::Error; + + fn from_str(s: &str) -> Result { + try_from(s) + } + } + + impl std::convert::TryFrom<&str> for Box<$id> { + type Error = crate::Error; + + fn try_from(s: &str) -> Result { + try_from(s) + } + } + + impl std::convert::TryFrom for Box<$id> { + type Error = crate::Error; + + fn try_from(s: String) -> Result { + try_from(s) + } + } + + impl std::fmt::Display for $id { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + write!(f, "{}", self.as_str()) + } + } + + partial_eq_string!($id); + partial_eq_string!(Box<$id>); + } +} diff --git a/crates/ruma-identifiers/src/server_name.rs b/crates/ruma-identifiers/src/server_name.rs index 2cf0ea94..d47353b1 100644 --- a/crates/ruma-identifiers/src/server_name.rs +++ b/crates/ruma-identifiers/src/server_name.rs @@ -1,159 +1,12 @@ //! Matrix-spec compliant server names. -use std::{convert::TryFrom, fmt, mem, rc::Rc, str::FromStr, sync::Arc}; use ruma_identifiers_validation::server_name::validate; -/// A Matrix-spec compliant server name. -#[repr(transparent)] -#[derive(PartialEq, Eq, PartialOrd, Ord, Hash)] -#[cfg_attr(feature = "serde", derive(serde::Serialize), serde(transparent, crate = "serde"))] -pub struct ServerName(str); - -/// An owned server name. -pub type ServerNameBox = Box; - -impl ServerName { - fn from_borrowed(s: &str) -> &Self { - unsafe { mem::transmute(s) } - } - - fn from_owned(s: Box) -> Box { - unsafe { Box::from_raw(Box::into_raw(s) as _) } - } - - fn into_owned(self: Box) -> Box { - unsafe { Box::from_raw(Box::into_raw(self) as _) } - } - - /// Creates a string slice from this `ServerName`. - pub fn as_str(&self) -> &str { - &self.0 - } - - /// Creates a byte slice from this `ServerName`. - pub fn as_bytes(&self) -> &[u8] { - self.0.as_bytes() - } +opaque_identifier_validated! { + /// A Matrix-spec compliant server name. + pub type ServerName [ validate ]; } -impl fmt::Debug for ServerName { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - f.write_str(self.as_str()) - } -} - -impl Clone for Box { - fn clone(&self) -> Self { - (**self).to_owned() - } -} - -impl ToOwned for ServerName { - type Owned = Box; - - fn to_owned(&self) -> Self::Owned { - Self::from_owned(self.0.into()) - } -} - -impl From<&ServerName> for Box { - fn from(s: &ServerName) -> Self { - s.to_owned() - } -} - -impl From<&ServerName> for Rc { - fn from(s: &ServerName) -> Self { - let rc = Rc::::from(s.as_str()); - unsafe { Rc::from_raw(Rc::into_raw(rc) as *const ServerName) } - } -} - -impl From<&ServerName> for Arc { - fn from(s: &ServerName) -> Self { - let arc = Arc::::from(s.as_str()); - unsafe { Arc::from_raw(Arc::into_raw(arc) as *const ServerName) } - } -} - -fn try_from(server_name: S) -> Result, crate::Error> -where - S: AsRef + Into>, -{ - validate(server_name.as_ref())?; - Ok(ServerName::from_owned(server_name.into())) -} - -impl AsRef for ServerName { - fn as_ref(&self) -> &str { - self.as_str() - } -} - -impl AsRef for Box { - fn as_ref(&self) -> &str { - self.as_str() - } -} - -impl From> for String { - fn from(s: Box) -> Self { - s.into_owned().into() - } -} - -impl<'a> TryFrom<&'a str> for &'a ServerName { - type Error = crate::Error; - - fn try_from(server_name: &'a str) -> Result { - validate(server_name)?; - Ok(ServerName::from_borrowed(server_name)) - } -} - -impl FromStr for Box { - type Err = crate::Error; - - fn from_str(s: &str) -> Result { - try_from(s) - } -} - -impl TryFrom<&str> for Box { - type Error = crate::Error; - - fn try_from(s: &str) -> Result { - try_from(s) - } -} - -impl TryFrom for Box { - type Error = crate::Error; - - fn try_from(s: String) -> Result { - try_from(s) - } -} - -impl fmt::Display for ServerName { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - write!(f, "{}", self.as_str()) - } -} - -#[cfg(feature = "serde")] -impl<'de> serde::Deserialize<'de> for Box { - fn deserialize(deserializer: D) -> Result - where - D: serde::Deserializer<'de>, - { - crate::deserialize_id(deserializer, "An IP address or hostname") - } -} - -partial_eq_string!(ServerName); -partial_eq_string!(Box); - #[cfg(test)] mod tests { use std::convert::TryFrom; diff --git a/crates/ruma-identifiers/src/session_id.rs b/crates/ruma-identifiers/src/session_id.rs new file mode 100644 index 00000000..f8130f9c --- /dev/null +++ b/crates/ruma-identifiers/src/session_id.rs @@ -0,0 +1,11 @@ +//! Matrix session ID. + +use ruma_identifiers_validation::session_id::validate; + +opaque_identifier_validated! { + /// A session ID. + /// + /// Session IDs in Matrix are opaque character sequences of `[0-9a-zA-Z.=_-]`. Their length must + /// must not exceed 255 characters. + pub type SessionId [ validate ]; +} diff --git a/crates/ruma-identity-service-api/src/association/bind_3pid/v2.rs b/crates/ruma-identity-service-api/src/association/bind_3pid/v2.rs index f26413d1..d9417e73 100644 --- a/crates/ruma-identity-service-api/src/association/bind_3pid/v2.rs +++ b/crates/ruma-identity-service-api/src/association/bind_3pid/v2.rs @@ -2,7 +2,7 @@ use ruma_api::ruma_api; use ruma_common::{thirdparty::Medium, MilliSecondsSinceUnixEpoch}; -use ruma_identifiers::{ServerSignatures, UserId}; +use ruma_identifiers::{ClientSecret, ServerSignatures, SessionId, UserId}; ruma_api! { metadata: { @@ -16,10 +16,10 @@ ruma_api! { request: { /// The session ID generated by the `requestToken` call. - pub sid: &'a str, + pub sid: &'a SessionId, /// The client secret passed to the `requestToken` call. - pub client_secret: &'a str, + pub client_secret: &'a ClientSecret, /// The Matrix user ID to associate with the 3PIDs. pub mxid: &'a UserId, @@ -52,7 +52,7 @@ ruma_api! { impl<'a> Request<'a> { /// Creates a `Request` with the given session ID, client secret and Matrix user ID. - pub fn new(sid: &'a str, client_secret: &'a str, mxid: &'a UserId) -> Self { + pub fn new(sid: &'a SessionId, client_secret: &'a ClientSecret, mxid: &'a UserId) -> Self { Self { sid, client_secret, mxid } } } diff --git a/crates/ruma-identity-service-api/src/association/check_3pid_validity/v2.rs b/crates/ruma-identity-service-api/src/association/check_3pid_validity/v2.rs index 2bf435e3..e49eeebe 100644 --- a/crates/ruma-identity-service-api/src/association/check_3pid_validity/v2.rs +++ b/crates/ruma-identity-service-api/src/association/check_3pid_validity/v2.rs @@ -3,6 +3,7 @@ use js_int::UInt; use ruma_api::ruma_api; use ruma_common::thirdparty::Medium; +use ruma_identifiers::{ClientSecret, SessionId}; ruma_api! { metadata: { @@ -17,11 +18,11 @@ ruma_api! { request: { /// The Session ID generated by the `requestToken` call. #[ruma_api(query)] - pub sid: &'a str, + pub sid: &'a SessionId, /// The client secret passed to the `requestToken` call. #[ruma_api(query)] - pub client_secret: &'a str, + pub client_secret: &'a ClientSecret, } response: { @@ -38,7 +39,7 @@ ruma_api! { impl<'a> Request<'a> { /// Creates a `Request` with the given Session ID and client secret. - pub fn new(sid: &'a str, client_secret: &'a str) -> Self { + pub fn new(sid: &'a SessionId, client_secret: &'a ClientSecret) -> Self { Self { sid, client_secret } } } diff --git a/crates/ruma-identity-service-api/src/association/email/create_email_validation_session/v2.rs b/crates/ruma-identity-service-api/src/association/email/create_email_validation_session/v2.rs index b4c84b42..bcbd14e9 100644 --- a/crates/ruma-identity-service-api/src/association/email/create_email_validation_session/v2.rs +++ b/crates/ruma-identity-service-api/src/association/email/create_email_validation_session/v2.rs @@ -2,6 +2,7 @@ use js_int::UInt; use ruma_api::ruma_api; +use ruma_identifiers::{ClientSecret, SessionIdBox}; ruma_api! { metadata: { @@ -15,7 +16,7 @@ ruma_api! { request: { /// A unique string generated by the client, and used to identify the validation attempt. - pub client_secret: &'a str, + pub client_secret: &'a ClientSecret, /// The email address to validate. pub email: &'a str, @@ -32,7 +33,7 @@ ruma_api! { response: { /// The session ID. Session IDs are opaque strings generated by the identity server. - pub sid: String, + pub sid: SessionIdBox, } } @@ -40,7 +41,7 @@ impl<'a> Request<'a> { /// Create a new `Request` with the given client secret, email ID, `send_attempt` number, and /// the link to redirect to after validation. pub fn new( - client_secret: &'a str, + client_secret: &'a ClientSecret, email: &'a str, send_attempt: UInt, next_link: Option<&'a str>, @@ -51,7 +52,7 @@ impl<'a> Request<'a> { impl Response { /// Create a new `Response` with the given session ID. - pub fn new(sid: String) -> Self { + pub fn new(sid: SessionIdBox) -> Self { Self { sid } } } diff --git a/crates/ruma-identity-service-api/src/association/email/validate_email/v2.rs b/crates/ruma-identity-service-api/src/association/email/validate_email/v2.rs index a95e4c92..7e3163b1 100644 --- a/crates/ruma-identity-service-api/src/association/email/validate_email/v2.rs +++ b/crates/ruma-identity-service-api/src/association/email/validate_email/v2.rs @@ -1,6 +1,7 @@ //! [POST /_matrix/identity/v2/validate/email/submitToken](https://matrix.org/docs/spec/identity_service/r0.3.0#post-matrix-identity-v2-validate-email-submittoken) use ruma_api::ruma_api; +use ruma_identifiers::{ClientSecret, SessionId}; ruma_api! { metadata: { @@ -14,10 +15,10 @@ ruma_api! { request: { /// The session ID, generated by the `requestToken` call. - pub sid: &'a str, + pub sid: &'a SessionId, /// The client secret that was supplied to the `requestToken` call. - pub client_secret: &'a str, + pub client_secret: &'a ClientSecret, /// The token generated by the `requestToken` call and emailed to the user. pub token: &'a str, @@ -31,7 +32,7 @@ ruma_api! { impl<'a> Request<'a> { /// Create a new `Request` with the given session ID, client secret and token. - pub fn new(sid: &'a str, client_secret: &'a str, token: &'a str) -> Self { + pub fn new(sid: &'a SessionId, client_secret: &'a ClientSecret, token: &'a str) -> Self { Self { sid, client_secret, token } } } diff --git a/crates/ruma-identity-service-api/src/association/email/validate_email_by_end_user/v2.rs b/crates/ruma-identity-service-api/src/association/email/validate_email_by_end_user/v2.rs index 1d083214..58278ac5 100644 --- a/crates/ruma-identity-service-api/src/association/email/validate_email_by_end_user/v2.rs +++ b/crates/ruma-identity-service-api/src/association/email/validate_email_by_end_user/v2.rs @@ -1,6 +1,7 @@ //! [GET /_matrix/identity/v2/validate/email/submitToken](https://matrix.org/docs/spec/identity_service/r0.3.0#get-matrix-identity-v2-validate-email-submittoken) use ruma_api::ruma_api; +use ruma_identifiers::{ClientSecret, SessionId}; ruma_api! { metadata: { @@ -15,11 +16,11 @@ ruma_api! { request: { /// The session ID, generated by the `requestToken` call. #[ruma_api(query)] - pub sid: &'a str, + pub sid: &'a SessionId, /// The client secret that was supplied to the `requestToken` call. #[ruma_api(query)] - pub client_secret: &'a str, + pub client_secret: &'a ClientSecret, /// The token generated by the `requestToken` call and emailed to the user. #[ruma_api(query)] @@ -32,7 +33,7 @@ ruma_api! { impl<'a> Request<'a> { /// Create a new `Request` with the given session ID, client secret and token. - pub fn new(sid: &'a str, client_secret: &'a str, token: &'a str) -> Self { + pub fn new(sid: &'a SessionId, client_secret: &'a ClientSecret, token: &'a str) -> Self { Self { sid, client_secret, token } } } diff --git a/crates/ruma-identity-service-api/src/association/msisdn/create_msisdn_validation_session/v2.rs b/crates/ruma-identity-service-api/src/association/msisdn/create_msisdn_validation_session/v2.rs index a2158eb9..42c0a317 100644 --- a/crates/ruma-identity-service-api/src/association/msisdn/create_msisdn_validation_session/v2.rs +++ b/crates/ruma-identity-service-api/src/association/msisdn/create_msisdn_validation_session/v2.rs @@ -2,6 +2,7 @@ use js_int::UInt; use ruma_api::ruma_api; +use ruma_identifiers::{ClientSecret, SessionIdBox}; ruma_api! { metadata: { @@ -15,7 +16,7 @@ ruma_api! { request: { /// A unique string generated by the client, and used to identify the validation attempt. - pub client_secret: &'a str, + pub client_secret: &'a ClientSecret, /// The two-letter uppercase ISO-3166-1 alpha-2 country code that the number in /// `phone_number` should be parsed as if it were dialled from. @@ -37,7 +38,7 @@ ruma_api! { response: { /// The session ID. Session IDs are opaque strings generated by the identity server. - pub sid: String, + pub sid: SessionIdBox, } } @@ -45,7 +46,7 @@ impl<'a> Request<'a> { /// Create a new `Request` with the given client secret, country code, phone number, the /// `send_attempt` number and the next link to go to after validation. pub fn new( - client_secret: &'a str, + client_secret: &'a ClientSecret, country: &'a str, phone_number: &'a str, send_attempt: UInt, @@ -57,7 +58,7 @@ impl<'a> Request<'a> { impl Response { /// Create a new `Response` with the given session ID. - pub fn new(sid: String) -> Self { + pub fn new(sid: SessionIdBox) -> Self { Self { sid } } } diff --git a/crates/ruma-identity-service-api/src/association/msisdn/validate_msisdn/v2.rs b/crates/ruma-identity-service-api/src/association/msisdn/validate_msisdn/v2.rs index c410fc66..97beafa4 100644 --- a/crates/ruma-identity-service-api/src/association/msisdn/validate_msisdn/v2.rs +++ b/crates/ruma-identity-service-api/src/association/msisdn/validate_msisdn/v2.rs @@ -1,6 +1,7 @@ //! [POST /_matrix/identity/v2/validate/msisdn/submitToken](https://matrix.org/docs/spec/identity_service/r0.3.0#post-matrix-identity-v2-validate-msisdn-submittoken) use ruma_api::ruma_api; +use ruma_identifiers::{ClientSecret, SessionId}; ruma_api! { metadata: { @@ -14,10 +15,10 @@ ruma_api! { request: { /// The session ID, generated by the `requestToken` call. - pub sid: &'a str, + pub sid: &'a SessionId, /// The client secret that was supplied to the `requestToken` call. - pub client_secret: &'a str, + pub client_secret: &'a ClientSecret, /// The token generated by the `requestToken` call and sent to the user. pub token: &'a str, @@ -31,7 +32,7 @@ ruma_api! { impl<'a> Request<'a> { /// Create a new `Request` with the given session ID, client secret and token. - pub fn new(sid: &'a str, client_secret: &'a str, token: &'a str) -> Self { + pub fn new(sid: &'a SessionId, client_secret: &'a ClientSecret, token: &'a str) -> Self { Self { sid, client_secret, token } } } diff --git a/crates/ruma-identity-service-api/src/association/msisdn/validate_msisdn_by_phone_number/v2.rs b/crates/ruma-identity-service-api/src/association/msisdn/validate_msisdn_by_phone_number/v2.rs index 177cde84..3dbeabfc 100644 --- a/crates/ruma-identity-service-api/src/association/msisdn/validate_msisdn_by_phone_number/v2.rs +++ b/crates/ruma-identity-service-api/src/association/msisdn/validate_msisdn_by_phone_number/v2.rs @@ -1,6 +1,7 @@ //! [GET /_matrix/identity/v2/validate/msisdn/submitToken](https://matrix.org/docs/spec/identity_service/r0.3.0#get-matrix-identity-v2-validate-msisdn-submittoken) use ruma_api::ruma_api; +use ruma_identifiers::{ClientSecret, SessionId}; ruma_api! { metadata: { @@ -15,11 +16,11 @@ ruma_api! { request: { /// The session ID, generated by the `requestToken` call. #[ruma_api(query)] - pub sid: &'a str, + pub sid: &'a SessionId, /// The client secret that was supplied to the `requestToken` call. #[ruma_api(query)] - pub client_secret: &'a str, + pub client_secret: &'a ClientSecret, /// The token generated by the `requestToken` call and sent to the user. #[ruma_api(query)] @@ -32,7 +33,7 @@ ruma_api! { impl<'a> Request<'a> { /// Create a new `Request` with the given session ID, client secret and token. - pub fn new(sid: &'a str, client_secret: &'a str, token: &'a str) -> Self { + pub fn new(sid: &'a SessionId, client_secret: &'a ClientSecret, token: &'a str) -> Self { Self { sid, client_secret, token } } } diff --git a/crates/ruma-serde-macros/src/outgoing.rs b/crates/ruma-serde-macros/src/outgoing.rs index d6f27a0b..d219323b 100644 --- a/crates/ruma-serde-macros/src/outgoing.rs +++ b/crates/ruma-serde-macros/src/outgoing.rs @@ -258,8 +258,10 @@ fn strip_lifetimes(field_type: &mut Type) -> bool { if last_seg.ident == "str" { // &str -> String Some(parse_quote! { ::std::string::String }) - } else if last_seg.ident == "DeviceId" + } else if last_seg.ident == "ClientSecret" + || last_seg.ident == "DeviceId" || last_seg.ident == "ServerName" + || last_seg.ident == "SessionId" || last_seg.ident == "RawJsonValue" { // The identifiers that need to be boxed `Box` since they are DST's. diff --git a/crates/ruma/src/lib.rs b/crates/ruma/src/lib.rs index 4cc095c1..912a976a 100644 --- a/crates/ruma/src/lib.rs +++ b/crates/ruma/src/lib.rs @@ -84,11 +84,12 @@ pub use ruma_serde::Outgoing; #[allow(deprecated)] // Allow re-export of deprecated items pub use ruma_identifiers::{ device_id, device_key_id, event_id, mxc_uri, room_alias_id, room_id, room_version_id, - server_key_id, server_name, server_signing_key_id, user_id, DeviceId, DeviceIdBox, - DeviceKeyAlgorithm, DeviceKeyId, DeviceSignatures, DeviceSigningKeyId, EntitySignatures, - EventEncryptionAlgorithm, EventId, KeyId, KeyName, KeyNameBox, MxcUri, RoomAliasId, RoomId, - RoomIdOrAliasId, RoomVersionId, ServerName, ServerNameBox, ServerSignatures, - ServerSigningKeyId, Signatures, SigningKeyAlgorithm, UserId, + server_key_id, server_name, server_signing_key_id, user_id, ClientSecret, ClientSecretBox, + DeviceId, DeviceIdBox, DeviceKeyAlgorithm, DeviceKeyId, DeviceSignatures, DeviceSigningKeyId, + EntitySignatures, EventEncryptionAlgorithm, EventId, KeyId, KeyName, KeyNameBox, MxcUri, + RoomAliasId, RoomId, RoomIdOrAliasId, RoomVersionId, ServerName, ServerNameBox, + ServerSignatures, ServerSigningKeyId, SessionId, SessionIdBox, Signatures, SigningKeyAlgorithm, + UserId, }; #[cfg(feature = "client")]