mirror of
				https://github.com/nitnelave/lldap.git
				synced 2023-04-12 14:25:13 +00:00 
			
		
		
		
	server: move domain types to a separate file
This commit is contained in:
		
							parent
							
								
									e89b1538af
								
							
						
					
					
						commit
						09a0522e2d
					
				| @ -1,223 +1,13 @@ | ||||
| use super::error::*; | ||||
| use super::{ | ||||
|     error::Result, | ||||
|     types::{ | ||||
|         Group, GroupDetails, GroupId, JpegPhoto, User, UserAndGroups, UserColumn, UserId, Uuid, | ||||
|     }, | ||||
| }; | ||||
| use async_trait::async_trait; | ||||
| use serde::{Deserialize, Serialize}; | ||||
| use std::collections::HashSet; | ||||
| 
 | ||||
| pub use super::model::{GroupColumn, UserColumn}; | ||||
| 
 | ||||
| #[derive(PartialEq, Hash, Eq, Clone, Debug, Default, Serialize, Deserialize)] | ||||
| #[serde(try_from = "&str")] | ||||
| pub struct Uuid(String); | ||||
| 
 | ||||
| impl Uuid { | ||||
|     pub fn from_name_and_date(name: &str, creation_date: &chrono::DateTime<chrono::Utc>) -> Self { | ||||
|         Uuid( | ||||
|             uuid::Uuid::new_v3( | ||||
|                 &uuid::Uuid::NAMESPACE_X500, | ||||
|                 &[name.as_bytes(), creation_date.to_rfc3339().as_bytes()].concat(), | ||||
|             ) | ||||
|             .to_string(), | ||||
|         ) | ||||
|     } | ||||
| 
 | ||||
|     pub fn as_str(&self) -> &str { | ||||
|         &self.0 | ||||
|     } | ||||
| 
 | ||||
|     pub fn into_string(self) -> String { | ||||
|         self.0 | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| impl<'a> std::convert::TryFrom<&'a str> for Uuid { | ||||
|     type Error = anyhow::Error; | ||||
|     fn try_from(s: &'a str) -> anyhow::Result<Self> { | ||||
|         Ok(Uuid(uuid::Uuid::parse_str(s)?.to_string())) | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| impl std::string::ToString for Uuid { | ||||
|     fn to_string(&self) -> String { | ||||
|         self.0.clone() | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| impl sea_orm::TryGetable for Uuid { | ||||
|     fn try_get( | ||||
|         res: &sea_orm::QueryResult, | ||||
|         pre: &str, | ||||
|         col: &str, | ||||
|     ) -> std::result::Result<Self, sea_orm::TryGetError> { | ||||
|         Ok(Uuid(String::try_get(res, pre, col)?)) | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| #[cfg(test)] | ||||
| #[macro_export] | ||||
| macro_rules! uuid { | ||||
|     ($s:literal) => { | ||||
|         <$crate::domain::handler::Uuid as std::convert::TryFrom<_>>::try_from($s).unwrap() | ||||
|     }; | ||||
| } | ||||
| 
 | ||||
| #[derive(PartialEq, Eq, Clone, Debug, Default, Serialize, Deserialize)] | ||||
| #[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, Clone, Debug, Serialize, Deserialize)] | ||||
| pub struct JpegPhoto(#[serde(with = "serde_bytes")] Vec<u8>); | ||||
| 
 | ||||
| impl JpegPhoto { | ||||
|     pub fn null() -> Self { | ||||
|         Self(vec![]) | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| impl From<JpegPhoto> for sea_orm::Value { | ||||
|     fn from(photo: JpegPhoto) -> Self { | ||||
|         photo.0.into() | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| impl From<&JpegPhoto> for sea_orm::Value { | ||||
|     fn from(photo: &JpegPhoto) -> Self { | ||||
|         photo.0.as_slice().into() | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| impl TryFrom<&[u8]> for JpegPhoto { | ||||
|     type Error = anyhow::Error; | ||||
|     fn try_from(bytes: &[u8]) -> anyhow::Result<Self> { | ||||
|         if bytes.is_empty() { | ||||
|             return Ok(JpegPhoto::null()); | ||||
|         } | ||||
|         // Confirm that it's a valid Jpeg, then store only the bytes.
 | ||||
|         image::io::Reader::with_format(std::io::Cursor::new(bytes), image::ImageFormat::Jpeg) | ||||
|             .decode()?; | ||||
|         Ok(JpegPhoto(bytes.to_vec())) | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| impl TryFrom<Vec<u8>> for JpegPhoto { | ||||
|     type Error = anyhow::Error; | ||||
|     fn try_from(bytes: Vec<u8>) -> anyhow::Result<Self> { | ||||
|         if bytes.is_empty() { | ||||
|             return Ok(JpegPhoto::null()); | ||||
|         } | ||||
|         // Confirm that it's a valid Jpeg, then store only the bytes.
 | ||||
|         image::io::Reader::with_format( | ||||
|             std::io::Cursor::new(bytes.as_slice()), | ||||
|             image::ImageFormat::Jpeg, | ||||
|         ) | ||||
|         .decode()?; | ||||
|         Ok(JpegPhoto(bytes)) | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| impl TryFrom<String> for JpegPhoto { | ||||
|     type Error = anyhow::Error; | ||||
|     fn try_from(string: String) -> anyhow::Result<Self> { | ||||
|         // The String format is in base64.
 | ||||
|         Self::try_from(base64::decode(string.as_str())?) | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| impl From<&JpegPhoto> for String { | ||||
|     fn from(val: &JpegPhoto) -> Self { | ||||
|         base64::encode(&val.0) | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| impl JpegPhoto { | ||||
|     pub fn into_bytes(self) -> Vec<u8> { | ||||
|         self.0 | ||||
|     } | ||||
| 
 | ||||
|     #[cfg(test)] | ||||
|     pub fn for_tests() -> Self { | ||||
|         use image::{ImageOutputFormat, Rgb, RgbImage}; | ||||
|         let img = RgbImage::from_fn(32, 32, |x, y| { | ||||
|             if (x + y) % 2 == 0 { | ||||
|                 Rgb([0, 0, 0]) | ||||
|             } else { | ||||
|                 Rgb([255, 255, 255]) | ||||
|             } | ||||
|         }); | ||||
|         let mut bytes: Vec<u8> = Vec::new(); | ||||
|         img.write_to( | ||||
|             &mut std::io::Cursor::new(&mut bytes), | ||||
|             ImageOutputFormat::Jpeg(0), | ||||
|         ) | ||||
|         .unwrap(); | ||||
|         Self(bytes) | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| #[derive(PartialEq, Eq, Debug, Clone, Serialize, Deserialize, sea_orm::FromQueryResult)] | ||||
| pub struct User { | ||||
|     pub user_id: UserId, | ||||
|     pub email: String, | ||||
|     pub display_name: Option<String>, | ||||
|     pub first_name: Option<String>, | ||||
|     pub last_name: Option<String>, | ||||
|     pub avatar: Option<JpegPhoto>, | ||||
|     pub creation_date: chrono::DateTime<chrono::Utc>, | ||||
|     pub uuid: Uuid, | ||||
| } | ||||
| 
 | ||||
| #[cfg(test)] | ||||
| impl Default for User { | ||||
|     fn default() -> Self { | ||||
|         use chrono::TimeZone; | ||||
|         let epoch = chrono::Utc.timestamp_opt(0, 0).unwrap(); | ||||
|         User { | ||||
|             user_id: UserId::default(), | ||||
|             email: String::new(), | ||||
|             display_name: None, | ||||
|             first_name: None, | ||||
|             last_name: None, | ||||
|             avatar: None, | ||||
|             creation_date: epoch, | ||||
|             uuid: Uuid::from_name_and_date("", &epoch), | ||||
|         } | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| #[derive(PartialEq, Eq, Debug, Serialize, Deserialize)] | ||||
| pub struct Group { | ||||
|     pub id: GroupId, | ||||
|     pub display_name: String, | ||||
|     pub creation_date: chrono::DateTime<chrono::Utc>, | ||||
|     pub uuid: Uuid, | ||||
|     pub users: Vec<UserId>, | ||||
| } | ||||
| 
 | ||||
| #[derive(PartialEq, Eq, Debug, Serialize, Deserialize, Clone)] | ||||
| pub struct BindRequest { | ||||
|     pub name: UserId, | ||||
| @ -282,23 +72,6 @@ pub trait LoginHandler: Clone + Send { | ||||
|     async fn bind(&self, request: BindRequest) -> Result<()>; | ||||
| } | ||||
| 
 | ||||
| #[derive(Debug, Copy, Clone, PartialEq, Eq, Hash, Serialize, Deserialize)] | ||||
| pub struct GroupId(pub i32); | ||||
| 
 | ||||
| #[derive(Debug, Clone, PartialEq, Eq, Hash, Serialize, Deserialize, sea_orm::FromQueryResult)] | ||||
| pub struct GroupDetails { | ||||
|     pub group_id: GroupId, | ||||
|     pub display_name: String, | ||||
|     pub creation_date: chrono::DateTime<chrono::Utc>, | ||||
|     pub uuid: Uuid, | ||||
| } | ||||
| 
 | ||||
| #[derive(Debug, Clone, PartialEq, Eq)] | ||||
| pub struct UserAndGroups { | ||||
|     pub user: User, | ||||
|     pub groups: Option<Vec<GroupDetails>>, | ||||
| } | ||||
| 
 | ||||
| #[async_trait] | ||||
| pub trait GroupBackendHandler { | ||||
|     async fn list_groups(&self, filters: Option<GroupRequestFilter>) -> Result<Vec<Group>>; | ||||
|  | ||||
| @ -4,8 +4,9 @@ use ldap3_proto::{ | ||||
| use tracing::{debug, info, instrument, warn}; | ||||
| 
 | ||||
| use crate::domain::{ | ||||
|     handler::{BackendHandler, Group, GroupColumn, GroupRequestFilter, UserId, Uuid}, | ||||
|     handler::{BackendHandler, GroupRequestFilter}, | ||||
|     ldap::error::LdapError, | ||||
|     types::{Group, GroupColumn, UserId, Uuid}, | ||||
| }; | ||||
| 
 | ||||
| use super::{ | ||||
|  | ||||
| @ -4,8 +4,9 @@ use ldap3_proto::{ | ||||
| use tracing::{debug, info, instrument, warn}; | ||||
| 
 | ||||
| use crate::domain::{ | ||||
|     handler::{BackendHandler, GroupDetails, User, UserColumn, UserId, UserRequestFilter}, | ||||
|     handler::{BackendHandler, UserRequestFilter}, | ||||
|     ldap::{error::LdapError, utils::expand_attribute_wildcards}, | ||||
|     types::{GroupDetails, User, UserColumn, UserId}, | ||||
| }; | ||||
| 
 | ||||
| use super::{ | ||||
|  | ||||
| @ -2,9 +2,10 @@ use itertools::Itertools; | ||||
| use ldap3_proto::LdapResultCode; | ||||
| use tracing::{debug, instrument, warn}; | ||||
| 
 | ||||
| use crate::domain::handler::{GroupColumn, UserColumn, UserId}; | ||||
| 
 | ||||
| use super::error::{LdapError, LdapResult}; | ||||
| use crate::domain::{ | ||||
|     ldap::error::{LdapError, LdapResult}, | ||||
|     types::{GroupColumn, UserColumn, UserId}, | ||||
| }; | ||||
| 
 | ||||
| fn make_dn_pair<I>(mut iter: I) -> LdapResult<(String, String)> | ||||
| where | ||||
|  | ||||
| @ -9,3 +9,4 @@ pub mod sql_migrations; | ||||
| pub mod sql_opaque_handler; | ||||
| pub mod sql_tables; | ||||
| pub mod sql_user_backend_handler; | ||||
| pub mod types; | ||||
|  | ||||
| @ -3,7 +3,7 @@ | ||||
| use sea_orm::entity::prelude::*; | ||||
| use serde::{Deserialize, Serialize}; | ||||
| 
 | ||||
| use crate::domain::handler::{GroupId, Uuid}; | ||||
| use crate::domain::types::{GroupId, Uuid}; | ||||
| 
 | ||||
| #[derive(Clone, Debug, PartialEq, DeriveEntityModel, Eq, Serialize, Deserialize)] | ||||
| #[sea_orm(table_name = "groups")] | ||||
| @ -15,29 +15,6 @@ pub struct Model { | ||||
|     pub uuid: Uuid, | ||||
| } | ||||
| 
 | ||||
| impl From<Model> for crate::domain::handler::Group { | ||||
|     fn from(group: Model) -> Self { | ||||
|         Self { | ||||
|             id: group.group_id, | ||||
|             display_name: group.display_name, | ||||
|             creation_date: group.creation_date, | ||||
|             uuid: group.uuid, | ||||
|             users: vec![], | ||||
|         } | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| impl From<Model> for crate::domain::handler::GroupDetails { | ||||
|     fn from(group: Model) -> Self { | ||||
|         Self { | ||||
|             group_id: group.group_id, | ||||
|             display_name: group.display_name, | ||||
|             creation_date: group.creation_date, | ||||
|             uuid: group.uuid, | ||||
|         } | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| #[derive(Copy, Clone, Debug, EnumIter, DeriveRelation)] | ||||
| pub enum Relation { | ||||
|     #[sea_orm(has_many = "super::memberships::Entity")] | ||||
| @ -51,3 +28,26 @@ impl Related<super::memberships::Entity> for Entity { | ||||
| } | ||||
| 
 | ||||
| impl ActiveModelBehavior for ActiveModel {} | ||||
| 
 | ||||
| impl From<Model> for crate::domain::types::Group { | ||||
|     fn from(group: Model) -> Self { | ||||
|         Self { | ||||
|             id: group.group_id, | ||||
|             display_name: group.display_name, | ||||
|             creation_date: group.creation_date, | ||||
|             uuid: group.uuid, | ||||
|             users: vec![], | ||||
|         } | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| impl From<Model> for crate::domain::types::GroupDetails { | ||||
|     fn from(group: Model) -> Self { | ||||
|         Self { | ||||
|             group_id: group.group_id, | ||||
|             display_name: group.display_name, | ||||
|             creation_date: group.creation_date, | ||||
|             uuid: group.uuid, | ||||
|         } | ||||
|     } | ||||
| } | ||||
|  | ||||
| @ -3,7 +3,7 @@ | ||||
| use sea_orm::entity::prelude::*; | ||||
| use serde::{Deserialize, Serialize}; | ||||
| 
 | ||||
| use crate::domain::handler::UserId; | ||||
| use crate::domain::types::UserId; | ||||
| 
 | ||||
| #[derive(Clone, Debug, PartialEq, DeriveEntityModel, Eq, Serialize, Deserialize)] | ||||
| #[sea_orm(table_name = "jwt_refresh_storage")] | ||||
|  | ||||
| @ -3,7 +3,7 @@ | ||||
| use sea_orm::entity::prelude::*; | ||||
| use serde::{Deserialize, Serialize}; | ||||
| 
 | ||||
| use crate::domain::handler::UserId; | ||||
| use crate::domain::types::UserId; | ||||
| 
 | ||||
| #[derive(Clone, Debug, PartialEq, DeriveEntityModel, Eq, Serialize, Deserialize)] | ||||
| #[sea_orm(table_name = "jwt_storage")] | ||||
|  | ||||
| @ -3,7 +3,7 @@ | ||||
| use sea_orm::entity::prelude::*; | ||||
| use serde::{Deserialize, Serialize}; | ||||
| 
 | ||||
| use crate::domain::handler::{GroupId, UserId}; | ||||
| use crate::domain::types::{GroupId, UserId}; | ||||
| 
 | ||||
| #[derive(Clone, Debug, PartialEq, DeriveEntityModel, Eq, Serialize, Deserialize)] | ||||
| #[sea_orm(table_name = "memberships")] | ||||
|  | ||||
| @ -3,7 +3,7 @@ | ||||
| use sea_orm::entity::prelude::*; | ||||
| use serde::{Deserialize, Serialize}; | ||||
| 
 | ||||
| use crate::domain::handler::UserId; | ||||
| use crate::domain::types::UserId; | ||||
| 
 | ||||
| #[derive(Clone, Debug, PartialEq, DeriveEntityModel, Eq, Serialize, Deserialize)] | ||||
| #[sea_orm(table_name = "password_reset_tokens")] | ||||
|  | ||||
| @ -3,7 +3,7 @@ | ||||
| use sea_orm::entity::prelude::*; | ||||
| use serde::{Deserialize, Serialize}; | ||||
| 
 | ||||
| use crate::domain::handler::{JpegPhoto, UserId, Uuid}; | ||||
| use crate::domain::types::{JpegPhoto, UserId, Uuid}; | ||||
| 
 | ||||
| #[derive(Copy, Clone, Default, Debug, DeriveEntity)] | ||||
| pub struct Entity; | ||||
| @ -118,7 +118,7 @@ impl Related<super::password_reset_tokens::Entity> for Entity { | ||||
| 
 | ||||
| impl ActiveModelBehavior for ActiveModel {} | ||||
| 
 | ||||
| impl From<Model> for crate::domain::handler::User { | ||||
| impl From<Model> for crate::domain::types::User { | ||||
|     fn from(user: Model) -> Self { | ||||
|         Self { | ||||
|             user_id: user.user_id, | ||||
|  | ||||
| @ -1,4 +1,4 @@ | ||||
| use crate::domain::{error::*, handler::UserId}; | ||||
| use crate::domain::{error::Result, types::UserId}; | ||||
| use async_trait::async_trait; | ||||
| 
 | ||||
| pub use lldap_auth::{login, registration}; | ||||
|  | ||||
| @ -1,4 +1,4 @@ | ||||
| use super::{handler::*, sql_tables::*}; | ||||
| use super::{handler::BackendHandler, sql_tables::DbConnection}; | ||||
| use crate::infra::configuration::Configuration; | ||||
| use async_trait::async_trait; | ||||
| 
 | ||||
| @ -20,8 +20,16 @@ impl BackendHandler for SqlBackendHandler {} | ||||
| #[cfg(test)] | ||||
| pub mod tests { | ||||
|     use super::*; | ||||
|     use crate::domain::sql_tables::init_table; | ||||
|     use crate::infra::configuration::ConfigurationBuilder; | ||||
|     use crate::{ | ||||
|         domain::{ | ||||
|             handler::{ | ||||
|                 CreateUserRequest, GroupBackendHandler, UserBackendHandler, UserRequestFilter, | ||||
|             }, | ||||
|             sql_tables::init_table, | ||||
|             types::{GroupId, UserId}, | ||||
|         }, | ||||
|         infra::configuration::ConfigurationBuilder, | ||||
|     }; | ||||
|     use lldap_auth::{opaque, registration}; | ||||
|     use sea_orm::Database; | ||||
| 
 | ||||
|  | ||||
| @ -1,12 +1,9 @@ | ||||
| use crate::domain::handler::Uuid; | ||||
| 
 | ||||
| use super::{ | ||||
| use crate::domain::{ | ||||
|     error::{DomainError, Result}, | ||||
|     handler::{ | ||||
|         Group, GroupBackendHandler, GroupDetails, GroupId, GroupRequestFilter, UpdateGroupRequest, | ||||
|     }, | ||||
|     handler::{GroupBackendHandler, GroupRequestFilter, UpdateGroupRequest}, | ||||
|     model::{self, GroupColumn, MembershipColumn}, | ||||
|     sql_backend_handler::SqlBackendHandler, | ||||
|     types::{Group, GroupDetails, GroupId, Uuid}, | ||||
| }; | ||||
| use async_trait::async_trait; | ||||
| use sea_orm::{ | ||||
| @ -155,7 +152,7 @@ impl GroupBackendHandler for SqlBackendHandler { | ||||
| #[cfg(test)] | ||||
| mod tests { | ||||
|     use super::*; | ||||
|     use crate::domain::{handler::UserId, sql_backend_handler::tests::*}; | ||||
|     use crate::domain::{sql_backend_handler::tests::*, types::UserId}; | ||||
| 
 | ||||
|     async fn get_group_ids( | ||||
|         handler: &SqlBackendHandler, | ||||
|  | ||||
| @ -1,6 +1,6 @@ | ||||
| use super::{ | ||||
|     handler::{GroupId, UserId, Uuid}, | ||||
| use crate::domain::{ | ||||
|     sql_tables::{DbConnection, SchemaVersion}, | ||||
|     types::{GroupId, UserId, Uuid}, | ||||
| }; | ||||
| use sea_orm::{ConnectionTrait, FromQueryResult, Statement}; | ||||
| use sea_query::{ColumnDef, Expr, ForeignKey, ForeignKeyAction, Iden, Query, Table, Value}; | ||||
|  | ||||
| @ -1,9 +1,10 @@ | ||||
| use super::{ | ||||
|     error::{DomainError, Result}, | ||||
|     handler::{BindRequest, LoginHandler, UserId}, | ||||
|     handler::{BindRequest, LoginHandler}, | ||||
|     model::{self, UserColumn}, | ||||
|     opaque_handler::{login, registration, OpaqueHandler}, | ||||
|     sql_backend_handler::SqlBackendHandler, | ||||
|     types::UserId, | ||||
| }; | ||||
| use async_trait::async_trait; | ||||
| use lldap_auth::opaque; | ||||
|  | ||||
| @ -1,8 +1,5 @@ | ||||
| use super::{ | ||||
|     handler::{GroupId, JpegPhoto, UserId, Uuid}, | ||||
|     sql_migrations::{get_schema_version, migrate_from_version, upgrade_to_v1}, | ||||
| }; | ||||
| use sea_orm::{DbErr, Value}; | ||||
| use super::sql_migrations::{get_schema_version, migrate_from_version, upgrade_to_v1}; | ||||
| use sea_orm::Value; | ||||
| 
 | ||||
| pub type DbConnection = sea_orm::DatabaseConnection; | ||||
| 
 | ||||
| @ -19,181 +16,6 @@ impl sea_orm::TryGetable for SchemaVersion { | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| impl From<GroupId> for sea_orm::Value { | ||||
|     fn from(group_id: GroupId) -> Self { | ||||
|         group_id.0.into() | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| impl sea_orm::TryGetable for GroupId { | ||||
|     fn try_get( | ||||
|         res: &sea_orm::QueryResult, | ||||
|         pre: &str, | ||||
|         col: &str, | ||||
|     ) -> Result<Self, sea_orm::TryGetError> { | ||||
|         Ok(GroupId(i32::try_get(res, pre, col)?)) | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| impl sea_orm::sea_query::value::ValueType for GroupId { | ||||
|     fn try_from(v: sea_orm::Value) -> Result<Self, sea_orm::sea_query::ValueTypeErr> { | ||||
|         Ok(GroupId(<i32 as sea_orm::sea_query::ValueType>::try_from( | ||||
|             v, | ||||
|         )?)) | ||||
|     } | ||||
| 
 | ||||
|     fn type_name() -> String { | ||||
|         "GroupId".to_owned() | ||||
|     } | ||||
| 
 | ||||
|     fn array_type() -> sea_orm::sea_query::ArrayType { | ||||
|         sea_orm::sea_query::ArrayType::Int | ||||
|     } | ||||
| 
 | ||||
|     fn column_type() -> sea_orm::sea_query::ColumnType { | ||||
|         sea_orm::sea_query::ColumnType::Integer(None) | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| impl sea_orm::TryFromU64 for GroupId { | ||||
|     fn try_from_u64(n: u64) -> Result<Self, sea_orm::DbErr> { | ||||
|         Ok(GroupId(i32::try_from_u64(n)?)) | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| impl From<UserId> for sea_orm::Value { | ||||
|     fn from(user_id: UserId) -> Self { | ||||
|         user_id.into_string().into() | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| impl From<&UserId> for sea_orm::Value { | ||||
|     fn from(user_id: &UserId) -> Self { | ||||
|         user_id.as_str().into() | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| impl sea_orm::TryGetable for UserId { | ||||
|     fn try_get( | ||||
|         res: &sea_orm::QueryResult, | ||||
|         pre: &str, | ||||
|         col: &str, | ||||
|     ) -> Result<Self, sea_orm::TryGetError> { | ||||
|         Ok(UserId::new(&String::try_get(res, pre, col)?)) | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| impl sea_orm::TryFromU64 for UserId { | ||||
|     fn try_from_u64(_n: u64) -> Result<Self, sea_orm::DbErr> { | ||||
|         Err(sea_orm::DbErr::ConvertFromU64( | ||||
|             "UserId cannot be constructed from u64", | ||||
|         )) | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| impl sea_orm::sea_query::value::ValueType for UserId { | ||||
|     fn try_from(v: sea_orm::Value) -> Result<Self, sea_orm::sea_query::ValueTypeErr> { | ||||
|         Ok(UserId::new( | ||||
|             <String as sea_orm::sea_query::ValueType>::try_from(v)?.as_str(), | ||||
|         )) | ||||
|     } | ||||
| 
 | ||||
|     fn type_name() -> String { | ||||
|         "UserId".to_owned() | ||||
|     } | ||||
| 
 | ||||
|     fn array_type() -> sea_orm::sea_query::ArrayType { | ||||
|         sea_orm::sea_query::ArrayType::String | ||||
|     } | ||||
| 
 | ||||
|     fn column_type() -> sea_orm::sea_query::ColumnType { | ||||
|         sea_orm::sea_query::ColumnType::String(Some(255)) | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| impl From<Uuid> for sea_query::Value { | ||||
|     fn from(uuid: Uuid) -> Self { | ||||
|         uuid.as_str().into() | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| impl From<&Uuid> for sea_query::Value { | ||||
|     fn from(uuid: &Uuid) -> Self { | ||||
|         uuid.as_str().into() | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| impl sea_orm::TryGetable for JpegPhoto { | ||||
|     fn try_get( | ||||
|         res: &sea_orm::QueryResult, | ||||
|         pre: &str, | ||||
|         col: &str, | ||||
|     ) -> Result<Self, sea_orm::TryGetError> { | ||||
|         <JpegPhoto as std::convert::TryFrom<Vec<_>>>::try_from(Vec::<u8>::try_get(res, pre, col)?) | ||||
|             .map_err(|e| { | ||||
|                 sea_orm::TryGetError::DbErr(DbErr::TryIntoErr { | ||||
|                     from: "[u8]", | ||||
|                     into: "JpegPhoto", | ||||
|                     source: e.into(), | ||||
|                 }) | ||||
|             }) | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| impl sea_orm::sea_query::value::ValueType for JpegPhoto { | ||||
|     fn try_from(v: sea_orm::Value) -> Result<Self, sea_orm::sea_query::ValueTypeErr> { | ||||
|         <JpegPhoto as std::convert::TryFrom<_>>::try_from( | ||||
|             <Vec<u8> as sea_orm::sea_query::ValueType>::try_from(v)?.as_slice(), | ||||
|         ) | ||||
|         .map_err(|_| sea_orm::sea_query::ValueTypeErr {}) | ||||
|     } | ||||
| 
 | ||||
|     fn type_name() -> String { | ||||
|         "JpegPhoto".to_owned() | ||||
|     } | ||||
| 
 | ||||
|     fn array_type() -> sea_orm::sea_query::ArrayType { | ||||
|         sea_orm::sea_query::ArrayType::Bytes | ||||
|     } | ||||
| 
 | ||||
|     fn column_type() -> sea_orm::sea_query::ColumnType { | ||||
|         sea_orm::sea_query::ColumnType::Binary(sea_orm::sea_query::BlobSize::Long) | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| impl sea_orm::sea_query::Nullable for JpegPhoto { | ||||
|     fn null() -> sea_orm::Value { | ||||
|         JpegPhoto::null().into() | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| impl sea_orm::entity::IntoActiveValue<JpegPhoto> for JpegPhoto { | ||||
|     fn into_active_value(self) -> sea_orm::ActiveValue<JpegPhoto> { | ||||
|         sea_orm::ActiveValue::Set(self) | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| impl sea_orm::sea_query::value::ValueType for Uuid { | ||||
|     fn try_from(v: sea_orm::Value) -> Result<Self, sea_orm::sea_query::ValueTypeErr> { | ||||
|         <super::handler::Uuid as std::convert::TryFrom<_>>::try_from( | ||||
|             <std::string::String as sea_orm::sea_query::ValueType>::try_from(v)?.as_str(), | ||||
|         ) | ||||
|         .map_err(|_| sea_orm::sea_query::ValueTypeErr {}) | ||||
|     } | ||||
| 
 | ||||
|     fn type_name() -> String { | ||||
|         "Uuid".to_owned() | ||||
|     } | ||||
| 
 | ||||
|     fn array_type() -> sea_orm::sea_query::ArrayType { | ||||
|         sea_orm::sea_query::ArrayType::String | ||||
|     } | ||||
| 
 | ||||
|     fn column_type() -> sea_orm::sea_query::ColumnType { | ||||
|         sea_orm::sea_query::ColumnType::String(Some(36)) | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| impl From<SchemaVersion> for Value { | ||||
|     fn from(version: SchemaVersion) -> Self { | ||||
|         version.0.into() | ||||
| @ -215,7 +37,10 @@ pub async fn init_table(pool: &DbConnection) -> anyhow::Result<()> { | ||||
| 
 | ||||
| #[cfg(test)] | ||||
| mod tests { | ||||
|     use crate::domain::sql_migrations; | ||||
|     use crate::domain::{ | ||||
|         sql_migrations, | ||||
|         types::{GroupId, Uuid}, | ||||
|     }; | ||||
| 
 | ||||
|     use super::*; | ||||
|     use chrono::prelude::*; | ||||
|  | ||||
| @ -1,11 +1,9 @@ | ||||
| use super::{ | ||||
|     error::{DomainError, Result}, | ||||
|     handler::{ | ||||
|         CreateUserRequest, GroupDetails, GroupId, UpdateUserRequest, User, UserAndGroups, | ||||
|         UserBackendHandler, UserId, UserRequestFilter, Uuid, | ||||
|     }, | ||||
|     handler::{CreateUserRequest, UpdateUserRequest, UserBackendHandler, UserRequestFilter}, | ||||
|     model::{self, GroupColumn, UserColumn}, | ||||
|     sql_backend_handler::SqlBackendHandler, | ||||
|     types::{GroupDetails, GroupId, User, UserAndGroups, UserId, Uuid}, | ||||
| }; | ||||
| use async_trait::async_trait; | ||||
| use sea_orm::{ | ||||
| @ -245,8 +243,8 @@ impl UserBackendHandler for SqlBackendHandler { | ||||
| mod tests { | ||||
|     use super::*; | ||||
|     use crate::domain::{ | ||||
|         handler::{JpegPhoto, UserColumn}, | ||||
|         sql_backend_handler::tests::*, | ||||
|         types::{JpegPhoto, UserColumn}, | ||||
|     }; | ||||
| 
 | ||||
|     #[tokio::test] | ||||
|  | ||||
							
								
								
									
										393
									
								
								server/src/domain/types.rs
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										393
									
								
								server/src/domain/types.rs
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,393 @@ | ||||
| use sea_orm::{ | ||||
|     entity::IntoActiveValue, | ||||
|     sea_query::{value::ValueType, ArrayType, ColumnType, Nullable, ValueTypeErr}, | ||||
|     DbErr, FromQueryResult, QueryResult, TryFromU64, TryGetError, TryGetable, Value, | ||||
| }; | ||||
| use serde::{Deserialize, Serialize}; | ||||
| 
 | ||||
| pub use super::model::{GroupColumn, UserColumn}; | ||||
| 
 | ||||
| pub type DateTime = chrono::DateTime<chrono::Utc>; | ||||
| 
 | ||||
| #[derive(PartialEq, Hash, Eq, Clone, Debug, Default, Serialize, Deserialize)] | ||||
| #[serde(try_from = "&str")] | ||||
| pub struct Uuid(String); | ||||
| 
 | ||||
| impl Uuid { | ||||
|     pub fn from_name_and_date(name: &str, creation_date: &DateTime) -> Self { | ||||
|         Uuid( | ||||
|             uuid::Uuid::new_v3( | ||||
|                 &uuid::Uuid::NAMESPACE_X500, | ||||
|                 &[name.as_bytes(), creation_date.to_rfc3339().as_bytes()].concat(), | ||||
|             ) | ||||
|             .to_string(), | ||||
|         ) | ||||
|     } | ||||
| 
 | ||||
|     pub fn as_str(&self) -> &str { | ||||
|         &self.0 | ||||
|     } | ||||
| 
 | ||||
|     pub fn into_string(self) -> String { | ||||
|         self.0 | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| impl<'a> std::convert::TryFrom<&'a str> for Uuid { | ||||
|     type Error = anyhow::Error; | ||||
|     fn try_from(s: &'a str) -> anyhow::Result<Self> { | ||||
|         Ok(Uuid(uuid::Uuid::parse_str(s)?.to_string())) | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| impl std::string::ToString for Uuid { | ||||
|     fn to_string(&self) -> String { | ||||
|         self.0.clone() | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| impl TryGetable for Uuid { | ||||
|     fn try_get(res: &QueryResult, pre: &str, col: &str) -> std::result::Result<Self, TryGetError> { | ||||
|         Ok(Uuid(String::try_get(res, pre, col)?)) | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| impl ValueType for Uuid { | ||||
|     fn try_from(v: Value) -> Result<Self, ValueTypeErr> { | ||||
|         <Self as std::convert::TryFrom<_>>::try_from( | ||||
|             <std::string::String as sea_orm::sea_query::ValueType>::try_from(v)?.as_str(), | ||||
|         ) | ||||
|         .map_err(|_| ValueTypeErr {}) | ||||
|     } | ||||
| 
 | ||||
|     fn type_name() -> String { | ||||
|         "Uuid".to_owned() | ||||
|     } | ||||
| 
 | ||||
|     fn array_type() -> ArrayType { | ||||
|         ArrayType::String | ||||
|     } | ||||
| 
 | ||||
|     fn column_type() -> ColumnType { | ||||
|         ColumnType::String(Some(36)) | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| impl From<Uuid> for Value { | ||||
|     fn from(uuid: Uuid) -> Self { | ||||
|         uuid.as_str().into() | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| impl From<&Uuid> for Value { | ||||
|     fn from(uuid: &Uuid) -> Self { | ||||
|         uuid.as_str().into() | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| #[cfg(test)] | ||||
| #[macro_export] | ||||
| macro_rules! uuid { | ||||
|     ($s:literal) => { | ||||
|         <$crate::domain::types::Uuid as std::convert::TryFrom<_>>::try_from($s).unwrap() | ||||
|     }; | ||||
| } | ||||
| 
 | ||||
| #[derive(PartialEq, Eq, Clone, Debug, Default, Serialize, Deserialize)] | ||||
| #[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) | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| impl From<UserId> for Value { | ||||
|     fn from(user_id: UserId) -> Self { | ||||
|         user_id.into_string().into() | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| impl From<&UserId> for Value { | ||||
|     fn from(user_id: &UserId) -> Self { | ||||
|         user_id.as_str().into() | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| impl TryGetable for UserId { | ||||
|     fn try_get(res: &QueryResult, pre: &str, col: &str) -> Result<Self, TryGetError> { | ||||
|         Ok(UserId::new(&String::try_get(res, pre, col)?)) | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| impl TryFromU64 for UserId { | ||||
|     fn try_from_u64(_n: u64) -> Result<Self, DbErr> { | ||||
|         Err(DbErr::ConvertFromU64( | ||||
|             "UserId cannot be constructed from u64", | ||||
|         )) | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| impl ValueType for UserId { | ||||
|     fn try_from(v: Value) -> Result<Self, ValueTypeErr> { | ||||
|         Ok(UserId::new(<String as ValueType>::try_from(v)?.as_str())) | ||||
|     } | ||||
| 
 | ||||
|     fn type_name() -> String { | ||||
|         "UserId".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<u8>); | ||||
| 
 | ||||
| impl From<JpegPhoto> for Value { | ||||
|     fn from(photo: JpegPhoto) -> Self { | ||||
|         photo.0.into() | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| impl From<&JpegPhoto> for Value { | ||||
|     fn from(photo: &JpegPhoto) -> Self { | ||||
|         photo.0.as_slice().into() | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| impl TryFrom<&[u8]> for JpegPhoto { | ||||
|     type Error = anyhow::Error; | ||||
|     fn try_from(bytes: &[u8]) -> anyhow::Result<Self> { | ||||
|         if bytes.is_empty() { | ||||
|             return Ok(JpegPhoto::null()); | ||||
|         } | ||||
|         // Confirm that it's a valid Jpeg, then store only the bytes.
 | ||||
|         image::io::Reader::with_format(std::io::Cursor::new(bytes), image::ImageFormat::Jpeg) | ||||
|             .decode()?; | ||||
|         Ok(JpegPhoto(bytes.to_vec())) | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| impl TryFrom<Vec<u8>> for JpegPhoto { | ||||
|     type Error = anyhow::Error; | ||||
|     fn try_from(bytes: Vec<u8>) -> anyhow::Result<Self> { | ||||
|         if bytes.is_empty() { | ||||
|             return Ok(JpegPhoto::null()); | ||||
|         } | ||||
|         // Confirm that it's a valid Jpeg, then store only the bytes.
 | ||||
|         image::io::Reader::with_format( | ||||
|             std::io::Cursor::new(bytes.as_slice()), | ||||
|             image::ImageFormat::Jpeg, | ||||
|         ) | ||||
|         .decode()?; | ||||
|         Ok(JpegPhoto(bytes)) | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| impl TryFrom<String> for JpegPhoto { | ||||
|     type Error = anyhow::Error; | ||||
|     fn try_from(string: String) -> anyhow::Result<Self> { | ||||
|         // The String format is in base64.
 | ||||
|         <Self as TryFrom<_>>::try_from(base64::decode(string.as_str())?) | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| impl From<&JpegPhoto> for String { | ||||
|     fn from(val: &JpegPhoto) -> Self { | ||||
|         base64::encode(&val.0) | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| impl JpegPhoto { | ||||
|     pub fn null() -> Self { | ||||
|         Self(vec![]) | ||||
|     } | ||||
| 
 | ||||
|     pub fn into_bytes(self) -> Vec<u8> { | ||||
|         self.0 | ||||
|     } | ||||
| 
 | ||||
|     #[cfg(test)] | ||||
|     pub fn for_tests() -> Self { | ||||
|         use image::{ImageOutputFormat, Rgb, RgbImage}; | ||||
|         let img = RgbImage::from_fn(32, 32, |x, y| { | ||||
|             if (x + y) % 2 == 0 { | ||||
|                 Rgb([0, 0, 0]) | ||||
|             } else { | ||||
|                 Rgb([255, 255, 255]) | ||||
|             } | ||||
|         }); | ||||
|         let mut bytes: Vec<u8> = Vec::new(); | ||||
|         img.write_to( | ||||
|             &mut std::io::Cursor::new(&mut bytes), | ||||
|             ImageOutputFormat::Jpeg(0), | ||||
|         ) | ||||
|         .unwrap(); | ||||
|         Self(bytes) | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| impl TryGetable for JpegPhoto { | ||||
|     fn try_get(res: &QueryResult, pre: &str, col: &str) -> Result<Self, TryGetError> { | ||||
|         <Self as std::convert::TryFrom<Vec<_>>>::try_from(Vec::<u8>::try_get(res, pre, col)?) | ||||
|             .map_err(|e| { | ||||
|                 TryGetError::DbErr(DbErr::TryIntoErr { | ||||
|                     from: "[u8]", | ||||
|                     into: "JpegPhoto", | ||||
|                     source: e.into(), | ||||
|                 }) | ||||
|             }) | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| impl ValueType for JpegPhoto { | ||||
|     fn try_from(v: Value) -> Result<Self, ValueTypeErr> { | ||||
|         <Self as std::convert::TryFrom<_>>::try_from( | ||||
|             <Vec<u8> as sea_orm::sea_query::ValueType>::try_from(v)?.as_slice(), | ||||
|         ) | ||||
|         .map_err(|_| ValueTypeErr {}) | ||||
|     } | ||||
| 
 | ||||
|     fn type_name() -> String { | ||||
|         "JpegPhoto".to_owned() | ||||
|     } | ||||
| 
 | ||||
|     fn array_type() -> ArrayType { | ||||
|         ArrayType::Bytes | ||||
|     } | ||||
| 
 | ||||
|     fn column_type() -> ColumnType { | ||||
|         ColumnType::Binary(sea_orm::sea_query::BlobSize::Long) | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| impl Nullable for JpegPhoto { | ||||
|     fn null() -> Value { | ||||
|         JpegPhoto::null().into() | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| impl IntoActiveValue<JpegPhoto> for JpegPhoto { | ||||
|     fn into_active_value(self) -> sea_orm::ActiveValue<JpegPhoto> { | ||||
|         sea_orm::ActiveValue::Set(self) | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| #[derive(PartialEq, Eq, Debug, Clone, Serialize, Deserialize, FromQueryResult)] | ||||
| pub struct User { | ||||
|     pub user_id: UserId, | ||||
|     pub email: String, | ||||
|     pub display_name: Option<String>, | ||||
|     pub first_name: Option<String>, | ||||
|     pub last_name: Option<String>, | ||||
|     pub avatar: Option<JpegPhoto>, | ||||
|     pub creation_date: DateTime, | ||||
|     pub uuid: Uuid, | ||||
| } | ||||
| 
 | ||||
| #[cfg(test)] | ||||
| impl Default for User { | ||||
|     fn default() -> Self { | ||||
|         use chrono::TimeZone; | ||||
|         let epoch = chrono::Utc.timestamp_opt(0, 0).unwrap(); | ||||
|         User { | ||||
|             user_id: UserId::default(), | ||||
|             email: String::new(), | ||||
|             display_name: None, | ||||
|             first_name: None, | ||||
|             last_name: None, | ||||
|             avatar: None, | ||||
|             creation_date: epoch, | ||||
|             uuid: Uuid::from_name_and_date("", &epoch), | ||||
|         } | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| #[derive(Debug, Copy, Clone, PartialEq, Eq, Hash, Serialize, Deserialize)] | ||||
| pub struct GroupId(pub i32); | ||||
| 
 | ||||
| impl From<GroupId> for Value { | ||||
|     fn from(group_id: GroupId) -> Self { | ||||
|         group_id.0.into() | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| impl TryGetable for GroupId { | ||||
|     fn try_get(res: &QueryResult, pre: &str, col: &str) -> Result<Self, TryGetError> { | ||||
|         Ok(GroupId(i32::try_get(res, pre, col)?)) | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| impl ValueType for GroupId { | ||||
|     fn try_from(v: Value) -> Result<Self, ValueTypeErr> { | ||||
|         Ok(GroupId(<i32 as ValueType>::try_from(v)?)) | ||||
|     } | ||||
| 
 | ||||
|     fn type_name() -> String { | ||||
|         "GroupId".to_owned() | ||||
|     } | ||||
| 
 | ||||
|     fn array_type() -> ArrayType { | ||||
|         ArrayType::Int | ||||
|     } | ||||
| 
 | ||||
|     fn column_type() -> ColumnType { | ||||
|         ColumnType::Integer(None) | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| impl TryFromU64 for GroupId { | ||||
|     fn try_from_u64(n: u64) -> Result<Self, DbErr> { | ||||
|         Ok(GroupId(i32::try_from_u64(n)?)) | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| #[derive(PartialEq, Eq, Debug, Serialize, Deserialize)] | ||||
| pub struct Group { | ||||
|     pub id: GroupId, | ||||
|     pub display_name: String, | ||||
|     pub creation_date: DateTime, | ||||
|     pub uuid: Uuid, | ||||
|     pub users: Vec<UserId>, | ||||
| } | ||||
| 
 | ||||
| #[derive(Debug, Clone, PartialEq, Eq, Hash, Serialize, Deserialize, FromQueryResult)] | ||||
| pub struct GroupDetails { | ||||
|     pub group_id: GroupId, | ||||
|     pub display_name: String, | ||||
|     pub creation_date: DateTime, | ||||
|     pub uuid: Uuid, | ||||
| } | ||||
| 
 | ||||
| #[derive(Debug, Clone, PartialEq, Eq)] | ||||
| pub struct UserAndGroups { | ||||
|     pub user: User, | ||||
|     pub groups: Option<Vec<GroupDetails>>, | ||||
| } | ||||
| @ -22,12 +22,12 @@ use tracing::{debug, instrument, warn}; | ||||
| 
 | ||||
| use lldap_auth::{login, password_reset, registration, JWTClaims}; | ||||
| 
 | ||||
| use crate::domain::handler::UserRequestFilter; | ||||
| use crate::{ | ||||
|     domain::{ | ||||
|         error::DomainError, | ||||
|         handler::{BackendHandler, BindRequest, GroupDetails, LoginHandler, UserColumn, UserId}, | ||||
|         handler::{BackendHandler, BindRequest, LoginHandler, UserRequestFilter}, | ||||
|         opaque_handler::OpaqueHandler, | ||||
|         types::{GroupDetails, UserColumn, UserId}, | ||||
|     }, | ||||
|     infra::{ | ||||
|         tcp_backend_handler::*, | ||||
|  | ||||
| @ -1,5 +1,5 @@ | ||||
| use crate::{ | ||||
|     domain::handler::UserId, | ||||
|     domain::types::UserId, | ||||
|     infra::cli::{GeneralConfigOpts, LdapsOpts, RunOpts, SmtpEncryption, SmtpOpts, TestEmailOpts}, | ||||
| }; | ||||
| use anyhow::{Context, Result}; | ||||
|  | ||||
| @ -1,6 +1,6 @@ | ||||
| use crate::domain::handler::{ | ||||
|     BackendHandler, CreateUserRequest, GroupId, JpegPhoto, UpdateGroupRequest, UpdateUserRequest, | ||||
|     UserId, | ||||
| use crate::domain::{ | ||||
|     handler::{BackendHandler, CreateUserRequest, UpdateGroupRequest, UpdateUserRequest}, | ||||
|     types::{GroupId, JpegPhoto, UserId}, | ||||
| }; | ||||
| use anyhow::Context as AnyhowContext; | ||||
| use juniper::{graphql_object, FieldResult, GraphQLInputObject, GraphQLObject}; | ||||
|  | ||||
| @ -1,15 +1,16 @@ | ||||
| use crate::domain::{ | ||||
|     handler::{BackendHandler, GroupDetails, GroupId, UserColumn, UserId}, | ||||
|     handler::BackendHandler, | ||||
|     ldap::utils::map_user_field, | ||||
|     types::{GroupDetails, GroupId, UserColumn, UserId}, | ||||
| }; | ||||
| use juniper::{graphql_object, FieldResult, GraphQLInputObject}; | ||||
| use serde::{Deserialize, Serialize}; | ||||
| use tracing::{debug, debug_span, Instrument}; | ||||
| 
 | ||||
| type DomainRequestFilter = crate::domain::handler::UserRequestFilter; | ||||
| type DomainUser = crate::domain::handler::User; | ||||
| type DomainGroup = crate::domain::handler::Group; | ||||
| type DomainUserAndGroups = crate::domain::handler::UserAndGroups; | ||||
| type DomainUser = crate::domain::types::User; | ||||
| type DomainGroup = crate::domain::types::Group; | ||||
| type DomainUserAndGroups = crate::domain::types::UserAndGroups; | ||||
| use super::api::Context; | ||||
| 
 | ||||
| #[derive(PartialEq, Eq, Debug, GraphQLInputObject)] | ||||
| @ -345,10 +346,7 @@ impl<Handler: BackendHandler> From<DomainGroup> for Group<Handler> { | ||||
| #[cfg(test)] | ||||
| mod tests { | ||||
|     use super::*; | ||||
|     use crate::{ | ||||
|         domain::handler::{MockTestBackendHandler, UserRequestFilter}, | ||||
|         infra::auth_service::ValidationResults, | ||||
|     }; | ||||
|     use crate::{domain::handler::MockTestBackendHandler, infra::auth_service::ValidationResults}; | ||||
|     use chrono::TimeZone; | ||||
|     use juniper::{ | ||||
|         execute, graphql_value, DefaultScalarValue, EmptyMutation, EmptySubscription, GraphQLType, | ||||
| @ -457,9 +455,12 @@ mod tests { | ||||
|         let mut mock = MockTestBackendHandler::new(); | ||||
|         mock.expect_list_users() | ||||
|             .with( | ||||
|                 eq(Some(UserRequestFilter::Or(vec![ | ||||
|                     UserRequestFilter::UserId(UserId::new("bob")), | ||||
|                     UserRequestFilter::Equality(UserColumn::Email, "robert@bobbers.on".to_string()), | ||||
|                 eq(Some(DomainRequestFilter::Or(vec![ | ||||
|                     DomainRequestFilter::UserId(UserId::new("bob")), | ||||
|                     DomainRequestFilter::Equality( | ||||
|                         UserColumn::Email, | ||||
|                         "robert@bobbers.on".to_string(), | ||||
|                     ), | ||||
|                 ]))), | ||||
|                 eq(false), | ||||
|             ) | ||||
|  | ||||
| @ -1,10 +1,6 @@ | ||||
| use std::collections::HashMap; | ||||
| 
 | ||||
| use crate::{ | ||||
|     domain::{ | ||||
|         handler::{ | ||||
|             BackendHandler, BindRequest, CreateUserRequest, JpegPhoto, LoginHandler, UserId, | ||||
|         }, | ||||
|         handler::{BackendHandler, BindRequest, CreateUserRequest, LoginHandler}, | ||||
|         ldap::{ | ||||
|             error::{LdapError, LdapResult}, | ||||
|             group::get_groups_list, | ||||
| @ -14,6 +10,7 @@ use crate::{ | ||||
|             }, | ||||
|         }, | ||||
|         opaque_handler::OpaqueHandler, | ||||
|         types::{JpegPhoto, UserId}, | ||||
|     }, | ||||
|     infra::auth_service::{Permission, ValidationResults}, | ||||
| }; | ||||
| @ -24,6 +21,7 @@ use ldap3_proto::proto::{ | ||||
|     LdapResult as LdapResultOp, LdapResultCode, LdapSearchRequest, LdapSearchResultEntry, | ||||
|     LdapSearchScope, | ||||
| }; | ||||
| use std::collections::HashMap; | ||||
| use tracing::{debug, instrument, warn}; | ||||
| 
 | ||||
| #[derive(Debug, PartialEq, Eq, Clone)] | ||||
| @ -569,7 +567,7 @@ impl<Backend: BackendHandler + LoginHandler + OpaqueHandler> LdapHandler<Backend | ||||
| mod tests { | ||||
|     use super::*; | ||||
|     use crate::{ | ||||
|         domain::{error::Result, handler::*, opaque_handler::*}, | ||||
|         domain::{error::Result, handler::*, opaque_handler::*, types::*}, | ||||
|         uuid, | ||||
|     }; | ||||
|     use async_trait::async_trait; | ||||
|  | ||||
| @ -1,9 +1,9 @@ | ||||
| use super::tcp_backend_handler::TcpBackendHandler; | ||||
| use crate::domain::{ | ||||
|     error::*, | ||||
|     handler::UserId, | ||||
|     model::{self, JwtRefreshStorageColumn, JwtStorageColumn, PasswordResetTokensColumn}, | ||||
|     sql_backend_handler::SqlBackendHandler, | ||||
|     types::UserId, | ||||
| }; | ||||
| use async_trait::async_trait; | ||||
| use sea_orm::{ | ||||
|  | ||||
| @ -1,7 +1,7 @@ | ||||
| use async_trait::async_trait; | ||||
| use std::collections::HashSet; | ||||
| 
 | ||||
| use crate::domain::{error::Result, handler::UserId}; | ||||
| use crate::domain::{error::Result, types::UserId}; | ||||
| 
 | ||||
| #[async_trait] | ||||
| pub trait TcpBackendHandler { | ||||
| @ -22,7 +22,7 @@ pub trait TcpBackendHandler { | ||||
| } | ||||
| 
 | ||||
| #[cfg(test)] | ||||
| use crate::domain::handler::*; | ||||
| use crate::domain::{handler::*, types::*}; | ||||
| #[cfg(test)] | ||||
| mockall::mock! { | ||||
|     pub TestTcpBackendHandler{} | ||||
|  | ||||
		Loading…
	
		Reference in New Issue
	
	Block a user
	 Valentin Tolmer
						Valentin Tolmer