domain: introduce UserId to make uid case insensitive

Note that if there was a non-lowercase user already in the DB, it cannot
be found again. To fix this, run in the DB:

sqlite> UPDATE users SET user_id = LOWER(user_id);
This commit is contained in:
Valentin Tolmer
2022-03-26 18:00:37 +01:00
committed by nitnelave
parent 26cedcb621
commit ca19e61f50
13 changed files with 299 additions and 181 deletions

View File

@@ -3,10 +3,41 @@ use async_trait::async_trait;
use serde::{Deserialize, Serialize};
use std::collections::HashSet;
#[derive(PartialEq, Eq, Clone, Debug, Default, Serialize, Deserialize)]
#[cfg_attr(not(target_arch = "wasm32"), derive(sqlx::FromRow))]
#[serde(from = "String")]
pub struct UserId(String);
impl UserId {
pub fn new(user_id: &str) -> Self {
Self(user_id.to_lowercase())
}
pub fn as_str(&self) -> &str {
self.0.as_str()
}
pub fn into_string(self) -> String {
self.0
}
}
impl std::fmt::Display for UserId {
fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
write!(f, "{}", self.0)
}
}
impl From<String> for UserId {
fn from(s: String) -> Self {
Self::new(&s)
}
}
#[derive(PartialEq, Eq, Debug, Serialize, Deserialize)]
#[cfg_attr(not(target_arch = "wasm32"), derive(sqlx::FromRow))]
pub struct User {
pub user_id: String,
pub user_id: UserId,
pub email: String,
pub display_name: String,
pub first_name: String,
@@ -19,7 +50,7 @@ impl Default for User {
fn default() -> Self {
use chrono::TimeZone;
User {
user_id: String::new(),
user_id: UserId::default(),
email: String::new(),
display_name: String::new(),
first_name: String::new(),
@@ -33,12 +64,12 @@ impl Default for User {
pub struct Group {
pub id: GroupId,
pub display_name: String,
pub users: Vec<String>,
pub users: Vec<UserId>,
}
#[derive(PartialEq, Eq, Debug, Serialize, Deserialize, Clone)]
pub struct BindRequest {
pub name: String,
pub name: UserId,
pub password: String,
}
@@ -47,6 +78,7 @@ pub enum UserRequestFilter {
And(Vec<UserRequestFilter>),
Or(Vec<UserRequestFilter>),
Not(Box<UserRequestFilter>),
UserId(UserId),
Equality(String, String),
// Check if a user belongs to a group identified by name.
MemberOf(String),
@@ -62,13 +94,13 @@ pub enum GroupRequestFilter {
DisplayName(String),
GroupId(GroupId),
// Check if the group contains a user identified by uid.
Member(String),
Member(UserId),
}
#[derive(PartialEq, Eq, Debug, Serialize, Deserialize, Clone, Default)]
pub struct CreateUserRequest {
// Same fields as User, but no creation_date, and with password.
pub user_id: String,
pub user_id: UserId,
pub email: String,
pub display_name: Option<String>,
pub first_name: Option<String>,
@@ -78,7 +110,7 @@ pub struct CreateUserRequest {
#[derive(PartialEq, Eq, Debug, Serialize, Deserialize, Clone, Default)]
pub struct UpdateUserRequest {
// Same fields as CreateUserRequest, but no with an extra layer of Option.
pub user_id: String,
pub user_id: UserId,
pub email: Option<String>,
pub display_name: Option<String>,
pub first_name: Option<String>,
@@ -106,17 +138,17 @@ pub struct GroupIdAndName(pub GroupId, pub String);
pub trait BackendHandler: Clone + Send {
async fn list_users(&self, filters: Option<UserRequestFilter>) -> Result<Vec<User>>;
async fn list_groups(&self, filters: Option<GroupRequestFilter>) -> Result<Vec<Group>>;
async fn get_user_details(&self, user_id: &str) -> Result<User>;
async fn get_user_details(&self, user_id: &UserId) -> Result<User>;
async fn get_group_details(&self, group_id: GroupId) -> Result<GroupIdAndName>;
async fn create_user(&self, request: CreateUserRequest) -> Result<()>;
async fn update_user(&self, request: UpdateUserRequest) -> Result<()>;
async fn update_group(&self, request: UpdateGroupRequest) -> Result<()>;
async fn delete_user(&self, user_id: &str) -> Result<()>;
async fn delete_user(&self, user_id: &UserId) -> Result<()>;
async fn create_group(&self, group_name: &str) -> Result<GroupId>;
async fn delete_group(&self, group_id: GroupId) -> Result<()>;
async fn add_user_to_group(&self, user_id: &str, group_id: GroupId) -> Result<()>;
async fn remove_user_from_group(&self, user_id: &str, group_id: GroupId) -> Result<()>;
async fn get_user_groups(&self, user: &str) -> Result<HashSet<GroupIdAndName>>;
async fn add_user_to_group(&self, user_id: &UserId, group_id: GroupId) -> Result<()>;
async fn remove_user_from_group(&self, user_id: &UserId, group_id: GroupId) -> Result<()>;
async fn get_user_groups(&self, user_id: &UserId) -> Result<HashSet<GroupIdAndName>>;
}
#[cfg(test)]
@@ -129,17 +161,17 @@ mockall::mock! {
impl BackendHandler for TestBackendHandler {
async fn list_users(&self, filters: Option<UserRequestFilter>) -> Result<Vec<User>>;
async fn list_groups(&self, filters: Option<GroupRequestFilter>) -> Result<Vec<Group>>;
async fn get_user_details(&self, user_id: &str) -> Result<User>;
async fn get_user_details(&self, user_id: &UserId) -> Result<User>;
async fn get_group_details(&self, group_id: GroupId) -> Result<GroupIdAndName>;
async fn create_user(&self, request: CreateUserRequest) -> Result<()>;
async fn update_user(&self, request: UpdateUserRequest) -> Result<()>;
async fn update_group(&self, request: UpdateGroupRequest) -> Result<()>;
async fn delete_user(&self, user_id: &str) -> Result<()>;
async fn delete_user(&self, user_id: &UserId) -> Result<()>;
async fn create_group(&self, group_name: &str) -> Result<GroupId>;
async fn delete_group(&self, group_id: GroupId) -> Result<()>;
async fn get_user_groups(&self, user: &str) -> Result<HashSet<GroupIdAndName>>;
async fn add_user_to_group(&self, user_id: &str, group_id: GroupId) -> Result<()>;
async fn remove_user_from_group(&self, user_id: &str, group_id: GroupId) -> Result<()>;
async fn get_user_groups(&self, user_id: &UserId) -> Result<HashSet<GroupIdAndName>>;
async fn add_user_to_group(&self, user_id: &UserId, group_id: GroupId) -> Result<()>;
async fn remove_user_from_group(&self, user_id: &UserId, group_id: GroupId) -> Result<()>;
}
#[async_trait]
impl LoginHandler for TestBackendHandler {