mirror of
https://github.com/kanidm/kanidm.git
synced 2025-06-06 08:07:47 +02:00
Add mail support to groups (#2718)
* Add mail support to groups * Update libs/client/src/group.rs --------- Co-authored-by: James Hodgkinson <james@terminaloutcomes.com>
This commit is contained in:
parent
604adccdae
commit
afd674d346
libs/client/src
server
tools/cli/src
|
@ -97,4 +97,22 @@ impl KanidmClient {
|
||||||
)
|
)
|
||||||
.await
|
.await
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub async fn idm_group_purge_mail(&self, id: &str) -> Result<(), ClientError> {
|
||||||
|
self.idm_group_purge_attr(id, "mail").await
|
||||||
|
}
|
||||||
|
|
||||||
|
pub async fn idm_group_set_mail<T: serde::Serialize>(
|
||||||
|
&self,
|
||||||
|
id: &str,
|
||||||
|
values: &[T],
|
||||||
|
) -> Result<(), ClientError> {
|
||||||
|
self.perform_put_request(&format!("/v1/group/{}/_attr/mail", id), values)
|
||||||
|
.await
|
||||||
|
}
|
||||||
|
|
||||||
|
pub async fn idm_group_get_mail(&self, id: &str) -> Result<Option<Vec<String>>, ClientError> {
|
||||||
|
self.perform_get_request(&format!("/v1/group/{}/_attr/mail", id))
|
||||||
|
.await
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1330,6 +1330,29 @@ lazy_static! {
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
lazy_static! {
|
||||||
|
pub static ref IDM_ACP_ACCOUNT_MAIL_READ_DL6: BuiltinAcp = BuiltinAcp {
|
||||||
|
classes: vec![
|
||||||
|
EntryClass::Object,
|
||||||
|
EntryClass::AccessControlProfile,
|
||||||
|
EntryClass::AccessControlSearch
|
||||||
|
],
|
||||||
|
name: "idm_acp_account_mail_read",
|
||||||
|
uuid: UUID_IDM_ACP_ACCOUNT_MAIL_READ_V1,
|
||||||
|
description: "Builtin IDM Control for reading account and group mail attributes.",
|
||||||
|
receiver: BuiltinAcpReceiver::Group(vec![UUID_IDM_ACCOUNT_MAIL_READ]),
|
||||||
|
target: BuiltinAcpTarget::Filter(ProtoFilter::And(vec![
|
||||||
|
ProtoFilter::Or(vec![
|
||||||
|
match_class_filter!(EntryClass::Account),
|
||||||
|
match_class_filter!(EntryClass::Group),
|
||||||
|
]),
|
||||||
|
FILTER_ANDNOT_TOMBSTONE_OR_RECYCLED.clone(),
|
||||||
|
])),
|
||||||
|
search_attrs: vec![Attribute::Mail],
|
||||||
|
..Default::default()
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
lazy_static! {
|
lazy_static! {
|
||||||
pub static ref IDM_ACP_SYSTEM_CONFIG_ACCOUNT_POLICY_MANAGE_V1: BuiltinAcp = BuiltinAcp {
|
pub static ref IDM_ACP_SYSTEM_CONFIG_ACCOUNT_POLICY_MANAGE_V1: BuiltinAcp = BuiltinAcp {
|
||||||
classes: vec![
|
classes: vec![
|
||||||
|
@ -1499,6 +1522,7 @@ lazy_static! {
|
||||||
Attribute::Spn,
|
Attribute::Spn,
|
||||||
Attribute::Uuid,
|
Attribute::Uuid,
|
||||||
Attribute::Description,
|
Attribute::Description,
|
||||||
|
Attribute::Mail,
|
||||||
Attribute::Member,
|
Attribute::Member,
|
||||||
Attribute::DynMember,
|
Attribute::DynMember,
|
||||||
Attribute::EntryManagedBy,
|
Attribute::EntryManagedBy,
|
||||||
|
@ -1508,6 +1532,7 @@ lazy_static! {
|
||||||
Attribute::Name,
|
Attribute::Name,
|
||||||
Attribute::Uuid,
|
Attribute::Uuid,
|
||||||
Attribute::Description,
|
Attribute::Description,
|
||||||
|
Attribute::Mail,
|
||||||
Attribute::Member,
|
Attribute::Member,
|
||||||
Attribute::EntryManagedBy,
|
Attribute::EntryManagedBy,
|
||||||
],
|
],
|
||||||
|
@ -1518,11 +1543,13 @@ lazy_static! {
|
||||||
modify_present_attrs: vec![
|
modify_present_attrs: vec![
|
||||||
Attribute::Name,
|
Attribute::Name,
|
||||||
Attribute::Description,
|
Attribute::Description,
|
||||||
|
Attribute::Mail,
|
||||||
Attribute::Member,
|
Attribute::Member,
|
||||||
],
|
],
|
||||||
modify_removed_attrs: vec![
|
modify_removed_attrs: vec![
|
||||||
Attribute::Name,
|
Attribute::Name,
|
||||||
Attribute::Description,
|
Attribute::Description,
|
||||||
|
Attribute::Mail,
|
||||||
Attribute::Member,
|
Attribute::Member,
|
||||||
],
|
],
|
||||||
..Default::default()
|
..Default::default()
|
||||||
|
|
|
@ -768,6 +768,24 @@ pub static ref SCHEMA_CLASS_GROUP: SchemaClass = SchemaClass {
|
||||||
..Default::default()
|
..Default::default()
|
||||||
};
|
};
|
||||||
|
|
||||||
|
pub static ref SCHEMA_CLASS_GROUP_DL6: SchemaClass = SchemaClass {
|
||||||
|
uuid: UUID_SCHEMA_CLASS_GROUP,
|
||||||
|
name: EntryClass::Group.into(),
|
||||||
|
description: "Object representation of a group".to_string(),
|
||||||
|
|
||||||
|
sync_allowed: true,
|
||||||
|
systemmay: vec![
|
||||||
|
Attribute::Member.into(),
|
||||||
|
Attribute::GrantUiHint.into(),
|
||||||
|
Attribute::Description.into(),
|
||||||
|
Attribute::Mail.into(),
|
||||||
|
],
|
||||||
|
systemmust: vec![
|
||||||
|
Attribute::Name.into(),
|
||||||
|
Attribute::Spn.into()],
|
||||||
|
..Default::default()
|
||||||
|
};
|
||||||
|
|
||||||
pub static ref SCHEMA_CLASS_DYNGROUP: SchemaClass = SchemaClass {
|
pub static ref SCHEMA_CLASS_DYNGROUP: SchemaClass = SchemaClass {
|
||||||
uuid: UUID_SCHEMA_CLASS_DYNGROUP,
|
uuid: UUID_SCHEMA_CLASS_DYNGROUP,
|
||||||
name: EntryClass::DynGroup.into(),
|
name: EntryClass::DynGroup.into(),
|
||||||
|
|
|
@ -891,6 +891,7 @@ impl<'a> QueryServerWriteTransaction<'a> {
|
||||||
SCHEMA_CLASS_DOMAIN_INFO_DL6.clone().into(),
|
SCHEMA_CLASS_DOMAIN_INFO_DL6.clone().into(),
|
||||||
SCHEMA_CLASS_SERVICE_ACCOUNT_DL6.clone().into(),
|
SCHEMA_CLASS_SERVICE_ACCOUNT_DL6.clone().into(),
|
||||||
SCHEMA_CLASS_SYNC_ACCOUNT_DL6.clone().into(),
|
SCHEMA_CLASS_SYNC_ACCOUNT_DL6.clone().into(),
|
||||||
|
SCHEMA_CLASS_GROUP_DL6.clone().into(),
|
||||||
SCHEMA_CLASS_KEY_PROVIDER_DL6.clone().into(),
|
SCHEMA_CLASS_KEY_PROVIDER_DL6.clone().into(),
|
||||||
SCHEMA_CLASS_KEY_PROVIDER_INTERNAL_DL6.clone().into(),
|
SCHEMA_CLASS_KEY_PROVIDER_INTERNAL_DL6.clone().into(),
|
||||||
SCHEMA_CLASS_KEY_OBJECT_DL6.clone().into(),
|
SCHEMA_CLASS_KEY_OBJECT_DL6.clone().into(),
|
||||||
|
@ -914,6 +915,7 @@ impl<'a> QueryServerWriteTransaction<'a> {
|
||||||
IDM_ACP_GROUP_ACCOUNT_POLICY_MANAGE_DL6.clone().into(),
|
IDM_ACP_GROUP_ACCOUNT_POLICY_MANAGE_DL6.clone().into(),
|
||||||
IDM_ACP_PEOPLE_CREATE_DL6.clone().into(),
|
IDM_ACP_PEOPLE_CREATE_DL6.clone().into(),
|
||||||
IDM_ACP_GROUP_MANAGE_DL6.clone().into(),
|
IDM_ACP_GROUP_MANAGE_DL6.clone().into(),
|
||||||
|
IDM_ACP_ACCOUNT_MAIL_READ_DL6.clone().into(),
|
||||||
// Update anonymous with the correct entry manager,
|
// Update anonymous with the correct entry manager,
|
||||||
BUILTIN_ACCOUNT_ANONYMOUS_DL6.clone().into(),
|
BUILTIN_ACCOUNT_ANONYMOUS_DL6.clone().into(),
|
||||||
// Add the internal key provider.
|
// Add the internal key provider.
|
||||||
|
|
|
@ -207,6 +207,32 @@ async fn test_server_rest_group_lifecycle(rsclient: KanidmClient) {
|
||||||
let members = rsclient.idm_group_get_members("demo_group").await.unwrap();
|
let members = rsclient.idm_group_get_members("demo_group").await.unwrap();
|
||||||
assert!(members.is_none());
|
assert!(members.is_none());
|
||||||
|
|
||||||
|
// Add a mail attribute.
|
||||||
|
rsclient
|
||||||
|
.idm_group_set_mail(
|
||||||
|
"demo_group",
|
||||||
|
&["primary@example.com", "secondary@example.com"],
|
||||||
|
)
|
||||||
|
.await
|
||||||
|
.unwrap();
|
||||||
|
|
||||||
|
let mail_addrs = rsclient.idm_group_get_mail("demo_group").await.unwrap();
|
||||||
|
|
||||||
|
assert_eq!(
|
||||||
|
mail_addrs,
|
||||||
|
Some(vec![
|
||||||
|
"primary@example.com".to_string(),
|
||||||
|
"secondary@example.com".to_string()
|
||||||
|
])
|
||||||
|
);
|
||||||
|
|
||||||
|
// Purge the mail addrs.
|
||||||
|
rsclient.idm_group_purge_mail("demo_group").await.unwrap();
|
||||||
|
|
||||||
|
let mail_addrs = rsclient.idm_group_get_mail("demo_group").await.unwrap();
|
||||||
|
|
||||||
|
assert_eq!(mail_addrs, None);
|
||||||
|
|
||||||
// Delete the group
|
// Delete the group
|
||||||
rsclient.idm_group_delete("demo_group").await.unwrap();
|
rsclient.idm_group_delete("demo_group").await.unwrap();
|
||||||
let g_list_3 = rsclient.idm_group_list().await.unwrap();
|
let g_list_3 = rsclient.idm_group_list().await.unwrap();
|
||||||
|
|
|
@ -16,6 +16,7 @@ impl GroupOpt {
|
||||||
GroupOpt::RemoveMembers(gcopt) => gcopt.copt.debug,
|
GroupOpt::RemoveMembers(gcopt) => gcopt.copt.debug,
|
||||||
GroupOpt::SetMembers(gcopt) => gcopt.copt.debug,
|
GroupOpt::SetMembers(gcopt) => gcopt.copt.debug,
|
||||||
GroupOpt::PurgeMembers(gcopt) => gcopt.copt.debug,
|
GroupOpt::PurgeMembers(gcopt) => gcopt.copt.debug,
|
||||||
|
GroupOpt::SetMail { copt, .. } => copt.debug,
|
||||||
GroupOpt::Posix { commands } => match commands {
|
GroupOpt::Posix { commands } => match commands {
|
||||||
GroupPosix::Show(gcopt) => gcopt.copt.debug,
|
GroupPosix::Show(gcopt) => gcopt.copt.debug,
|
||||||
GroupPosix::Set(gcopt) => gcopt.copt.debug,
|
GroupPosix::Set(gcopt) => gcopt.copt.debug,
|
||||||
|
@ -145,6 +146,22 @@ impl GroupOpt {
|
||||||
Ok(_) => println!("Successfully set members for group {}", gcopt.name.as_str()),
|
Ok(_) => println!("Successfully set members for group {}", gcopt.name.as_str()),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
GroupOpt::SetMail { copt, name, mail } => {
|
||||||
|
let client = copt.to_client(OpType::Write).await;
|
||||||
|
|
||||||
|
let result = if mail.is_empty() {
|
||||||
|
client.idm_group_purge_mail(name.as_str()).await
|
||||||
|
} else {
|
||||||
|
client
|
||||||
|
.idm_group_set_mail(name.as_str(), mail.as_slice())
|
||||||
|
.await
|
||||||
|
};
|
||||||
|
|
||||||
|
match result {
|
||||||
|
Err(e) => handle_client_error(e, copt.output_mode),
|
||||||
|
Ok(_) => println!("Successfully set mail for group {}", name.as_str()),
|
||||||
|
}
|
||||||
|
}
|
||||||
GroupOpt::SetEntryManagedBy {
|
GroupOpt::SetEntryManagedBy {
|
||||||
name,
|
name,
|
||||||
entry_managed_by,
|
entry_managed_by,
|
||||||
|
|
|
@ -256,6 +256,16 @@ pub enum GroupOpt {
|
||||||
/// set operation.
|
/// set operation.
|
||||||
#[clap(name = "set-members")]
|
#[clap(name = "set-members")]
|
||||||
SetMembers(GroupNamedMembers),
|
SetMembers(GroupNamedMembers),
|
||||||
|
/// Set the exact list of mail addresses that this group is associated with. The first
|
||||||
|
/// mail address in the list is the `primary` and the remainder are aliases. Setting
|
||||||
|
/// an empty list will clear the mail attribute.
|
||||||
|
#[clap(name = "set-mail")]
|
||||||
|
SetMail {
|
||||||
|
#[clap(flatten)]
|
||||||
|
copt: CommonOpt,
|
||||||
|
name: String,
|
||||||
|
mail: Vec<String>,
|
||||||
|
},
|
||||||
/// Set a new entry-managed-by for this group.
|
/// Set a new entry-managed-by for this group.
|
||||||
#[clap(name = "set-entry-manager")]
|
#[clap(name = "set-entry-manager")]
|
||||||
SetEntryManagedBy {
|
SetEntryManagedBy {
|
||||||
|
|
Loading…
Reference in a new issue