From cef5252a606819dfa24f912d40c2356b3eba61e5 Mon Sep 17 00:00:00 2001 From: moovlin Date: Sun, 4 Dec 2022 22:23:50 -0500 Subject: [PATCH] Changed the struct to make things launch. I've done no testing. --- app/src/components/create_user.rs | 2 +- app/src/components/user_details_form.rs | 4 +- schema.graphql | 4 +- server/src/domain/handler.rs | 7 +- server/src/domain/ldap/user.rs | 2 +- server/src/domain/model/users.rs | 4 +- server/src/domain/sql_backend_handler.rs | 4 +- server/src/domain/sql_user_backend_handler.rs | 18 ++--- server/src/domain/types.rs | 80 ++++++++++++++++++- server/src/infra/auth_service.rs | 4 +- server/src/infra/graphql/mutation.rs | 12 +-- server/src/infra/graphql/query.rs | 2 +- server/src/infra/ldap_handler.rs | 21 +++-- server/src/main.rs | 3 +- 14 files changed, 124 insertions(+), 43 deletions(-) diff --git a/app/src/components/create_user.rs b/app/src/components/create_user.rs index b7ece1c..579a706 100644 --- a/app/src/components/create_user.rs +++ b/app/src/components/create_user.rs @@ -86,7 +86,7 @@ impl CommonComponent for CreateUserForm { user: create_user::CreateUserInput { id: model.username, email: model.email, - displayName: to_option(model.display_name), + displayName: model.display_name, firstName: to_option(model.first_name), lastName: to_option(model.last_name), avatar: None, diff --git a/app/src/components/user_details_form.rs b/app/src/components/user_details_form.rs index ca5c1af..0f4e789 100644 --- a/app/src/components/user_details_form.rs +++ b/app/src/components/user_details_form.rs @@ -355,7 +355,7 @@ impl UserDetailsForm { let mut user_input = update_user::UpdateUserInput { id: self.common.user.id.clone(), email: None, - displayName: None, + displayName: self.common.user.id.clone(), firstName: None, lastName: None, avatar: None, @@ -367,7 +367,7 @@ impl UserDetailsForm { user_input.email = Some(email); } if base_user.display_name != model.display_name { - user_input.displayName = Some(model.display_name); + user_input.displayName = model.display_name; } if base_user.first_name != model.first_name { user_input.firstName = Some(model.first_name); diff --git a/schema.graphql b/schema.graphql index 4c88bd7..3eade5b 100644 --- a/schema.graphql +++ b/schema.graphql @@ -57,7 +57,7 @@ type Query { input CreateUserInput { id: String! email: String! - displayName: String + displayName: String! firstName: String lastName: String avatar: String @@ -84,7 +84,7 @@ type Success { input UpdateUserInput { id: String! email: String - displayName: String + displayName: String! firstName: String lastName: String avatar: String diff --git a/server/src/domain/handler.rs b/server/src/domain/handler.rs index ef43e93..7e53c81 100644 --- a/server/src/domain/handler.rs +++ b/server/src/domain/handler.rs @@ -1,7 +1,8 @@ use super::{ error::Result, types::{ - Group, GroupDetails, GroupId, JpegPhoto, User, UserAndGroups, UserColumn, UserId, Uuid, + DisplayName, Group, GroupDetails, GroupId, JpegPhoto, User, UserAndGroups, UserColumn, + UserId, Uuid, }, }; use async_trait::async_trait; @@ -44,7 +45,7 @@ pub struct CreateUserRequest { // Same fields as User, but no creation_date, and with password. pub user_id: UserId, pub email: String, - pub display_name: Option, + pub display_name: DisplayName, pub first_name: Option, pub last_name: Option, pub avatar: Option, @@ -55,7 +56,7 @@ pub struct UpdateUserRequest { // Same fields as CreateUserRequest, but no with an extra layer of Option. pub user_id: UserId, pub email: Option, - pub display_name: Option, + pub display_name: DisplayName, pub first_name: Option, pub last_name: Option, pub avatar: Option, diff --git a/server/src/domain/ldap/user.rs b/server/src/domain/ldap/user.rs index 08f9853..f73908f 100644 --- a/server/src/domain/ldap/user.rs +++ b/server/src/domain/ldap/user.rs @@ -48,7 +48,7 @@ fn get_user_attribute( .into_bytes() }) .collect(), - "cn" | "displayname" => vec![user.display_name.clone()?.into_bytes()], + "cn" | "displayname" => vec![user.display_name.to_string().into_bytes()], "createtimestamp" | "modifytimestamp" => vec![user.creation_date.to_rfc3339().into_bytes()], "1.1" => return None, // We ignore the operational attribute wildcard. diff --git a/server/src/domain/model/users.rs b/server/src/domain/model/users.rs index a9f1b02..64c0f37 100644 --- a/server/src/domain/model/users.rs +++ b/server/src/domain/model/users.rs @@ -3,7 +3,7 @@ use sea_orm::entity::prelude::*; use serde::{Deserialize, Serialize}; -use crate::domain::types::{JpegPhoto, UserId, Uuid}; +use crate::domain::types::{DisplayName, JpegPhoto, UserId, Uuid}; #[derive(Copy, Clone, Default, Debug, DeriveEntity)] pub struct Entity; @@ -14,7 +14,7 @@ pub struct Model { #[sea_orm(primary_key, auto_increment = false)] pub user_id: UserId, pub email: String, - pub display_name: Option, + pub display_name: DisplayName, pub first_name: Option, pub last_name: Option, pub avatar: Option, diff --git a/server/src/domain/sql_backend_handler.rs b/server/src/domain/sql_backend_handler.rs index 86181f7..b0fd0da 100644 --- a/server/src/domain/sql_backend_handler.rs +++ b/server/src/domain/sql_backend_handler.rs @@ -26,7 +26,7 @@ pub mod tests { CreateUserRequest, GroupBackendHandler, UserBackendHandler, UserRequestFilter, }, sql_tables::init_table, - types::{GroupId, UserId}, + types::{DisplayName, GroupId, UserId}, }, infra::configuration::ConfigurationBuilder, }; @@ -86,7 +86,7 @@ pub mod tests { .create_user(CreateUserRequest { user_id: UserId::new(name), email: "bob@bob.bob".to_string(), - display_name: Some("display ".to_string() + name), + display_name: DisplayName::new(("display ".to_owned() + name).as_str()), first_name: Some("first ".to_string() + name), last_name: Some("last ".to_string() + name), ..Default::default() diff --git a/server/src/domain/sql_user_backend_handler.rs b/server/src/domain/sql_user_backend_handler.rs index dc7b99e..1e8a640 100644 --- a/server/src/domain/sql_user_backend_handler.rs +++ b/server/src/domain/sql_user_backend_handler.rs @@ -163,7 +163,7 @@ impl UserBackendHandler for SqlBackendHandler { let new_user = model::users::ActiveModel { user_id: Set(request.user_id), email: Set(request.email), - display_name: to_value(&request.display_name), + display_name: ActiveValue::Set(request.display_name), first_name: to_value(&request.first_name), last_name: to_value(&request.last_name), avatar: request.avatar.into_active_value(), @@ -181,7 +181,7 @@ impl UserBackendHandler for SqlBackendHandler { let update_user = model::users::ActiveModel { user_id: ActiveValue::Set(request.user_id), email: request.email.map(ActiveValue::Set).unwrap_or_default(), - display_name: to_value(&request.display_name), + display_name: ActiveValue::Set(request.display_name), first_name: to_value(&request.first_name), last_name: to_value(&request.last_name), avatar: request.avatar.into_active_value(), @@ -238,7 +238,7 @@ mod tests { use super::*; use crate::domain::{ sql_backend_handler::tests::*, - types::{JpegPhoto, UserColumn}, + types::{DisplayName, JpegPhoto, UserColumn}, }; #[tokio::test] @@ -407,11 +407,7 @@ mod tests { .map(|u| { ( u.user.user_id.to_string(), - u.user - .display_name - .as_deref() - .unwrap_or("") - .to_owned(), + u.user.display_name.to_string(), u.groups .unwrap_or_default() .into_iter() @@ -567,7 +563,7 @@ mod tests { .update_user(UpdateUserRequest { user_id: UserId::new("bob"), email: Some("email".to_string()), - display_name: Some("display_name".to_string()), + display_name: DisplayName::new("display_name"), first_name: Some("first_name".to_string()), last_name: Some("last_name".to_string()), avatar: Some(JpegPhoto::for_tests()), @@ -581,7 +577,7 @@ mod tests { .await .unwrap(); assert_eq!(user.email, "email"); - assert_eq!(user.display_name.unwrap(), "display_name"); + assert_eq!(user.display_name.as_str(), "display_name"); assert_eq!(user.first_name.unwrap(), "first_name"); assert_eq!(user.last_name.unwrap(), "last_name"); assert_eq!(user.avatar, Some(JpegPhoto::for_tests())); @@ -607,7 +603,7 @@ mod tests { .get_user_details(&UserId::new("bob")) .await .unwrap(); - assert_eq!(user.display_name.unwrap(), "display bob"); + assert_eq!(user.display_name.as_str(), "display bob"); assert_eq!(user.first_name.unwrap(), "first_name"); assert_eq!(user.last_name, None); assert_eq!(user.avatar, None); diff --git a/server/src/domain/types.rs b/server/src/domain/types.rs index 76673d8..436079c 100644 --- a/server/src/domain/types.rs +++ b/server/src/domain/types.rs @@ -167,6 +167,82 @@ impl ValueType for UserId { } } +#[derive(PartialEq, Eq, Clone, Debug, Default, Serialize, Deserialize)] +#[serde(from = "String")] +pub struct DisplayName(String); + +impl DisplayName { + pub fn new(display_name: &str) -> Self { + Self(display_name.to_string()) + } + + pub fn as_str(&self) -> &str { + self.0.as_str() + } + + pub fn into_string(self) -> String { + self.0 + } +} + +impl std::fmt::Display for DisplayName { + fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { + write!(f, "{}", self.0) + } +} + +impl From for DisplayName { + fn from(s: String) -> Self { + Self::new(&s) + } +} + +impl From for Value { + fn from(display_name: DisplayName) -> Self { + display_name.into_string().into() + } +} + +impl From<&DisplayName> for Value { + fn from(display_name: &DisplayName) -> Self { + display_name.as_str().into() + } +} + +impl TryGetable for DisplayName { + fn try_get(res: &QueryResult, pre: &str, col: &str) -> Result { + Ok(DisplayName::new(&String::try_get(res, pre, col)?)) + } +} + +impl TryFromU64 for DisplayName { + fn try_from_u64(_n: u64) -> Result { + Err(DbErr::ConvertFromU64( + "DisplayName cannot be constructed from u64", + )) + } +} + +impl ValueType for DisplayName { + fn try_from(v: Value) -> Result { + Ok(DisplayName::new( + ::try_from(v)?.as_str(), + )) + } + + fn type_name() -> String { + "DisplayName".to_owned() + } + + fn array_type() -> ArrayType { + ArrayType::String + } + + fn column_type() -> ColumnType { + ColumnType::String(Some(255)) + } +} + #[derive(PartialEq, Eq, Clone, Debug, Serialize, Deserialize)] pub struct JpegPhoto(#[serde(with = "serde_bytes")] Vec); @@ -304,7 +380,7 @@ impl IntoActiveValue for JpegPhoto { pub struct User { pub user_id: UserId, pub email: String, - pub display_name: Option, + pub display_name: DisplayName, pub first_name: Option, pub last_name: Option, pub avatar: Option, @@ -320,7 +396,7 @@ impl Default for User { User { user_id: UserId::default(), email: String::new(), - display_name: None, + display_name: DisplayName::default(), first_name: None, last_name: None, avatar: None, diff --git a/server/src/infra/auth_service.rs b/server/src/infra/auth_service.rs index e8f8c7e..caecb18 100644 --- a/server/src/infra/auth_service.rs +++ b/server/src/infra/auth_service.rs @@ -171,9 +171,7 @@ where Some(token) => token, }; if let Err(e) = super::mail::send_password_reset_email( - user.display_name - .as_deref() - .unwrap_or_else(|| user.user_id.as_str()), + user.display_name.as_str(), &user.email, &token, &data.server_url, diff --git a/server/src/infra/graphql/mutation.rs b/server/src/infra/graphql/mutation.rs index 9a25013..f84c49d 100644 --- a/server/src/infra/graphql/mutation.rs +++ b/server/src/infra/graphql/mutation.rs @@ -1,6 +1,6 @@ use crate::domain::{ handler::{BackendHandler, CreateUserRequest, UpdateGroupRequest, UpdateUserRequest}, - types::{GroupId, JpegPhoto, UserId}, + types::{DisplayName, GroupId, JpegPhoto, UserId}, }; use anyhow::Context as AnyhowContext; use juniper::{graphql_object, FieldResult, GraphQLInputObject, GraphQLObject}; @@ -27,7 +27,7 @@ impl Mutation { pub struct CreateUserInput { id: String, email: String, - display_name: Option, + display_name: String, first_name: Option, last_name: Option, // Base64 encoded JpegPhoto. @@ -39,7 +39,7 @@ pub struct CreateUserInput { pub struct UpdateUserInput { id: String, email: Option, - display_name: Option, + display_name: String, first_name: Option, last_name: Option, // Base64 encoded JpegPhoto. @@ -79,6 +79,7 @@ impl Mutation { return Err("Unauthorized user creation".into()); } let user_id = UserId::new(&user.id); + let display_name = DisplayName::new(&user.display_name); let avatar = user .avatar .map(base64::decode) @@ -92,7 +93,7 @@ impl Mutation { .create_user(CreateUserRequest { user_id: user_id.clone(), email: user.email, - display_name: user.display_name, + display_name: display_name.clone(), first_name: user.first_name, last_name: user.last_name, avatar, @@ -137,6 +138,7 @@ impl Mutation { debug!(?user.id); }); let user_id = UserId::new(&user.id); + let display_name = DisplayName::new(&user.display_name); if !context.validation_result.can_write(&user_id) { span.in_scope(|| debug!("Unauthorized")); return Err("Unauthorized user update".into()); @@ -154,7 +156,7 @@ impl Mutation { .update_user(UpdateUserRequest { user_id, email: user.email, - display_name: user.display_name, + display_name: display_name.clone(), first_name: user.first_name, last_name: user.last_name, avatar, diff --git a/server/src/infra/graphql/query.rs b/server/src/infra/graphql/query.rs index 03091f6..f15ecc0 100644 --- a/server/src/infra/graphql/query.rs +++ b/server/src/infra/graphql/query.rs @@ -214,7 +214,7 @@ impl User { } fn display_name(&self) -> &str { - self.user.display_name.as_deref().unwrap_or("") + self.user.display_name.as_str() } fn first_name(&self) -> &str { diff --git a/server/src/infra/ldap_handler.rs b/server/src/infra/ldap_handler.rs index 790d2e2..b119b59 100644 --- a/server/src/infra/ldap_handler.rs +++ b/server/src/infra/ldap_handler.rs @@ -10,7 +10,7 @@ use crate::{ }, }, opaque_handler::OpaqueHandler, - types::{JpegPhoto, UserId}, + types::{DisplayName, JpegPhoto, UserId}, }, infra::auth_service::{Permission, ValidationResults}, }; @@ -460,6 +460,8 @@ impl LdapHandler LdapResult<(String, Vec)> { if attr.vals.len() > 1 { Err(LdapError { @@ -506,7 +508,12 @@ impl LdapHandler