server: Use schema to populate attributes
This commit is contained in:
committed by
nitnelave
parent
829ebf59f7
commit
3140af63de
@@ -6,8 +6,9 @@ use tracing::info;
|
||||
use crate::domain::{
|
||||
error::Result,
|
||||
handler::{
|
||||
BackendHandler, CreateUserRequest, GroupListerBackendHandler, GroupRequestFilter,
|
||||
UpdateGroupRequest, UpdateUserRequest, UserListerBackendHandler, UserRequestFilter,
|
||||
BackendHandler, CreateUserRequest, GroupBackendHandler, GroupListerBackendHandler,
|
||||
GroupRequestFilter, Schema, SchemaBackendHandler, UpdateGroupRequest, UpdateUserRequest,
|
||||
UserBackendHandler, UserListerBackendHandler, UserRequestFilter,
|
||||
},
|
||||
types::{Group, GroupDetails, GroupId, User, UserAndGroups, UserId},
|
||||
};
|
||||
@@ -72,6 +73,7 @@ impl ValidationResults {
|
||||
pub trait UserReadableBackendHandler {
|
||||
async fn get_user_details(&self, user_id: &UserId) -> Result<User>;
|
||||
async fn get_user_groups(&self, user_id: &UserId) -> Result<HashSet<GroupDetails>>;
|
||||
async fn get_schema(&self) -> Result<Schema>;
|
||||
}
|
||||
|
||||
#[async_trait]
|
||||
@@ -106,10 +108,13 @@ pub trait AdminBackendHandler:
|
||||
#[async_trait]
|
||||
impl<Handler: BackendHandler> UserReadableBackendHandler for Handler {
|
||||
async fn get_user_details(&self, user_id: &UserId) -> Result<User> {
|
||||
self.get_user_details(user_id).await
|
||||
<Handler as UserBackendHandler>::get_user_details(self, user_id).await
|
||||
}
|
||||
async fn get_user_groups(&self, user_id: &UserId) -> Result<HashSet<GroupDetails>> {
|
||||
self.get_user_groups(user_id).await
|
||||
<Handler as UserBackendHandler>::get_user_groups(self, user_id).await
|
||||
}
|
||||
async fn get_schema(&self) -> Result<Schema> {
|
||||
<Handler as SchemaBackendHandler>::get_schema(self).await
|
||||
}
|
||||
}
|
||||
|
||||
@@ -120,44 +125,44 @@ impl<Handler: BackendHandler> ReadonlyBackendHandler for Handler {
|
||||
filters: Option<UserRequestFilter>,
|
||||
get_groups: bool,
|
||||
) -> Result<Vec<UserAndGroups>> {
|
||||
self.list_users(filters, get_groups).await
|
||||
<Handler as UserListerBackendHandler>::list_users(self, filters, get_groups).await
|
||||
}
|
||||
async fn list_groups(&self, filters: Option<GroupRequestFilter>) -> Result<Vec<Group>> {
|
||||
self.list_groups(filters).await
|
||||
<Handler as GroupListerBackendHandler>::list_groups(self, filters).await
|
||||
}
|
||||
async fn get_group_details(&self, group_id: GroupId) -> Result<GroupDetails> {
|
||||
self.get_group_details(group_id).await
|
||||
<Handler as GroupBackendHandler>::get_group_details(self, group_id).await
|
||||
}
|
||||
}
|
||||
|
||||
#[async_trait]
|
||||
impl<Handler: BackendHandler> UserWriteableBackendHandler for Handler {
|
||||
async fn update_user(&self, request: UpdateUserRequest) -> Result<()> {
|
||||
self.update_user(request).await
|
||||
<Handler as UserBackendHandler>::update_user(self, request).await
|
||||
}
|
||||
}
|
||||
#[async_trait]
|
||||
impl<Handler: BackendHandler> AdminBackendHandler for Handler {
|
||||
async fn create_user(&self, request: CreateUserRequest) -> Result<()> {
|
||||
self.create_user(request).await
|
||||
<Handler as UserBackendHandler>::create_user(self, request).await
|
||||
}
|
||||
async fn delete_user(&self, user_id: &UserId) -> Result<()> {
|
||||
self.delete_user(user_id).await
|
||||
<Handler as UserBackendHandler>::delete_user(self, user_id).await
|
||||
}
|
||||
async fn add_user_to_group(&self, user_id: &UserId, group_id: GroupId) -> Result<()> {
|
||||
self.add_user_to_group(user_id, group_id).await
|
||||
<Handler as UserBackendHandler>::add_user_to_group(self, user_id, group_id).await
|
||||
}
|
||||
async fn remove_user_from_group(&self, user_id: &UserId, group_id: GroupId) -> Result<()> {
|
||||
self.remove_user_from_group(user_id, group_id).await
|
||||
<Handler as UserBackendHandler>::remove_user_from_group(self, user_id, group_id).await
|
||||
}
|
||||
async fn update_group(&self, request: UpdateGroupRequest) -> Result<()> {
|
||||
self.update_group(request).await
|
||||
<Handler as GroupBackendHandler>::update_group(self, request).await
|
||||
}
|
||||
async fn create_group(&self, group_name: &str) -> Result<GroupId> {
|
||||
self.create_group(group_name).await
|
||||
<Handler as GroupBackendHandler>::create_group(self, group_name).await
|
||||
}
|
||||
async fn delete_group(&self, group_id: GroupId) -> Result<()> {
|
||||
self.delete_group(group_id).await
|
||||
<Handler as GroupBackendHandler>::delete_group(self, group_id).await
|
||||
}
|
||||
}
|
||||
|
||||
@@ -262,6 +267,15 @@ pub struct UserRestrictedListerBackendHandler<'a, Handler> {
|
||||
pub user_filter: Option<UserId>,
|
||||
}
|
||||
|
||||
#[async_trait]
|
||||
impl<'a, Handler: SchemaBackendHandler + Sync> SchemaBackendHandler
|
||||
for UserRestrictedListerBackendHandler<'a, Handler>
|
||||
{
|
||||
async fn get_schema(&self) -> Result<Schema> {
|
||||
self.handler.get_schema().await
|
||||
}
|
||||
}
|
||||
|
||||
#[async_trait]
|
||||
impl<'a, Handler: UserListerBackendHandler + Sync> UserListerBackendHandler
|
||||
for UserRestrictedListerBackendHandler<'a, Handler>
|
||||
|
||||
@@ -2,7 +2,7 @@ use crate::{
|
||||
domain::{
|
||||
handler::BackendHandler,
|
||||
ldap::utils::{map_user_field, UserFieldType},
|
||||
types::{GroupDetails, GroupId, UserColumn, UserId},
|
||||
types::{GroupDetails, GroupId, JpegPhoto, UserColumn, UserId},
|
||||
},
|
||||
infra::{
|
||||
access_control::{ReadonlyBackendHandler, UserReadableBackendHandler},
|
||||
@@ -236,15 +236,29 @@ impl<Handler: BackendHandler> User<Handler> {
|
||||
}
|
||||
|
||||
fn first_name(&self) -> &str {
|
||||
self.user.first_name.as_deref().unwrap_or("")
|
||||
self.user
|
||||
.attributes
|
||||
.iter()
|
||||
.find(|a| a.name == "first_name")
|
||||
.map(|a| a.value.unwrap())
|
||||
.unwrap_or("")
|
||||
}
|
||||
|
||||
fn last_name(&self) -> &str {
|
||||
self.user.last_name.as_deref().unwrap_or("")
|
||||
self.user
|
||||
.attributes
|
||||
.iter()
|
||||
.find(|a| a.name == "last_name")
|
||||
.map(|a| a.value.unwrap())
|
||||
.unwrap_or("")
|
||||
}
|
||||
|
||||
fn avatar(&self) -> Option<String> {
|
||||
self.user.avatar.as_ref().map(String::from)
|
||||
self.user
|
||||
.attributes
|
||||
.iter()
|
||||
.find(|a| a.name == "avatar")
|
||||
.map(|a| String::from(&a.value.unwrap::<JpegPhoto>()))
|
||||
}
|
||||
|
||||
fn creation_date(&self) -> chrono::DateTime<chrono::Utc> {
|
||||
|
||||
@@ -1,6 +1,8 @@
|
||||
use crate::{
|
||||
domain::{
|
||||
handler::{BackendHandler, BindRequest, CreateUserRequest, LoginHandler},
|
||||
handler::{
|
||||
BackendHandler, BindRequest, CreateUserRequest, LoginHandler, SchemaBackendHandler,
|
||||
},
|
||||
ldap::{
|
||||
error::{LdapError, LdapResult},
|
||||
group::{convert_groups_to_ldap_op, get_groups_list},
|
||||
@@ -467,12 +469,17 @@ impl<Backend: BackendHandler + LoginHandler + OpaqueHandler> LdapHandler<Backend
|
||||
.get_user_restricted_lister_handler(user_info);
|
||||
let (users, groups) = self.do_search_internal(&backend_handler, request).await?;
|
||||
|
||||
let schema = backend_handler.get_schema().await.map_err(|e| LdapError {
|
||||
code: LdapResultCode::OperationsError,
|
||||
message: format!("Unable to get schema: {:#}", e),
|
||||
})?;
|
||||
let mut results = Vec::new();
|
||||
if let Some(users) = users {
|
||||
results.extend(convert_users_to_ldap_op(
|
||||
users,
|
||||
&request.attrs,
|
||||
&self.ldap_info,
|
||||
&schema,
|
||||
));
|
||||
}
|
||||
if let Some(groups) = groups {
|
||||
@@ -769,6 +776,7 @@ mod tests {
|
||||
});
|
||||
Ok(set)
|
||||
});
|
||||
setup_default_schema(&mut mock);
|
||||
let mut ldap_handler = LdapHandler::new_for_tests(mock, "dc=Example,dc=com");
|
||||
let request = LdapBindRequest {
|
||||
dn: "uid=test,ou=people,dc=example,dc=coM".to_string(),
|
||||
@@ -799,6 +807,44 @@ mod tests {
|
||||
setup_bound_handler_with_group(mock, "lldap_admin").await
|
||||
}
|
||||
|
||||
fn setup_default_schema(mock: &mut MockTestBackendHandler) {
|
||||
mock.expect_get_schema().returning(|| {
|
||||
Ok(Schema {
|
||||
user_attributes: AttributeList {
|
||||
attributes: vec![
|
||||
AttributeSchema {
|
||||
name: "avatar".to_owned(),
|
||||
attribute_type: AttributeType::JpegPhoto,
|
||||
is_list: false,
|
||||
is_visible: true,
|
||||
is_editable: true,
|
||||
is_hardcoded: true,
|
||||
},
|
||||
AttributeSchema {
|
||||
name: "first_name".to_owned(),
|
||||
attribute_type: AttributeType::String,
|
||||
is_list: false,
|
||||
is_visible: true,
|
||||
is_editable: true,
|
||||
is_hardcoded: true,
|
||||
},
|
||||
AttributeSchema {
|
||||
name: "last_name".to_owned(),
|
||||
attribute_type: AttributeType::String,
|
||||
is_list: false,
|
||||
is_visible: true,
|
||||
is_editable: true,
|
||||
is_hardcoded: true,
|
||||
},
|
||||
],
|
||||
},
|
||||
group_attributes: AttributeList {
|
||||
attributes: Vec::new(),
|
||||
},
|
||||
})
|
||||
});
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
async fn test_bind() {
|
||||
let mut mock = MockTestBackendHandler::new();
|
||||
@@ -1083,9 +1129,17 @@ mod tests {
|
||||
user_id: UserId::new("bob_1"),
|
||||
email: "bob@bobmail.bob".to_string(),
|
||||
display_name: Some("Bôb Böbberson".to_string()),
|
||||
first_name: Some("Bôb".to_string()),
|
||||
last_name: Some("Böbberson".to_string()),
|
||||
uuid: uuid!("698e1d5f-7a40-3151-8745-b9b8a37839da"),
|
||||
attributes: vec![
|
||||
AttributeValue {
|
||||
name: "first_name".to_owned(),
|
||||
value: Serialized::from("Bôb"),
|
||||
},
|
||||
AttributeValue {
|
||||
name: "last_name".to_owned(),
|
||||
value: Serialized::from("Böbberson"),
|
||||
},
|
||||
],
|
||||
..Default::default()
|
||||
},
|
||||
groups: None,
|
||||
@@ -1095,9 +1149,20 @@ mod tests {
|
||||
user_id: UserId::new("jim"),
|
||||
email: "jim@cricket.jim".to_string(),
|
||||
display_name: Some("Jimminy Cricket".to_string()),
|
||||
first_name: Some("Jim".to_string()),
|
||||
last_name: Some("Cricket".to_string()),
|
||||
avatar: Some(JpegPhoto::for_tests()),
|
||||
attributes: vec![
|
||||
AttributeValue {
|
||||
name: "avatar".to_owned(),
|
||||
value: Serialized::from(&JpegPhoto::for_tests()),
|
||||
},
|
||||
AttributeValue {
|
||||
name: "first_name".to_owned(),
|
||||
value: Serialized::from("Jim"),
|
||||
},
|
||||
AttributeValue {
|
||||
name: "last_name".to_owned(),
|
||||
value: Serialized::from("Cricket"),
|
||||
},
|
||||
],
|
||||
uuid: uuid!("04ac75e0-2900-3e21-926c-2f732c26b3fc"),
|
||||
creation_date: Utc
|
||||
.with_ymd_and_hms(2014, 7, 8, 9, 10, 11)
|
||||
@@ -1746,8 +1811,16 @@ mod tests {
|
||||
user_id: UserId::new("bob_1"),
|
||||
email: "bob@bobmail.bob".to_string(),
|
||||
display_name: Some("Bôb Böbberson".to_string()),
|
||||
first_name: Some("Bôb".to_string()),
|
||||
last_name: Some("Böbberson".to_string()),
|
||||
attributes: vec![
|
||||
AttributeValue {
|
||||
name: "first_name".to_owned(),
|
||||
value: Serialized::from("Bôb"),
|
||||
},
|
||||
AttributeValue {
|
||||
name: "last_name".to_owned(),
|
||||
value: Serialized::from("Böbberson"),
|
||||
},
|
||||
],
|
||||
..Default::default()
|
||||
},
|
||||
groups: None,
|
||||
@@ -1820,8 +1893,16 @@ mod tests {
|
||||
user_id: UserId::new("bob_1"),
|
||||
email: "bob@bobmail.bob".to_string(),
|
||||
display_name: Some("Bôb Böbberson".to_string()),
|
||||
last_name: Some("Böbberson".to_string()),
|
||||
avatar: Some(JpegPhoto::for_tests()),
|
||||
attributes: vec![
|
||||
AttributeValue {
|
||||
name: "avatar".to_owned(),
|
||||
value: Serialized::from(&JpegPhoto::for_tests()),
|
||||
},
|
||||
AttributeValue {
|
||||
name: "last_name".to_owned(),
|
||||
value: Serialized::from("Böbberson"),
|
||||
},
|
||||
],
|
||||
uuid: uuid!("b4ac75e0-2900-3e21-926c-2f732c26b3fc"),
|
||||
..Default::default()
|
||||
},
|
||||
|
||||
Reference in New Issue
Block a user