mirror of
https://github.com/lldap/lldap.git
synced 2026-07-25 17:58:44 +00:00
server: Add support for custom LDAP object classes for users and groups
This commit is contained in:
committed by
nitnelave
parent
fa9743be6a
commit
646fe32645
Generated
+1
@@ -162,6 +162,7 @@ enum AttributeType {
|
|||||||
|
|
||||||
type AttributeList {
|
type AttributeList {
|
||||||
attributes: [AttributeSchema!]!
|
attributes: [AttributeSchema!]!
|
||||||
|
extraLdapObjectClasses: [String!]!
|
||||||
}
|
}
|
||||||
|
|
||||||
type Success {
|
type Success {
|
||||||
|
|||||||
@@ -2,7 +2,8 @@ use crate::domain::{
|
|||||||
error::Result,
|
error::Result,
|
||||||
types::{
|
types::{
|
||||||
AttributeName, AttributeType, AttributeValue, Email, Group, GroupDetails, GroupId,
|
AttributeName, AttributeType, AttributeValue, Email, Group, GroupDetails, GroupId,
|
||||||
GroupName, JpegPhoto, Serialized, User, UserAndGroups, UserColumn, UserId, Uuid,
|
GroupName, JpegPhoto, LdapObjectClass, Serialized, User, UserAndGroups, UserColumn, UserId,
|
||||||
|
Uuid,
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
use async_trait::async_trait;
|
use async_trait::async_trait;
|
||||||
@@ -175,6 +176,8 @@ impl AttributeList {
|
|||||||
pub struct Schema {
|
pub struct Schema {
|
||||||
pub user_attributes: AttributeList,
|
pub user_attributes: AttributeList,
|
||||||
pub group_attributes: AttributeList,
|
pub group_attributes: AttributeList,
|
||||||
|
pub extra_user_object_classes: Vec<LdapObjectClass>,
|
||||||
|
pub extra_group_object_classes: Vec<LdapObjectClass>,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[async_trait]
|
#[async_trait]
|
||||||
@@ -227,6 +230,11 @@ pub trait SchemaBackendHandler: ReadSchemaBackendHandler {
|
|||||||
// Note: It's up to the caller to make sure that the attribute is not hardcoded.
|
// Note: It's up to the caller to make sure that the attribute is not hardcoded.
|
||||||
async fn delete_user_attribute(&self, name: &AttributeName) -> Result<()>;
|
async fn delete_user_attribute(&self, name: &AttributeName) -> Result<()>;
|
||||||
async fn delete_group_attribute(&self, name: &AttributeName) -> Result<()>;
|
async fn delete_group_attribute(&self, name: &AttributeName) -> Result<()>;
|
||||||
|
|
||||||
|
async fn add_user_object_class(&self, name: &LdapObjectClass) -> Result<()>;
|
||||||
|
async fn add_group_object_class(&self, name: &LdapObjectClass) -> Result<()>;
|
||||||
|
async fn delete_user_object_class(&self, name: &LdapObjectClass) -> Result<()>;
|
||||||
|
async fn delete_group_object_class(&self, name: &LdapObjectClass) -> Result<()>;
|
||||||
}
|
}
|
||||||
|
|
||||||
#[async_trait]
|
#[async_trait]
|
||||||
|
|||||||
@@ -30,7 +30,17 @@ pub fn get_group_attribute(
|
|||||||
) -> Option<Vec<Vec<u8>>> {
|
) -> Option<Vec<Vec<u8>>> {
|
||||||
let attribute = AttributeName::from(attribute);
|
let attribute = AttributeName::from(attribute);
|
||||||
let attribute_values = match map_group_field(&attribute, schema) {
|
let attribute_values = match map_group_field(&attribute, schema) {
|
||||||
GroupFieldType::ObjectClass => vec![b"groupOfUniqueNames".to_vec()],
|
GroupFieldType::ObjectClass => {
|
||||||
|
let mut classes = vec![b"groupOfUniqueNames".to_vec()];
|
||||||
|
classes.extend(
|
||||||
|
schema
|
||||||
|
.get_schema()
|
||||||
|
.extra_group_object_classes
|
||||||
|
.iter()
|
||||||
|
.map(|c| c.as_str().as_bytes().to_vec()),
|
||||||
|
);
|
||||||
|
classes
|
||||||
|
}
|
||||||
// Always returned as part of the base response.
|
// Always returned as part of the base response.
|
||||||
GroupFieldType::Dn => return None,
|
GroupFieldType::Dn => return None,
|
||||||
GroupFieldType::EntryDn => {
|
GroupFieldType::EntryDn => {
|
||||||
|
|||||||
@@ -28,12 +28,22 @@ pub fn get_user_attribute(
|
|||||||
) -> Option<Vec<Vec<u8>>> {
|
) -> Option<Vec<Vec<u8>>> {
|
||||||
let attribute = AttributeName::from(attribute);
|
let attribute = AttributeName::from(attribute);
|
||||||
let attribute_values = match map_user_field(&attribute, schema) {
|
let attribute_values = match map_user_field(&attribute, schema) {
|
||||||
UserFieldType::ObjectClass => vec![
|
UserFieldType::ObjectClass => {
|
||||||
b"inetOrgPerson".to_vec(),
|
let mut classes = vec![
|
||||||
b"posixAccount".to_vec(),
|
b"inetOrgPerson".to_vec(),
|
||||||
b"mailAccount".to_vec(),
|
b"posixAccount".to_vec(),
|
||||||
b"person".to_vec(),
|
b"mailAccount".to_vec(),
|
||||||
],
|
b"person".to_vec(),
|
||||||
|
];
|
||||||
|
classes.extend(
|
||||||
|
schema
|
||||||
|
.get_schema()
|
||||||
|
.extra_user_object_classes
|
||||||
|
.iter()
|
||||||
|
.map(|c| c.as_str().as_bytes().to_vec()),
|
||||||
|
);
|
||||||
|
classes
|
||||||
|
}
|
||||||
// dn is always returned as part of the base response.
|
// dn is always returned as part of the base response.
|
||||||
UserFieldType::Dn => return None,
|
UserFieldType::Dn => return None,
|
||||||
UserFieldType::EntryDn => {
|
UserFieldType::EntryDn => {
|
||||||
|
|||||||
@@ -0,0 +1,23 @@
|
|||||||
|
use sea_orm::entity::prelude::*;
|
||||||
|
use serde::{Deserialize, Serialize};
|
||||||
|
|
||||||
|
use crate::domain::types::LdapObjectClass;
|
||||||
|
|
||||||
|
#[derive(Clone, Debug, PartialEq, DeriveEntityModel, Eq, Serialize, Deserialize)]
|
||||||
|
#[sea_orm(table_name = "group_object_classes")]
|
||||||
|
pub struct Model {
|
||||||
|
#[sea_orm(primary_key, auto_increment = false)]
|
||||||
|
pub lower_object_class: String,
|
||||||
|
pub object_class: LdapObjectClass,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Copy, Clone, Debug, EnumIter, DeriveRelation)]
|
||||||
|
pub enum Relation {}
|
||||||
|
|
||||||
|
impl ActiveModelBehavior for ActiveModel {}
|
||||||
|
|
||||||
|
impl From<Model> for LdapObjectClass {
|
||||||
|
fn from(value: Model) -> Self {
|
||||||
|
value.object_class
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,5 +1,3 @@
|
|||||||
//! `SeaORM` Entity. Generated by sea-orm-codegen 0.10.3
|
|
||||||
|
|
||||||
pub mod prelude;
|
pub mod prelude;
|
||||||
|
|
||||||
pub mod groups;
|
pub mod groups;
|
||||||
@@ -11,8 +9,10 @@ pub mod users;
|
|||||||
|
|
||||||
pub mod user_attribute_schema;
|
pub mod user_attribute_schema;
|
||||||
pub mod user_attributes;
|
pub mod user_attributes;
|
||||||
|
pub mod user_object_classes;
|
||||||
|
|
||||||
pub mod group_attribute_schema;
|
pub mod group_attribute_schema;
|
||||||
pub mod group_attributes;
|
pub mod group_attributes;
|
||||||
|
pub mod group_object_classes;
|
||||||
|
|
||||||
pub use prelude::*;
|
pub use prelude::*;
|
||||||
|
|||||||
@@ -4,6 +4,8 @@ pub use super::group_attribute_schema::Column as GroupAttributeSchemaColumn;
|
|||||||
pub use super::group_attribute_schema::Entity as GroupAttributeSchema;
|
pub use super::group_attribute_schema::Entity as GroupAttributeSchema;
|
||||||
pub use super::group_attributes::Column as GroupAttributesColumn;
|
pub use super::group_attributes::Column as GroupAttributesColumn;
|
||||||
pub use super::group_attributes::Entity as GroupAttributes;
|
pub use super::group_attributes::Entity as GroupAttributes;
|
||||||
|
pub use super::group_object_classes::Column as GroupObjectClassesColumn;
|
||||||
|
pub use super::group_object_classes::Entity as GroupObjectClasses;
|
||||||
pub use super::groups::Column as GroupColumn;
|
pub use super::groups::Column as GroupColumn;
|
||||||
pub use super::groups::Entity as Group;
|
pub use super::groups::Entity as Group;
|
||||||
pub use super::jwt_refresh_storage::Column as JwtRefreshStorageColumn;
|
pub use super::jwt_refresh_storage::Column as JwtRefreshStorageColumn;
|
||||||
@@ -18,5 +20,7 @@ pub use super::user_attribute_schema::Column as UserAttributeSchemaColumn;
|
|||||||
pub use super::user_attribute_schema::Entity as UserAttributeSchema;
|
pub use super::user_attribute_schema::Entity as UserAttributeSchema;
|
||||||
pub use super::user_attributes::Column as UserAttributesColumn;
|
pub use super::user_attributes::Column as UserAttributesColumn;
|
||||||
pub use super::user_attributes::Entity as UserAttributes;
|
pub use super::user_attributes::Entity as UserAttributes;
|
||||||
|
pub use super::user_object_classes::Column as UserObjectClassesColumn;
|
||||||
|
pub use super::user_object_classes::Entity as UserObjectClasses;
|
||||||
pub use super::users::Column as UserColumn;
|
pub use super::users::Column as UserColumn;
|
||||||
pub use super::users::Entity as User;
|
pub use super::users::Entity as User;
|
||||||
|
|||||||
@@ -0,0 +1,23 @@
|
|||||||
|
use sea_orm::entity::prelude::*;
|
||||||
|
use serde::{Deserialize, Serialize};
|
||||||
|
|
||||||
|
use crate::domain::types::LdapObjectClass;
|
||||||
|
|
||||||
|
#[derive(Clone, Debug, PartialEq, DeriveEntityModel, Eq, Serialize, Deserialize)]
|
||||||
|
#[sea_orm(table_name = "user_object_classes")]
|
||||||
|
pub struct Model {
|
||||||
|
#[sea_orm(primary_key, auto_increment = false)]
|
||||||
|
pub lower_object_class: String,
|
||||||
|
pub object_class: LdapObjectClass,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Copy, Clone, Debug, EnumIter, DeriveRelation)]
|
||||||
|
pub enum Relation {}
|
||||||
|
|
||||||
|
impl ActiveModelBehavior for ActiveModel {}
|
||||||
|
|
||||||
|
impl From<Model> for LdapObjectClass {
|
||||||
|
fn from(value: Model) -> Self {
|
||||||
|
value.object_class
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -88,6 +88,20 @@ pub enum GroupAttributes {
|
|||||||
GroupAttributeValue,
|
GroupAttributeValue,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[derive(DeriveIden, PartialEq, Eq, Debug, Serialize, Deserialize, Clone, Copy)]
|
||||||
|
pub enum UserObjectClasses {
|
||||||
|
Table,
|
||||||
|
LowerObjectClass,
|
||||||
|
ObjectClass,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(DeriveIden, PartialEq, Eq, Debug, Serialize, Deserialize, Clone, Copy)]
|
||||||
|
pub enum GroupObjectClasses {
|
||||||
|
Table,
|
||||||
|
LowerObjectClass,
|
||||||
|
ObjectClass,
|
||||||
|
}
|
||||||
|
|
||||||
// Metadata about the SQL DB.
|
// Metadata about the SQL DB.
|
||||||
#[derive(DeriveIden)]
|
#[derive(DeriveIden)]
|
||||||
pub enum Metadata {
|
pub enum Metadata {
|
||||||
@@ -1031,6 +1045,51 @@ async fn migrate_to_v8(transaction: DatabaseTransaction) -> Result<DatabaseTrans
|
|||||||
Ok(transaction)
|
Ok(transaction)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
async fn migrate_to_v9(transaction: DatabaseTransaction) -> Result<DatabaseTransaction, DbErr> {
|
||||||
|
let builder = transaction.get_database_backend();
|
||||||
|
transaction
|
||||||
|
.execute(
|
||||||
|
builder.build(
|
||||||
|
Table::create()
|
||||||
|
.table(UserObjectClasses::Table)
|
||||||
|
.if_not_exists()
|
||||||
|
.col(
|
||||||
|
ColumnDef::new(UserObjectClasses::LowerObjectClass)
|
||||||
|
.string_len(255)
|
||||||
|
.not_null()
|
||||||
|
.primary_key(),
|
||||||
|
)
|
||||||
|
.col(
|
||||||
|
ColumnDef::new(UserObjectClasses::ObjectClass)
|
||||||
|
.string_len(255)
|
||||||
|
.not_null(),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
)
|
||||||
|
.await?;
|
||||||
|
transaction
|
||||||
|
.execute(
|
||||||
|
builder.build(
|
||||||
|
Table::create()
|
||||||
|
.table(GroupObjectClasses::Table)
|
||||||
|
.if_not_exists()
|
||||||
|
.col(
|
||||||
|
ColumnDef::new(GroupObjectClasses::LowerObjectClass)
|
||||||
|
.string_len(255)
|
||||||
|
.not_null()
|
||||||
|
.primary_key(),
|
||||||
|
)
|
||||||
|
.col(
|
||||||
|
ColumnDef::new(GroupObjectClasses::ObjectClass)
|
||||||
|
.string_len(255)
|
||||||
|
.not_null(),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
)
|
||||||
|
.await?;
|
||||||
|
Ok(transaction)
|
||||||
|
}
|
||||||
|
|
||||||
// This is needed to make an array of async functions.
|
// This is needed to make an array of async functions.
|
||||||
macro_rules! to_sync {
|
macro_rules! to_sync {
|
||||||
($l:ident) => {
|
($l:ident) => {
|
||||||
@@ -1059,6 +1118,7 @@ pub async fn migrate_from_version(
|
|||||||
to_sync!(migrate_to_v6),
|
to_sync!(migrate_to_v6),
|
||||||
to_sync!(migrate_to_v7),
|
to_sync!(migrate_to_v7),
|
||||||
to_sync!(migrate_to_v8),
|
to_sync!(migrate_to_v8),
|
||||||
|
to_sync!(migrate_to_v9),
|
||||||
];
|
];
|
||||||
assert_eq!(migrations.len(), (LAST_SCHEMA_VERSION.0 - 1) as usize);
|
assert_eq!(migrations.len(), (LAST_SCHEMA_VERSION.0 - 1) as usize);
|
||||||
for migration in 2..=last_version.0 {
|
for migration in 2..=last_version.0 {
|
||||||
|
|||||||
@@ -6,7 +6,7 @@ use crate::domain::{
|
|||||||
},
|
},
|
||||||
model,
|
model,
|
||||||
sql_backend_handler::SqlBackendHandler,
|
sql_backend_handler::SqlBackendHandler,
|
||||||
types::AttributeName,
|
types::{AttributeName, LdapObjectClass},
|
||||||
};
|
};
|
||||||
use async_trait::async_trait;
|
use async_trait::async_trait;
|
||||||
use sea_orm::{
|
use sea_orm::{
|
||||||
@@ -66,6 +66,44 @@ impl SchemaBackendHandler for SqlBackendHandler {
|
|||||||
.await?;
|
.await?;
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
async fn add_user_object_class(&self, name: &LdapObjectClass) -> Result<()> {
|
||||||
|
let mut name_key = name.to_string();
|
||||||
|
name_key.make_ascii_lowercase();
|
||||||
|
model::user_object_classes::ActiveModel {
|
||||||
|
lower_object_class: Set(name_key),
|
||||||
|
object_class: Set(name.clone()),
|
||||||
|
}
|
||||||
|
.insert(&self.sql_pool)
|
||||||
|
.await?;
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
async fn add_group_object_class(&self, name: &LdapObjectClass) -> Result<()> {
|
||||||
|
let mut name_key = name.to_string();
|
||||||
|
name_key.make_ascii_lowercase();
|
||||||
|
model::group_object_classes::ActiveModel {
|
||||||
|
lower_object_class: Set(name_key),
|
||||||
|
object_class: Set(name.clone()),
|
||||||
|
}
|
||||||
|
.insert(&self.sql_pool)
|
||||||
|
.await?;
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
async fn delete_user_object_class(&self, name: &LdapObjectClass) -> Result<()> {
|
||||||
|
model::UserObjectClasses::delete_by_id(name.as_str().to_ascii_lowercase())
|
||||||
|
.exec(&self.sql_pool)
|
||||||
|
.await?;
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
async fn delete_group_object_class(&self, name: &LdapObjectClass) -> Result<()> {
|
||||||
|
model::GroupObjectClasses::delete_by_id(name.as_str().to_ascii_lowercase())
|
||||||
|
.exec(&self.sql_pool)
|
||||||
|
.await?;
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl SqlBackendHandler {
|
impl SqlBackendHandler {
|
||||||
@@ -79,6 +117,8 @@ impl SqlBackendHandler {
|
|||||||
group_attributes: AttributeList {
|
group_attributes: AttributeList {
|
||||||
attributes: Self::get_group_attributes(transaction).await?,
|
attributes: Self::get_group_attributes(transaction).await?,
|
||||||
},
|
},
|
||||||
|
extra_user_object_classes: Self::get_user_object_classes(transaction).await?,
|
||||||
|
extra_group_object_classes: Self::get_group_object_classes(transaction).await?,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -105,6 +145,30 @@ impl SqlBackendHandler {
|
|||||||
.map(|m| m.into())
|
.map(|m| m.into())
|
||||||
.collect())
|
.collect())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
async fn get_user_object_classes(
|
||||||
|
transaction: &DatabaseTransaction,
|
||||||
|
) -> Result<Vec<LdapObjectClass>> {
|
||||||
|
Ok(model::UserObjectClasses::find()
|
||||||
|
.order_by_asc(model::UserObjectClassesColumn::ObjectClass)
|
||||||
|
.all(transaction)
|
||||||
|
.await?
|
||||||
|
.into_iter()
|
||||||
|
.map(Into::into)
|
||||||
|
.collect())
|
||||||
|
}
|
||||||
|
|
||||||
|
async fn get_group_object_classes(
|
||||||
|
transaction: &DatabaseTransaction,
|
||||||
|
) -> Result<Vec<LdapObjectClass>> {
|
||||||
|
Ok(model::GroupObjectClasses::find()
|
||||||
|
.order_by_asc(model::GroupObjectClassesColumn::ObjectClass)
|
||||||
|
.all(transaction)
|
||||||
|
.await?
|
||||||
|
.into_iter()
|
||||||
|
.map(Into::into)
|
||||||
|
.collect())
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
@@ -151,7 +215,9 @@ mod tests {
|
|||||||
},
|
},
|
||||||
group_attributes: AttributeList {
|
group_attributes: AttributeList {
|
||||||
attributes: Vec::new()
|
attributes: Vec::new()
|
||||||
}
|
},
|
||||||
|
extra_user_object_classes: Vec::new(),
|
||||||
|
extra_group_object_classes: Vec::new(),
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
@@ -247,4 +313,50 @@ mod tests {
|
|||||||
.attributes
|
.attributes
|
||||||
.contains(&expected_value));
|
.contains(&expected_value));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[tokio::test]
|
||||||
|
async fn test_user_object_class_add_and_delete() {
|
||||||
|
let fixture = TestFixture::new().await;
|
||||||
|
let new_object_class = LdapObjectClass::new("newObjectClass");
|
||||||
|
fixture
|
||||||
|
.handler
|
||||||
|
.add_user_object_class(&new_object_class)
|
||||||
|
.await
|
||||||
|
.unwrap();
|
||||||
|
assert_eq!(
|
||||||
|
fixture
|
||||||
|
.handler
|
||||||
|
.get_schema()
|
||||||
|
.await
|
||||||
|
.unwrap()
|
||||||
|
.extra_user_object_classes,
|
||||||
|
vec![new_object_class.clone()]
|
||||||
|
);
|
||||||
|
fixture
|
||||||
|
.handler
|
||||||
|
.add_user_object_class(&LdapObjectClass::new("newobjEctclass"))
|
||||||
|
.await
|
||||||
|
.expect_err("Should not be able to add the same object class twice");
|
||||||
|
assert_eq!(
|
||||||
|
fixture
|
||||||
|
.handler
|
||||||
|
.get_schema()
|
||||||
|
.await
|
||||||
|
.unwrap()
|
||||||
|
.extra_user_object_classes,
|
||||||
|
vec![new_object_class.clone()]
|
||||||
|
);
|
||||||
|
fixture
|
||||||
|
.handler
|
||||||
|
.delete_user_object_class(&new_object_class)
|
||||||
|
.await
|
||||||
|
.unwrap();
|
||||||
|
assert!(fixture
|
||||||
|
.handler
|
||||||
|
.get_schema()
|
||||||
|
.await
|
||||||
|
.unwrap()
|
||||||
|
.extra_user_object_classes
|
||||||
|
.is_empty());
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -11,7 +11,7 @@ pub type DbConnection = sea_orm::DatabaseConnection;
|
|||||||
#[derive(Copy, PartialEq, Eq, Debug, Clone, PartialOrd, Ord, DeriveValueType)]
|
#[derive(Copy, PartialEq, Eq, Debug, Clone, PartialOrd, Ord, DeriveValueType)]
|
||||||
pub struct SchemaVersion(pub i16);
|
pub struct SchemaVersion(pub i16);
|
||||||
|
|
||||||
pub const LAST_SCHEMA_VERSION: SchemaVersion = SchemaVersion(8);
|
pub const LAST_SCHEMA_VERSION: SchemaVersion = SchemaVersion(9);
|
||||||
|
|
||||||
#[derive(Copy, PartialEq, Eq, Debug, Clone, PartialOrd, Ord)]
|
#[derive(Copy, PartialEq, Eq, Debug, Clone, PartialOrd, Ord)]
|
||||||
pub struct PrivateKeyHash(pub [u8; 32]);
|
pub struct PrivateKeyHash(pub [u8; 32]);
|
||||||
|
|||||||
@@ -271,6 +271,8 @@ impl TryFromU64 for AttributeName {
|
|||||||
))
|
))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
make_case_insensitive_comparable_string!(LdapObjectClass);
|
||||||
make_case_insensitive_comparable_string!(Email);
|
make_case_insensitive_comparable_string!(Email);
|
||||||
make_case_insensitive_comparable_string!(GroupName);
|
make_case_insensitive_comparable_string!(GroupName);
|
||||||
|
|
||||||
|
|||||||
@@ -7,7 +7,7 @@ use crate::{
|
|||||||
ldap::utils::{map_user_field, UserFieldType},
|
ldap::utils::{map_user_field, UserFieldType},
|
||||||
model::UserColumn,
|
model::UserColumn,
|
||||||
schema::PublicSchema,
|
schema::PublicSchema,
|
||||||
types::{AttributeType, GroupDetails, GroupId, JpegPhoto, UserId},
|
types::{AttributeType, GroupDetails, GroupId, JpegPhoto, LdapObjectClass, UserId},
|
||||||
},
|
},
|
||||||
infra::{
|
infra::{
|
||||||
access_control::{ReadonlyBackendHandler, UserReadableBackendHandler},
|
access_control::{ReadonlyBackendHandler, UserReadableBackendHandler},
|
||||||
@@ -523,26 +523,32 @@ impl<Handler: BackendHandler> From<DomainAttributeSchema> for AttributeSchema<Ha
|
|||||||
|
|
||||||
#[derive(PartialEq, Eq, Debug, Serialize, Deserialize)]
|
#[derive(PartialEq, Eq, Debug, Serialize, Deserialize)]
|
||||||
pub struct AttributeList<Handler: BackendHandler> {
|
pub struct AttributeList<Handler: BackendHandler> {
|
||||||
schema: DomainAttributeList,
|
attributes: DomainAttributeList,
|
||||||
|
extra_classes: Vec<LdapObjectClass>,
|
||||||
_phantom: std::marker::PhantomData<Box<Handler>>,
|
_phantom: std::marker::PhantomData<Box<Handler>>,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[graphql_object(context = Context<Handler>)]
|
#[graphql_object(context = Context<Handler>)]
|
||||||
impl<Handler: BackendHandler> AttributeList<Handler> {
|
impl<Handler: BackendHandler> AttributeList<Handler> {
|
||||||
fn attributes(&self) -> Vec<AttributeSchema<Handler>> {
|
fn attributes(&self) -> Vec<AttributeSchema<Handler>> {
|
||||||
self.schema
|
self.attributes
|
||||||
.attributes
|
.attributes
|
||||||
.clone()
|
.clone()
|
||||||
.into_iter()
|
.into_iter()
|
||||||
.map(Into::into)
|
.map(Into::into)
|
||||||
.collect()
|
.collect()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn extra_ldap_object_classes(&self) -> Vec<String> {
|
||||||
|
self.extra_classes.iter().map(|c| c.to_string()).collect()
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<Handler: BackendHandler> From<DomainAttributeList> for AttributeList<Handler> {
|
impl<Handler: BackendHandler> AttributeList<Handler> {
|
||||||
fn from(value: DomainAttributeList) -> Self {
|
fn new(attributes: DomainAttributeList, extra_classes: Vec<LdapObjectClass>) -> Self {
|
||||||
Self {
|
Self {
|
||||||
schema: value,
|
attributes,
|
||||||
|
extra_classes,
|
||||||
_phantom: std::marker::PhantomData,
|
_phantom: std::marker::PhantomData,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -557,10 +563,16 @@ pub struct Schema<Handler: BackendHandler> {
|
|||||||
#[graphql_object(context = Context<Handler>)]
|
#[graphql_object(context = Context<Handler>)]
|
||||||
impl<Handler: BackendHandler> Schema<Handler> {
|
impl<Handler: BackendHandler> Schema<Handler> {
|
||||||
fn user_schema(&self) -> AttributeList<Handler> {
|
fn user_schema(&self) -> AttributeList<Handler> {
|
||||||
self.schema.get_schema().user_attributes.clone().into()
|
AttributeList::<Handler>::new(
|
||||||
|
self.schema.get_schema().user_attributes.clone(),
|
||||||
|
self.schema.get_schema().extra_user_object_classes.clone(),
|
||||||
|
)
|
||||||
}
|
}
|
||||||
fn group_schema(&self) -> AttributeList<Handler> {
|
fn group_schema(&self) -> AttributeList<Handler> {
|
||||||
self.schema.get_schema().group_attributes.clone().into()
|
AttributeList::<Handler>::new(
|
||||||
|
self.schema.get_schema().group_attributes.clone(),
|
||||||
|
self.schema.get_schema().extra_group_object_classes.clone(),
|
||||||
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -670,7 +682,7 @@ mod tests {
|
|||||||
use crate::{
|
use crate::{
|
||||||
domain::{
|
domain::{
|
||||||
handler::AttributeList,
|
handler::AttributeList,
|
||||||
types::{AttributeName, AttributeType, Serialized},
|
types::{AttributeName, AttributeType, LdapObjectClass, Serialized},
|
||||||
},
|
},
|
||||||
infra::{
|
infra::{
|
||||||
access_control::{Permission, ValidationResults},
|
access_control::{Permission, ValidationResults},
|
||||||
@@ -755,6 +767,11 @@ mod tests {
|
|||||||
is_hardcoded: false,
|
is_hardcoded: false,
|
||||||
}],
|
}],
|
||||||
},
|
},
|
||||||
|
extra_user_object_classes: vec![
|
||||||
|
LdapObjectClass::from("customUserClass"),
|
||||||
|
LdapObjectClass::from("myUserClass"),
|
||||||
|
],
|
||||||
|
extra_group_object_classes: vec![LdapObjectClass::from("customGroupClass")],
|
||||||
})
|
})
|
||||||
});
|
});
|
||||||
mock.expect_get_user_details()
|
mock.expect_get_user_details()
|
||||||
@@ -946,6 +963,7 @@ mod tests {
|
|||||||
isEditable
|
isEditable
|
||||||
isHardcoded
|
isHardcoded
|
||||||
}
|
}
|
||||||
|
extraLdapObjectClasses
|
||||||
}
|
}
|
||||||
groupSchema {
|
groupSchema {
|
||||||
attributes {
|
attributes {
|
||||||
@@ -956,6 +974,7 @@ mod tests {
|
|||||||
isEditable
|
isEditable
|
||||||
isHardcoded
|
isHardcoded
|
||||||
}
|
}
|
||||||
|
extraLdapObjectClasses
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}"#;
|
}"#;
|
||||||
@@ -1040,7 +1059,8 @@ mod tests {
|
|||||||
"isEditable": false,
|
"isEditable": false,
|
||||||
"isHardcoded": true,
|
"isHardcoded": true,
|
||||||
},
|
},
|
||||||
]
|
],
|
||||||
|
"extraLdapObjectClasses": ["customUserClass"],
|
||||||
},
|
},
|
||||||
"groupSchema": {
|
"groupSchema": {
|
||||||
"attributes": [
|
"attributes": [
|
||||||
@@ -1076,7 +1096,8 @@ mod tests {
|
|||||||
"isEditable": false,
|
"isEditable": false,
|
||||||
"isHardcoded": true,
|
"isHardcoded": true,
|
||||||
},
|
},
|
||||||
]
|
],
|
||||||
|
"extraLdapObjectClasses": [],
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}),
|
}),
|
||||||
@@ -1093,6 +1114,7 @@ mod tests {
|
|||||||
attributes {
|
attributes {
|
||||||
name
|
name
|
||||||
}
|
}
|
||||||
|
extraLdapObjectClasses
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}"#;
|
}"#;
|
||||||
@@ -1114,6 +1136,8 @@ mod tests {
|
|||||||
group_attributes: AttributeList {
|
group_attributes: AttributeList {
|
||||||
attributes: Vec::new(),
|
attributes: Vec::new(),
|
||||||
},
|
},
|
||||||
|
extra_user_object_classes: vec![LdapObjectClass::from("customUserClass")],
|
||||||
|
extra_group_object_classes: Vec::new(),
|
||||||
})
|
})
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -1139,7 +1163,8 @@ mod tests {
|
|||||||
{"name": "mail"},
|
{"name": "mail"},
|
||||||
{"name": "user_id"},
|
{"name": "user_id"},
|
||||||
{"name": "uuid"},
|
{"name": "uuid"},
|
||||||
]
|
],
|
||||||
|
"extraLdapObjectClasses": ["customUserClass"],
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} ),
|
} ),
|
||||||
|
|||||||
@@ -1290,7 +1290,8 @@ mod tests {
|
|||||||
b"inetOrgPerson".to_vec(),
|
b"inetOrgPerson".to_vec(),
|
||||||
b"posixAccount".to_vec(),
|
b"posixAccount".to_vec(),
|
||||||
b"mailAccount".to_vec(),
|
b"mailAccount".to_vec(),
|
||||||
b"person".to_vec()
|
b"person".to_vec(),
|
||||||
|
b"customUserClass".to_vec(),
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
LdapPartialAttribute {
|
LdapPartialAttribute {
|
||||||
@@ -1332,7 +1333,8 @@ mod tests {
|
|||||||
b"inetOrgPerson".to_vec(),
|
b"inetOrgPerson".to_vec(),
|
||||||
b"posixAccount".to_vec(),
|
b"posixAccount".to_vec(),
|
||||||
b"mailAccount".to_vec(),
|
b"mailAccount".to_vec(),
|
||||||
b"person".to_vec()
|
b"person".to_vec(),
|
||||||
|
b"customUserClass".to_vec(),
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
LdapPartialAttribute {
|
LdapPartialAttribute {
|
||||||
@@ -1919,7 +1921,8 @@ mod tests {
|
|||||||
b"inetOrgPerson".to_vec(),
|
b"inetOrgPerson".to_vec(),
|
||||||
b"posixAccount".to_vec(),
|
b"posixAccount".to_vec(),
|
||||||
b"mailAccount".to_vec(),
|
b"mailAccount".to_vec(),
|
||||||
b"person".to_vec()
|
b"person".to_vec(),
|
||||||
|
b"customUserClass".to_vec(),
|
||||||
]
|
]
|
||||||
},]
|
},]
|
||||||
}),
|
}),
|
||||||
@@ -1983,7 +1986,8 @@ mod tests {
|
|||||||
b"inetOrgPerson".to_vec(),
|
b"inetOrgPerson".to_vec(),
|
||||||
b"posixAccount".to_vec(),
|
b"posixAccount".to_vec(),
|
||||||
b"mailAccount".to_vec(),
|
b"mailAccount".to_vec(),
|
||||||
b"person".to_vec()
|
b"person".to_vec(),
|
||||||
|
b"customUserClass".to_vec(),
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
LdapPartialAttribute {
|
LdapPartialAttribute {
|
||||||
@@ -2068,6 +2072,7 @@ mod tests {
|
|||||||
b"posixAccount".to_vec(),
|
b"posixAccount".to_vec(),
|
||||||
b"mailAccount".to_vec(),
|
b"mailAccount".to_vec(),
|
||||||
b"person".to_vec(),
|
b"person".to_vec(),
|
||||||
|
b"customUserClass".to_vec(),
|
||||||
],
|
],
|
||||||
},
|
},
|
||||||
LdapPartialAttribute {
|
LdapPartialAttribute {
|
||||||
@@ -2849,6 +2854,11 @@ mod tests {
|
|||||||
is_hardcoded: false,
|
is_hardcoded: false,
|
||||||
}],
|
}],
|
||||||
},
|
},
|
||||||
|
extra_user_object_classes: vec![
|
||||||
|
LdapObjectClass::from("customUserClass"),
|
||||||
|
LdapObjectClass::from("myUserClass"),
|
||||||
|
],
|
||||||
|
extra_group_object_classes: vec![LdapObjectClass::from("customGroupClass")],
|
||||||
})
|
})
|
||||||
});
|
});
|
||||||
let mut ldap_handler = setup_bound_readonly_handler(mock).await;
|
let mut ldap_handler = setup_bound_readonly_handler(mock).await;
|
||||||
|
|||||||
@@ -47,6 +47,10 @@ mockall::mock! {
|
|||||||
async fn add_group_attribute(&self, request: CreateAttributeRequest) -> Result<()>;
|
async fn add_group_attribute(&self, request: CreateAttributeRequest) -> Result<()>;
|
||||||
async fn delete_user_attribute(&self, name: &AttributeName) -> Result<()>;
|
async fn delete_user_attribute(&self, name: &AttributeName) -> Result<()>;
|
||||||
async fn delete_group_attribute(&self, name: &AttributeName) -> Result<()>;
|
async fn delete_group_attribute(&self, name: &AttributeName) -> Result<()>;
|
||||||
|
async fn add_user_object_class(&self, request: &LdapObjectClass) -> Result<()>;
|
||||||
|
async fn add_group_object_class(&self, request: &LdapObjectClass) -> Result<()>;
|
||||||
|
async fn delete_user_object_class(&self, name: &LdapObjectClass) -> Result<()>;
|
||||||
|
async fn delete_group_object_class(&self, name: &LdapObjectClass) -> Result<()>;
|
||||||
}
|
}
|
||||||
#[async_trait]
|
#[async_trait]
|
||||||
impl BackendHandler for TestBackendHandler {}
|
impl BackendHandler for TestBackendHandler {}
|
||||||
@@ -102,6 +106,8 @@ pub fn setup_default_schema(mock: &mut MockTestBackendHandler) {
|
|||||||
group_attributes: AttributeList {
|
group_attributes: AttributeList {
|
||||||
attributes: Vec::new(),
|
attributes: Vec::new(),
|
||||||
},
|
},
|
||||||
|
extra_user_object_classes: vec![LdapObjectClass::from("customUserClass")],
|
||||||
|
extra_group_object_classes: Vec::new(),
|
||||||
})
|
})
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user