mirror of
https://github.com/lldap/lldap.git
synced 2026-04-05 14:48:10 +00:00
server: split off create_* from ldap_handler
This commit is contained in:
committed by
nitnelave
parent
52f22c00c3
commit
6a2a5fe7f5
258
server/src/infra/ldap/create.rs
Normal file
258
server/src/infra/ldap/create.rs
Normal file
@@ -0,0 +1,258 @@
|
||||
use crate::{
|
||||
domain::{
|
||||
deserialize,
|
||||
ldap::{
|
||||
error::{LdapError, LdapResult},
|
||||
utils::{LdapInfo, UserOrGroupName, get_user_or_group_id_from_distinguished_name},
|
||||
},
|
||||
},
|
||||
infra::{access_control::AdminBackendHandler, ldap::handler::make_add_error},
|
||||
};
|
||||
use ldap3_proto::proto::{
|
||||
LdapAddRequest, LdapAttribute, LdapOp, LdapPartialAttribute, LdapResultCode,
|
||||
};
|
||||
use lldap_domain::{
|
||||
requests::{CreateGroupRequest, CreateUserRequest},
|
||||
types::{Attribute, AttributeName, AttributeType, Email, GroupName, UserId},
|
||||
};
|
||||
use std::collections::HashMap;
|
||||
use tracing::instrument;
|
||||
|
||||
#[instrument(skip_all, level = "debug")]
|
||||
pub(crate) async fn create_user_or_group(
|
||||
backend_handler: &impl AdminBackendHandler,
|
||||
ldap_info: &LdapInfo,
|
||||
request: LdapAddRequest,
|
||||
) -> LdapResult<Vec<LdapOp>> {
|
||||
let base_dn_str = &ldap_info.base_dn_str;
|
||||
match get_user_or_group_id_from_distinguished_name(&request.dn, &ldap_info.base_dn) {
|
||||
UserOrGroupName::User(user_id) => {
|
||||
create_user(backend_handler, user_id, request.attributes).await
|
||||
}
|
||||
UserOrGroupName::Group(group_name) => {
|
||||
create_group(backend_handler, group_name, request.attributes).await
|
||||
}
|
||||
err => Err(err.into_ldap_error(
|
||||
&request.dn,
|
||||
format!(
|
||||
r#""uid=id,ou=people,{}" or "uid=id,ou=groups,{}""#,
|
||||
base_dn_str, base_dn_str
|
||||
),
|
||||
)),
|
||||
}
|
||||
}
|
||||
|
||||
#[instrument(skip_all, level = "debug")]
|
||||
async fn create_user(
|
||||
backend_handler: &impl AdminBackendHandler,
|
||||
user_id: UserId,
|
||||
attributes: Vec<LdapAttribute>,
|
||||
) -> LdapResult<Vec<LdapOp>> {
|
||||
fn parse_attribute(mut attr: LdapPartialAttribute) -> LdapResult<(String, Vec<u8>)> {
|
||||
if attr.vals.len() > 1 {
|
||||
Err(LdapError {
|
||||
code: LdapResultCode::ConstraintViolation,
|
||||
message: format!("Expected a single value for attribute {}", attr.atype),
|
||||
})
|
||||
} else {
|
||||
attr.atype.make_ascii_lowercase();
|
||||
match attr.vals.pop() {
|
||||
Some(val) => Ok((attr.atype, val)),
|
||||
None => Err(LdapError {
|
||||
code: LdapResultCode::ConstraintViolation,
|
||||
message: format!("Missing value for attribute {}", attr.atype),
|
||||
}),
|
||||
}
|
||||
}
|
||||
}
|
||||
let attributes: HashMap<String, Vec<u8>> = attributes
|
||||
.into_iter()
|
||||
.filter(|a| !a.atype.eq_ignore_ascii_case("objectclass"))
|
||||
.map(parse_attribute)
|
||||
.collect::<LdapResult<_>>()?;
|
||||
fn decode_attribute_value(val: &[u8]) -> LdapResult<String> {
|
||||
std::str::from_utf8(val)
|
||||
.map_err(|e| LdapError {
|
||||
code: LdapResultCode::ConstraintViolation,
|
||||
message: format!(
|
||||
"Attribute value is invalid UTF-8: {:#?} (value {:?})",
|
||||
e, val
|
||||
),
|
||||
})
|
||||
.map(str::to_owned)
|
||||
}
|
||||
let get_attribute = |name| {
|
||||
attributes
|
||||
.get(name)
|
||||
.map(Vec::as_slice)
|
||||
.map(decode_attribute_value)
|
||||
};
|
||||
let make_encoded_attribute = |name: &str, typ: AttributeType, value: String| {
|
||||
Ok(Attribute {
|
||||
name: AttributeName::from(name),
|
||||
value: deserialize::deserialize_attribute_value(&[value], typ, false).map_err(|e| {
|
||||
LdapError {
|
||||
code: LdapResultCode::ConstraintViolation,
|
||||
message: format!("Invalid attribute value: {}", e),
|
||||
}
|
||||
})?,
|
||||
})
|
||||
};
|
||||
let mut new_user_attributes: Vec<Attribute> = Vec::new();
|
||||
if let Some(first_name) = get_attribute("givenname").transpose()? {
|
||||
new_user_attributes.push(make_encoded_attribute(
|
||||
"first_name",
|
||||
AttributeType::String,
|
||||
first_name,
|
||||
)?);
|
||||
}
|
||||
if let Some(last_name) = get_attribute("sn").transpose()? {
|
||||
new_user_attributes.push(make_encoded_attribute(
|
||||
"last_name",
|
||||
AttributeType::String,
|
||||
last_name,
|
||||
)?);
|
||||
}
|
||||
if let Some(avatar) = get_attribute("avatar").transpose()? {
|
||||
new_user_attributes.push(make_encoded_attribute(
|
||||
"avatar",
|
||||
AttributeType::JpegPhoto,
|
||||
avatar,
|
||||
)?);
|
||||
}
|
||||
backend_handler
|
||||
.create_user(CreateUserRequest {
|
||||
user_id,
|
||||
email: Email::from(
|
||||
get_attribute("mail")
|
||||
.or_else(|| get_attribute("email"))
|
||||
.transpose()?
|
||||
.unwrap_or_default(),
|
||||
),
|
||||
display_name: get_attribute("cn").transpose()?,
|
||||
attributes: new_user_attributes,
|
||||
})
|
||||
.await
|
||||
.map_err(|e| LdapError {
|
||||
code: LdapResultCode::OperationsError,
|
||||
message: format!("Could not create user: {:#?}", e),
|
||||
})?;
|
||||
Ok(vec![make_add_error(LdapResultCode::Success, String::new())])
|
||||
}
|
||||
|
||||
#[instrument(skip_all, level = "debug")]
|
||||
async fn create_group(
|
||||
backend_handler: &impl AdminBackendHandler,
|
||||
group_name: GroupName,
|
||||
_attributes: Vec<LdapAttribute>,
|
||||
) -> LdapResult<Vec<LdapOp>> {
|
||||
backend_handler
|
||||
.create_group(CreateGroupRequest {
|
||||
display_name: group_name,
|
||||
attributes: Vec::new(),
|
||||
})
|
||||
.await
|
||||
.map_err(|e| LdapError {
|
||||
code: LdapResultCode::OperationsError,
|
||||
message: format!("Could not create group: {:#?}", e),
|
||||
})?;
|
||||
Ok(vec![make_add_error(LdapResultCode::Success, String::new())])
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
use crate::infra::{
|
||||
ldap::handler::tests::setup_bound_admin_handler, test_utils::MockTestBackendHandler,
|
||||
};
|
||||
use lldap_domain::types::*;
|
||||
use mockall::predicate::eq;
|
||||
use pretty_assertions::assert_eq;
|
||||
use tokio;
|
||||
|
||||
#[tokio::test]
|
||||
async fn test_create_user() {
|
||||
let mut mock = MockTestBackendHandler::new();
|
||||
mock.expect_create_user()
|
||||
.with(eq(CreateUserRequest {
|
||||
user_id: UserId::new("bob"),
|
||||
email: "".into(),
|
||||
display_name: Some("Bob".to_string()),
|
||||
..Default::default()
|
||||
}))
|
||||
.times(1)
|
||||
.return_once(|_| Ok(()));
|
||||
let ldap_handler = setup_bound_admin_handler(mock).await;
|
||||
let request = LdapAddRequest {
|
||||
dn: "uid=bob,ou=people,dc=example,dc=com".to_owned(),
|
||||
attributes: vec![LdapPartialAttribute {
|
||||
atype: "cn".to_owned(),
|
||||
vals: vec![b"Bob".to_vec()],
|
||||
}],
|
||||
};
|
||||
assert_eq!(
|
||||
ldap_handler.create_user_or_group(request).await,
|
||||
Ok(vec![make_add_error(LdapResultCode::Success, String::new())])
|
||||
);
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
async fn test_create_group() {
|
||||
let mut mock = MockTestBackendHandler::new();
|
||||
mock.expect_create_group()
|
||||
.with(eq(CreateGroupRequest {
|
||||
display_name: GroupName::new("bob"),
|
||||
..Default::default()
|
||||
}))
|
||||
.times(1)
|
||||
.return_once(|_| Ok(GroupId(5)));
|
||||
let ldap_handler = setup_bound_admin_handler(mock).await;
|
||||
let request = LdapAddRequest {
|
||||
dn: "uid=bob,ou=groups,dc=example,dc=com".to_owned(),
|
||||
attributes: vec![LdapPartialAttribute {
|
||||
atype: "cn".to_owned(),
|
||||
vals: vec![b"Bobby".to_vec()],
|
||||
}],
|
||||
};
|
||||
assert_eq!(
|
||||
ldap_handler.create_user_or_group(request).await,
|
||||
Ok(vec![make_add_error(LdapResultCode::Success, String::new())])
|
||||
);
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
async fn test_create_user_multiple_object_class() {
|
||||
let mut mock = MockTestBackendHandler::new();
|
||||
mock.expect_create_user()
|
||||
.with(eq(CreateUserRequest {
|
||||
user_id: UserId::new("bob"),
|
||||
email: "".into(),
|
||||
display_name: Some("Bob".to_string()),
|
||||
..Default::default()
|
||||
}))
|
||||
.times(1)
|
||||
.return_once(|_| Ok(()));
|
||||
let ldap_handler = setup_bound_admin_handler(mock).await;
|
||||
let request = LdapAddRequest {
|
||||
dn: "uid=bob,ou=people,dc=example,dc=com".to_owned(),
|
||||
attributes: vec![
|
||||
LdapPartialAttribute {
|
||||
atype: "cn".to_owned(),
|
||||
vals: vec![b"Bob".to_vec()],
|
||||
},
|
||||
LdapPartialAttribute {
|
||||
atype: "objectClass".to_owned(),
|
||||
vals: vec![
|
||||
b"top".to_vec(),
|
||||
b"person".to_vec(),
|
||||
b"inetOrgPerson".to_vec(),
|
||||
],
|
||||
},
|
||||
],
|
||||
};
|
||||
assert_eq!(
|
||||
ldap_handler.create_user_or_group(request).await,
|
||||
Ok(vec![make_add_error(LdapResultCode::Success, String::new())])
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -1,18 +1,16 @@
|
||||
use crate::{
|
||||
domain::{
|
||||
deserialize,
|
||||
ldap::{
|
||||
error::{LdapError, LdapResult},
|
||||
utils::{
|
||||
LdapInfo, UserOrGroupName, get_user_id_from_distinguished_name,
|
||||
get_user_or_group_id_from_distinguished_name, parse_distinguished_name,
|
||||
LdapInfo, get_user_id_from_distinguished_name, parse_distinguished_name,
|
||||
},
|
||||
},
|
||||
opaque_handler::OpaqueHandler,
|
||||
},
|
||||
infra::{
|
||||
access_control::{
|
||||
AccessControlledBackendHandler, AdminBackendHandler, UserReadableBackendHandler,
|
||||
AccessControlledBackendHandler, UserReadableBackendHandler,
|
||||
},
|
||||
ldap::{
|
||||
password::{self, do_password_modification},
|
||||
@@ -21,25 +19,22 @@ use crate::{
|
||||
make_search_success, root_dse_response,
|
||||
},
|
||||
compare,
|
||||
create,
|
||||
},
|
||||
},
|
||||
};
|
||||
use ldap3_proto::proto::{
|
||||
LdapAddRequest, LdapAttribute, LdapBindRequest, LdapBindResponse, LdapCompareRequest,
|
||||
LdapAddRequest, LdapBindRequest, LdapBindResponse, LdapCompareRequest,
|
||||
LdapExtendedRequest, LdapExtendedResponse, LdapFilter, LdapModify, LdapModifyRequest,
|
||||
LdapModifyType, LdapOp, LdapPartialAttribute, LdapPasswordModifyRequest,
|
||||
LdapModifyType, LdapOp, LdapPasswordModifyRequest,
|
||||
LdapResult as LdapResultOp, LdapResultCode, LdapSearchRequest, OID_PASSWORD_MODIFY, OID_WHOAMI,
|
||||
};
|
||||
use lldap_auth::access_control::ValidationResults;
|
||||
use lldap_domain::{
|
||||
requests::{CreateGroupRequest, CreateUserRequest},
|
||||
types::{Attribute, AttributeName, AttributeType, Email, GroupName, UserId},
|
||||
};
|
||||
use lldap_domain::types::{AttributeName, UserId};
|
||||
use lldap_domain_handlers::handler::{BackendHandler, LoginHandler};
|
||||
use std::collections::HashMap;
|
||||
use tracing::{debug, instrument};
|
||||
|
||||
fn make_add_error(code: LdapResultCode, message: String) -> LdapOp {
|
||||
pub(crate) fn make_add_error(code: LdapResultCode, message: String) -> LdapOp {
|
||||
LdapOp::AddResponse(LdapResultOp {
|
||||
code,
|
||||
matcheddn: "".to_string(),
|
||||
@@ -338,7 +333,7 @@ impl<Backend: BackendHandler + LoginHandler + OpaqueHandler> LdapHandler<Backend
|
||||
}
|
||||
|
||||
#[instrument(skip_all, level = "debug")]
|
||||
async fn do_create_user_or_group(&self, request: LdapAddRequest) -> LdapResult<Vec<LdapOp>> {
|
||||
pub async fn create_user_or_group(&self, request: LdapAddRequest) -> LdapResult<Vec<LdapOp>> {
|
||||
let backend_handler = self
|
||||
.user_info
|
||||
.as_ref()
|
||||
@@ -347,143 +342,7 @@ impl<Backend: BackendHandler + LoginHandler + OpaqueHandler> LdapHandler<Backend
|
||||
code: LdapResultCode::InsufficentAccessRights,
|
||||
message: "Unauthorized write".to_string(),
|
||||
})?;
|
||||
let base_dn_str = &self.ldap_info.base_dn_str;
|
||||
match get_user_or_group_id_from_distinguished_name(&request.dn, &self.ldap_info.base_dn) {
|
||||
UserOrGroupName::User(user_id) => {
|
||||
self.do_create_user(backend_handler, user_id, request.attributes)
|
||||
.await
|
||||
}
|
||||
UserOrGroupName::Group(group_name) => {
|
||||
self.do_create_group(backend_handler, group_name, request.attributes)
|
||||
.await
|
||||
}
|
||||
err => Err(err.into_ldap_error(
|
||||
&request.dn,
|
||||
format!(
|
||||
r#""uid=id,ou=people,{}" or "uid=id,ou=groups,{}""#,
|
||||
base_dn_str, base_dn_str
|
||||
),
|
||||
)),
|
||||
}
|
||||
}
|
||||
|
||||
#[instrument(skip_all, level = "debug")]
|
||||
async fn do_create_user(
|
||||
&self,
|
||||
backend_handler: &impl AdminBackendHandler,
|
||||
user_id: UserId,
|
||||
attributes: Vec<LdapAttribute>,
|
||||
) -> LdapResult<Vec<LdapOp>> {
|
||||
fn parse_attribute(mut attr: LdapPartialAttribute) -> LdapResult<(String, Vec<u8>)> {
|
||||
if attr.vals.len() > 1 {
|
||||
Err(LdapError {
|
||||
code: LdapResultCode::ConstraintViolation,
|
||||
message: format!("Expected a single value for attribute {}", attr.atype),
|
||||
})
|
||||
} else {
|
||||
attr.atype.make_ascii_lowercase();
|
||||
match attr.vals.pop() {
|
||||
Some(val) => Ok((attr.atype, val)),
|
||||
None => Err(LdapError {
|
||||
code: LdapResultCode::ConstraintViolation,
|
||||
message: format!("Missing value for attribute {}", attr.atype),
|
||||
}),
|
||||
}
|
||||
}
|
||||
}
|
||||
let attributes: HashMap<String, Vec<u8>> = attributes
|
||||
.into_iter()
|
||||
.filter(|a| !a.atype.eq_ignore_ascii_case("objectclass"))
|
||||
.map(parse_attribute)
|
||||
.collect::<LdapResult<_>>()?;
|
||||
fn decode_attribute_value(val: &[u8]) -> LdapResult<String> {
|
||||
std::str::from_utf8(val)
|
||||
.map_err(|e| LdapError {
|
||||
code: LdapResultCode::ConstraintViolation,
|
||||
message: format!(
|
||||
"Attribute value is invalid UTF-8: {:#?} (value {:?})",
|
||||
e, val
|
||||
),
|
||||
})
|
||||
.map(str::to_owned)
|
||||
}
|
||||
let get_attribute = |name| {
|
||||
attributes
|
||||
.get(name)
|
||||
.map(Vec::as_slice)
|
||||
.map(decode_attribute_value)
|
||||
};
|
||||
let make_encoded_attribute = |name: &str, typ: AttributeType, value: String| {
|
||||
Ok(Attribute {
|
||||
name: AttributeName::from(name),
|
||||
value: deserialize::deserialize_attribute_value(&[value], typ, false).map_err(
|
||||
|e| LdapError {
|
||||
code: LdapResultCode::ConstraintViolation,
|
||||
message: format!("Invalid attribute value: {}", e),
|
||||
},
|
||||
)?,
|
||||
})
|
||||
};
|
||||
let mut new_user_attributes: Vec<Attribute> = Vec::new();
|
||||
if let Some(first_name) = get_attribute("givenname").transpose()? {
|
||||
new_user_attributes.push(make_encoded_attribute(
|
||||
"first_name",
|
||||
AttributeType::String,
|
||||
first_name,
|
||||
)?);
|
||||
}
|
||||
if let Some(last_name) = get_attribute("sn").transpose()? {
|
||||
new_user_attributes.push(make_encoded_attribute(
|
||||
"last_name",
|
||||
AttributeType::String,
|
||||
last_name,
|
||||
)?);
|
||||
}
|
||||
if let Some(avatar) = get_attribute("avatar").transpose()? {
|
||||
new_user_attributes.push(make_encoded_attribute(
|
||||
"avatar",
|
||||
AttributeType::JpegPhoto,
|
||||
avatar,
|
||||
)?);
|
||||
}
|
||||
backend_handler
|
||||
.create_user(CreateUserRequest {
|
||||
user_id,
|
||||
email: Email::from(
|
||||
get_attribute("mail")
|
||||
.or_else(|| get_attribute("email"))
|
||||
.transpose()?
|
||||
.unwrap_or_default(),
|
||||
),
|
||||
display_name: get_attribute("cn").transpose()?,
|
||||
attributes: new_user_attributes,
|
||||
})
|
||||
.await
|
||||
.map_err(|e| LdapError {
|
||||
code: LdapResultCode::OperationsError,
|
||||
message: format!("Could not create user: {:#?}", e),
|
||||
})?;
|
||||
Ok(vec![make_add_error(LdapResultCode::Success, String::new())])
|
||||
}
|
||||
|
||||
#[instrument(skip_all, level = "debug")]
|
||||
async fn do_create_group(
|
||||
&self,
|
||||
backend_handler: &impl AdminBackendHandler,
|
||||
group_name: GroupName,
|
||||
_attributes: Vec<LdapAttribute>,
|
||||
) -> LdapResult<Vec<LdapOp>> {
|
||||
backend_handler
|
||||
.create_group(CreateGroupRequest {
|
||||
display_name: group_name,
|
||||
attributes: Vec::new(),
|
||||
})
|
||||
.await
|
||||
.map_err(|e| LdapError {
|
||||
code: LdapResultCode::OperationsError,
|
||||
message: format!("Could not create group: {:#?}", e),
|
||||
})?;
|
||||
Ok(vec![make_add_error(LdapResultCode::Success, String::new())])
|
||||
create::create_user_or_group(backend_handler, &self.ldap_info, request).await
|
||||
}
|
||||
|
||||
#[instrument(skip_all, level = "debug")]
|
||||
@@ -518,7 +377,7 @@ impl<Backend: BackendHandler + LoginHandler + OpaqueHandler> LdapHandler<Backend
|
||||
LdapOp::ModifyRequest(request) => self.do_modify_request(&request).await,
|
||||
LdapOp::ExtendedRequest(request) => self.do_extended_request(&request).await,
|
||||
LdapOp::AddRequest(request) => self
|
||||
.do_create_user_or_group(request)
|
||||
.create_user_or_group(request)
|
||||
.await
|
||||
.unwrap_or_else(|e: LdapError| vec![make_add_error(e.code, e.message)]),
|
||||
LdapOp::CompareRequest(request) => self
|
||||
@@ -642,91 +501,4 @@ pub mod tests {
|
||||
)])
|
||||
);
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
async fn test_create_user() {
|
||||
let mut mock = MockTestBackendHandler::new();
|
||||
mock.expect_create_user()
|
||||
.with(eq(CreateUserRequest {
|
||||
user_id: UserId::new("bob"),
|
||||
email: "".into(),
|
||||
display_name: Some("Bob".to_string()),
|
||||
..Default::default()
|
||||
}))
|
||||
.times(1)
|
||||
.return_once(|_| Ok(()));
|
||||
let ldap_handler = setup_bound_admin_handler(mock).await;
|
||||
let request = LdapAddRequest {
|
||||
dn: "uid=bob,ou=people,dc=example,dc=com".to_owned(),
|
||||
attributes: vec![LdapPartialAttribute {
|
||||
atype: "cn".to_owned(),
|
||||
vals: vec![b"Bob".to_vec()],
|
||||
}],
|
||||
};
|
||||
assert_eq!(
|
||||
ldap_handler.do_create_user_or_group(request).await,
|
||||
Ok(vec![make_add_error(LdapResultCode::Success, String::new())])
|
||||
);
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
async fn test_create_group() {
|
||||
let mut mock = MockTestBackendHandler::new();
|
||||
mock.expect_create_group()
|
||||
.with(eq(CreateGroupRequest {
|
||||
display_name: GroupName::new("bob"),
|
||||
..Default::default()
|
||||
}))
|
||||
.times(1)
|
||||
.return_once(|_| Ok(GroupId(5)));
|
||||
let ldap_handler = setup_bound_admin_handler(mock).await;
|
||||
let request = LdapAddRequest {
|
||||
dn: "uid=bob,ou=groups,dc=example,dc=com".to_owned(),
|
||||
attributes: vec![LdapPartialAttribute {
|
||||
atype: "cn".to_owned(),
|
||||
vals: vec![b"Bobby".to_vec()],
|
||||
}],
|
||||
};
|
||||
assert_eq!(
|
||||
ldap_handler.do_create_user_or_group(request).await,
|
||||
Ok(vec![make_add_error(LdapResultCode::Success, String::new())])
|
||||
);
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
async fn test_create_user_multiple_object_class() {
|
||||
let mut mock = MockTestBackendHandler::new();
|
||||
mock.expect_create_user()
|
||||
.with(eq(CreateUserRequest {
|
||||
user_id: UserId::new("bob"),
|
||||
email: "".into(),
|
||||
display_name: Some("Bob".to_string()),
|
||||
..Default::default()
|
||||
}))
|
||||
.times(1)
|
||||
.return_once(|_| Ok(()));
|
||||
let ldap_handler = setup_bound_admin_handler(mock).await;
|
||||
let request = LdapAddRequest {
|
||||
dn: "uid=bob,ou=people,dc=example,dc=com".to_owned(),
|
||||
attributes: vec![
|
||||
LdapPartialAttribute {
|
||||
atype: "cn".to_owned(),
|
||||
vals: vec![b"Bob".to_vec()],
|
||||
},
|
||||
LdapPartialAttribute {
|
||||
atype: "objectClass".to_owned(),
|
||||
vals: vec![
|
||||
b"top".to_vec(),
|
||||
b"person".to_vec(),
|
||||
b"inetOrgPerson".to_vec(),
|
||||
],
|
||||
},
|
||||
],
|
||||
};
|
||||
assert_eq!(
|
||||
ldap_handler.do_create_user_or_group(request).await,
|
||||
Ok(vec![make_add_error(LdapResultCode::Success, String::new())])
|
||||
);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
pub mod compare;
|
||||
pub mod create;
|
||||
pub mod handler;
|
||||
pub mod password;
|
||||
pub mod search;
|
||||
|
||||
Reference in New Issue
Block a user