Move user ID localpart classification into a public function

This commit is contained in:
Jonas Platte 2020-04-17 13:12:38 +02:00
parent 76a5a487d2
commit 93f75353a7
No known key found for this signature in database
GPG Key ID: 7D261D771D915378
3 changed files with 33 additions and 17 deletions

View File

@ -27,6 +27,9 @@ Improvements:
* Add support for historical uppercase MXIDs
* Made all dependencies optional
* `serde` is the only one that is enabled by default
* The `user_id` module is now public and contains `fn localpart_is_fully_conforming`
* This function can be used to determine whether a user name (the localpart of a user ID) is valid
without actually constructing a full user ID first
# 0.14.1

View File

@ -20,10 +20,9 @@ use std::num::NonZeroU8;
use serde::de::{self, Deserialize as _, Deserializer, Unexpected};
#[doc(inline)]
pub use crate::device_id::DeviceId;
pub use crate::{
error::Error, event_id::EventId, room_alias_id::RoomAliasId, room_id::RoomId,
room_id_or_room_alias_id::RoomIdOrAliasId, room_version_id::RoomVersionId,
device_id::DeviceId, error::Error, event_id::EventId, room_alias_id::RoomAliasId,
room_id::RoomId, room_id_or_room_alias_id::RoomIdOrAliasId, room_version_id::RoomVersionId,
server_name::is_valid_server_name, user_id::UserId,
};
@ -40,7 +39,7 @@ mod room_id;
mod room_id_or_room_alias_id;
mod room_version_id;
mod server_name;
mod user_id;
pub mod user_id;
/// All identifiers must be 255 bytes or less.
const MAX_BYTES: usize = 255;

View File

@ -84,29 +84,43 @@ impl TryFrom<Cow<'_, str>> for UserId {
let colon_idx = parse_id(&user_id, &['@'])?;
let localpart = &user_id[1..colon_idx.get() as usize];
// See https://matrix.org/docs/spec/appendices#user-identifiers
let is_fully_conforming = localpart.bytes().all(|b| match b {
b'0'..=b'9' | b'a'..=b'z' | b'-' | b'.' | b'=' | b'_' | b'/' => true,
_ => false,
});
// If it's not fully conforming, check if it contains characters that are also disallowed
// for historical user IDs. If there are, return an error.
// See https://matrix.org/docs/spec/appendices#historical-user-ids
if !is_fully_conforming && localpart.bytes().any(|b| b < 0x21 || b == b':' || b > 0x7E) {
return Err(Error::InvalidCharacters);
}
let is_historical = localpart_is_fully_comforming(localpart)?;
Ok(Self {
full_id: user_id.into_owned(),
colon_idx,
is_historical: !is_fully_conforming,
is_historical: !is_historical,
})
}
}
common_impls!(UserId, "a Matrix user ID");
/// Check whether the given user id localpart is valid and fully conforming
///
/// Returns an `Err` for invalid user ID localparts, `Ok(false)` for historical user ID localparts
/// and `Ok(true)` for fully conforming user ID localparts.
pub fn localpart_is_fully_comforming(localpart: &str) -> Result<bool, Error> {
if localpart.is_empty() {
return Err(Error::InvalidLocalPart);
}
// See https://matrix.org/docs/spec/appendices#user-identifiers
let is_fully_conforming = localpart.bytes().all(|b| match b {
b'0'..=b'9' | b'a'..=b'z' | b'-' | b'.' | b'=' | b'_' | b'/' => true,
_ => false,
});
// If it's not fully conforming, check if it contains characters that are also disallowed
// for historical user IDs. If there are, return an error.
// See https://matrix.org/docs/spec/appendices#historical-user-ids
if !is_fully_conforming && localpart.bytes().any(|b| b < 0x21 || b == b':' || b > 0x7E) {
Err(Error::InvalidCharacters)
} else {
Ok(is_fully_conforming)
}
}
#[cfg(test)]
mod tests {
use std::convert::TryFrom;