From 01dd745a06b110105d2f0c3e429c2706898a9164 Mon Sep 17 00:00:00 2001 From: Jonas Platte Date: Sun, 21 Apr 2019 01:07:49 +0200 Subject: [PATCH] Update login endpoint to r0.6.0 --- src/r0/session/get_login_types.rs | 14 ++- src/r0/session/login.rs | 184 +++++++++++++++++++++++++---- src/r0/session/login/user_serde.rs | 64 ++++++++++ src/r0/thirdparty.rs | 2 +- 4 files changed, 237 insertions(+), 27 deletions(-) create mode 100644 src/r0/session/login/user_serde.rs diff --git a/src/r0/session/get_login_types.rs b/src/r0/session/get_login_types.rs index 7f0d0d4d..ba891343 100644 --- a/src/r0/session/get_login_types.rs +++ b/src/r0/session/get_login_types.rs @@ -3,8 +3,6 @@ use ruma_api::ruma_api; use serde::{Deserialize, Serialize}; -use super::login::LoginType; - ruma_api! { metadata { description: "Gets the homeserver's supported login types to authenticate users. Clients should pick one of these and supply it as the type when logging in.", @@ -30,3 +28,15 @@ pub struct LoginFlow { #[serde(rename = "type")] pub login_type: LoginType, } + +/// The authentication mechanism. +#[derive(Clone, Copy, Debug, PartialEq, Eq, Deserialize, Serialize)] +#[serde(tag = "type")] +pub enum LoginType { + /// A password is supplied to authenticate. + #[serde(rename = "m.login.password")] + Password, + /// Token-based login. + #[serde(rename = "m.login.token")] + Token, +} diff --git a/src/r0/session/login.rs b/src/r0/session/login.rs index 6e4a4b17..31e2ed99 100644 --- a/src/r0/session/login.rs +++ b/src/r0/session/login.rs @@ -1,9 +1,10 @@ -//! [POST /_matrix/client/r0/login](https://matrix.org/docs/spec/client_server/r0.4.0.html#post-matrix-client-r0-login) +//! [POST /_matrix/client/r0/login](https://matrix.org/docs/spec/client_server/r0.6.0#post-matrix-client-r0-login) -use crate::r0::thirdparty::Medium; use ruma_api::ruma_api; use ruma_identifiers::{DeviceId, UserId}; -use serde::{Deserialize, Serialize}; +use serde::{Deserialize, Deserializer, Serialize, Serializer}; + +use crate::r0::thirdparty::Medium; ruma_api! { metadata { @@ -16,47 +17,182 @@ ruma_api! { } request { - /// The user's password. - pub password: String, - /// When logging in using a third party identifier, the medium of the identifier. - #[serde(skip_serializing_if = "Option::is_none")] - pub medium: Option, + /// Identification information for the user. + #[serde(flatten)] + pub user: UserInfo, /// The authentication mechanism. - #[serde(rename = "type")] - pub login_type: LoginType, - /// The fully qualified user ID or just local part of the user ID. - pub user: String, - /// Third party identifier for the user. - #[serde(skip_serializing_if = "Option::is_none")] - pub address: Option, + #[serde(flatten)] + pub login_info: LoginInfo, /// ID of the client device #[serde(skip_serializing_if = "Option::is_none")] pub device_id: Option, + /// A display name to assign to the newly-created device. Ignored if device_id corresponds + /// to a known device. + #[serde(skip_serializing_if = "Option::is_none")] + pub initial_device_display_name: Option, } response { + /// The fully-qualified Matrix ID that has been registered. + pub user_id: UserId, /// An access token for the account. pub access_token: String, /// The hostname of the homeserver on which the account has been registered. - pub home_server: String, - /// A refresh token may be exchanged for a new access token using the /tokenrefresh API - /// endpoint. + /// + /// Deprecated: Clients should extract the server_name from user_id (by splitting at the + /// first colon) if they require it. #[serde(skip_serializing_if = "Option::is_none")] - pub refresh_token: Option, - /// The fully-qualified Matrix ID that has been registered. - pub user_id: UserId, + pub home_server: Option, /// ID of the logged-in device. /// /// Will be the same as the corresponging parameter in the request, if one was /// specified. pub device_id: String, + /// Client configuration provided by the server. + /// + /// If present, clients SHOULD use the provided object to reconfigure themselves. + pub well_known: Option, } } +/// Identification information for the user. +#[derive(Clone, Debug, PartialEq, Eq)] +pub enum UserInfo { + /// Either a fully qualified Matrix user ID, or just the localpart (as part of the 'identifier' + /// field, recommended since r0.4.0). + MatrixId(String), + /// Third party identifier (as part of the 'identifier' field, recommended since r0.4.0). + ThirdPartyId { + /// Third party identifier for the user. + address: String, + /// The medium of the identifier. + medium: Medium, + }, + /// Same as third-party identification with medium == msisdn, but with a non-canonicalised + /// phone number. + PhoneNumber { + /// The country that the phone number is from. + country: String, + /// The phone number. + phone: String, + }, +} + /// The authentication mechanism. -#[derive(Clone, Copy, Debug, Deserialize, Serialize)] -pub enum LoginType { +#[derive(Clone, Debug, PartialEq, Eq, Deserialize, Serialize)] +#[serde(tag = "type")] +pub enum LoginInfo { /// A password is supplied to authenticate. #[serde(rename = "m.login.password")] - Password, + Password { + /// The password. + password: String, + }, + /// Token-based login. + #[serde(rename = "m.login.token")] + Token { + /// The token. + token: String, + }, +} + +/// Client configuration provided by the server. +#[derive(Clone, Debug, Deserialize, Serialize)] +pub struct DiscoveryInfo { + #[serde(rename = "m.homeserver")] + homeserver: HomeserverInfo, + #[serde(rename = "m.identity_server")] + identity_server: Option, +} + +/// Information about the homeserver to connect to. +#[derive(Clone, Debug, Deserialize, Serialize)] +pub struct HomeserverInfo { + /// The base URL for the homeserver for client-server connections. + base_url: String, +} + +/// Information about the identity server to connect to. +#[derive(Clone, Debug, Deserialize, Serialize)] +pub struct IdentityServerInfo { + /// The base URL for the identity server for client-server connections. + base_url: String, +} + +mod user_serde; + +impl Serialize for UserInfo { + fn serialize(&self, serializer: S) -> Result + where + S: Serializer, + { + user_serde::UserInfo::from(self).serialize(serializer) + } +} + +impl<'de> Deserialize<'de> for UserInfo { + fn deserialize(deserializer: D) -> Result + where + D: Deserializer<'de>, + { + user_serde::UserInfo::deserialize(deserializer).map(Into::into) + } +} + +#[cfg(test)] +mod tests { + use serde_json; + + use super::{LoginInfo, UserInfo}; + + #[test] + fn deserialize_login_type() { + assert_eq!( + serde_json::from_str::( + r#" + { + "type": "m.login.password", + "password": "ilovebananas" + } + "#, + ) + .unwrap(), + LoginInfo::Password { + password: "ilovebananas".into() + } + ); + + assert_eq!( + serde_json::from_str::( + r#" + { + "type": "m.login.token", + "token": "1234567890abcdef" + } + "#, + ) + .unwrap(), + LoginInfo::Token { + token: "1234567890abcdef".into() + } + ); + } + + #[test] + fn deserialize_user() { + assert_eq!( + serde_json::from_str::( + r#" + { + "identifier": { + "type": "m.id.user", + "user": "cheeky_monkey" + } + } + "#, + ) + .unwrap(), + UserInfo::MatrixId("cheeky_monkey".into()) + ); + } } diff --git a/src/r0/session/login/user_serde.rs b/src/r0/session/login/user_serde.rs new file mode 100644 index 00000000..17202ce8 --- /dev/null +++ b/src/r0/session/login/user_serde.rs @@ -0,0 +1,64 @@ +//! Helper module for the Serialize / Deserialize impl's for the User struct +//! in the parent module. + +use serde::{Deserialize, Serialize}; + +use super::Medium; + +// The following three structs could just be used in place of the one in the parent module, but +// that one is arguably much easier to deal with. +#[derive(Clone, Debug, PartialEq, Eq, Deserialize, Serialize)] +pub struct UserInfo<'a> { + #[serde(borrow)] + identifier: UserIdentifier<'a>, +} + +#[derive(Clone, Debug, PartialEq, Eq, Deserialize, Serialize)] +#[serde(tag = "type")] +pub enum UserIdentifier<'a> { + #[serde(rename = "m.id.user")] + MatrixId { user: &'a str }, + #[serde(rename = "m.id.thirdparty")] + ThirdPartyId { medium: Medium, address: &'a str }, + #[serde(rename = "m.id.phone")] + PhoneNumber { country: &'a str, phone: &'a str }, +} + +impl<'a> From<&'a super::UserInfo> for UserInfo<'a> { + fn from(su: &'a super::UserInfo) -> Self { + use super::UserInfo::*; + + match su { + MatrixId(user) => UserInfo { + identifier: UserIdentifier::MatrixId { user }, + }, + ThirdPartyId { address, medium } => UserInfo { + identifier: UserIdentifier::ThirdPartyId { + address, + medium: *medium, + }, + }, + PhoneNumber { country, phone } => UserInfo { + identifier: UserIdentifier::PhoneNumber { country, phone }, + }, + } + } +} + +impl Into for UserInfo<'_> { + fn into(self) -> super::UserInfo { + use super::UserInfo::*; + + match self.identifier { + UserIdentifier::MatrixId { user } => MatrixId(user.to_owned()), + UserIdentifier::ThirdPartyId { address, medium } => ThirdPartyId { + address: address.to_owned(), + medium: medium.to_owned(), + }, + UserIdentifier::PhoneNumber { country, phone } => PhoneNumber { + country: country.to_owned(), + phone: phone.to_owned(), + }, + } + } +} diff --git a/src/r0/thirdparty.rs b/src/r0/thirdparty.rs index 82b1012d..ad0796a9 100644 --- a/src/r0/thirdparty.rs +++ b/src/r0/thirdparty.rs @@ -74,7 +74,7 @@ pub struct User { } /// The medium of a third party identifier. -#[derive(Clone, Copy, Debug, Deserialize, Serialize)] +#[derive(Clone, Copy, Debug, PartialEq, Eq, Deserialize, Serialize)] #[serde(rename_all = "lowercase")] pub enum Medium { /// Email address identifier