mirror of
https://github.com/kanidm/kanidm.git
synced 2025-02-24 04:57:00 +01:00
Adding a new verb group remove_members (#434)
Co-authored-by: William Brown <william@blackhats.net.au>
This commit is contained in:
parent
2bd8606cb6
commit
e6f34d5dc5
|
@ -193,7 +193,7 @@ group
|
||||||
GET -> get this groups attr
|
GET -> get this groups attr
|
||||||
PUT -> overwrite this group attr value list
|
PUT -> overwrite this group attr value list
|
||||||
POST -> append this list to group attr
|
POST -> append this list to group attr
|
||||||
DELETE -> purge this attr
|
DELETE -> purge this attr (if body empty) or the elements listed in the body
|
||||||
|
|
||||||
schema
|
schema
|
||||||
======
|
======
|
||||||
|
|
|
@ -129,7 +129,6 @@ impl KanidmAsyncClient {
|
||||||
// let dest = format!("{}{}", self.addr, dest);
|
// let dest = format!("{}{}", self.addr, dest);
|
||||||
|
|
||||||
let req_string = serde_json::to_string(&request).map_err(ClientError::JSONEncode)?;
|
let req_string = serde_json::to_string(&request).map_err(ClientError::JSONEncode)?;
|
||||||
|
|
||||||
let response = self
|
let response = self
|
||||||
.client
|
.client
|
||||||
.post(dest.as_str())
|
.post(dest.as_str())
|
||||||
|
@ -260,7 +259,55 @@ impl KanidmAsyncClient {
|
||||||
|
|
||||||
async fn perform_delete_request(&self, dest: &str) -> Result<bool, ClientError> {
|
async fn perform_delete_request(&self, dest: &str) -> Result<bool, ClientError> {
|
||||||
let dest = format!("{}{}", self.addr, dest);
|
let dest = format!("{}{}", self.addr, dest);
|
||||||
let response = self.client.delete(dest.as_str());
|
|
||||||
|
let response = self
|
||||||
|
.client
|
||||||
|
.delete(dest.as_str())
|
||||||
|
.header(CONTENT_TYPE, APPLICATION_JSON);
|
||||||
|
let response = if let Some(token) = &self.bearer_token {
|
||||||
|
response.bearer_auth(token)
|
||||||
|
} else {
|
||||||
|
response
|
||||||
|
};
|
||||||
|
|
||||||
|
let response = response.send().await.map_err(ClientError::Transport)?;
|
||||||
|
|
||||||
|
let opid = response
|
||||||
|
.headers()
|
||||||
|
.get(KOPID)
|
||||||
|
.and_then(|hv| hv.to_str().ok().map(|s| s.to_string()))
|
||||||
|
.unwrap_or_else(|| "missing_kopid".to_string());
|
||||||
|
debug!("opid -> {:?}", opid);
|
||||||
|
|
||||||
|
match response.status() {
|
||||||
|
reqwest::StatusCode::OK => {}
|
||||||
|
unexpect => {
|
||||||
|
return Err(ClientError::Http(
|
||||||
|
unexpect,
|
||||||
|
response.json().await.ok(),
|
||||||
|
opid,
|
||||||
|
))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
response
|
||||||
|
.json()
|
||||||
|
.await
|
||||||
|
.map_err(|e| ClientError::JSONDecode(e, opid))
|
||||||
|
}
|
||||||
|
async fn perform_delete_request_with_body<R: Serialize>(
|
||||||
|
&self,
|
||||||
|
dest: &str,
|
||||||
|
request: R,
|
||||||
|
) -> Result<bool, ClientError> {
|
||||||
|
let dest = format!("{}{}", self.addr, dest);
|
||||||
|
|
||||||
|
let req_string = serde_json::to_string(&request).map_err(ClientError::JSONEncode)?;
|
||||||
|
let response = self
|
||||||
|
.client
|
||||||
|
.delete(dest.as_str())
|
||||||
|
.body(req_string)
|
||||||
|
.header(CONTENT_TYPE, APPLICATION_JSON);
|
||||||
let response = if let Some(token) = &self.bearer_token {
|
let response = if let Some(token) = &self.bearer_token {
|
||||||
response.bearer_auth(token)
|
response.bearer_auth(token)
|
||||||
} else {
|
} else {
|
||||||
|
@ -658,11 +705,28 @@ impl KanidmAsyncClient {
|
||||||
.await
|
.await
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
pub async fn idm_group_remove_members(
|
||||||
pub fn idm_group_remove_member(&self, id: &str, member: &str) -> Result<(), ClientError> {
|
&self,
|
||||||
unimplemented!();
|
group: &str,
|
||||||
|
members: &[&str],
|
||||||
|
) -> Result<bool, ClientError> {
|
||||||
|
debug!(
|
||||||
|
"{}",
|
||||||
|
[
|
||||||
|
"Asked to remove members ",
|
||||||
|
&members.join(","),
|
||||||
|
" from ",
|
||||||
|
group
|
||||||
|
]
|
||||||
|
.concat()
|
||||||
|
.to_string()
|
||||||
|
);
|
||||||
|
self.perform_delete_request_with_body(
|
||||||
|
["/v1/group/", group, "/_attr/member"].concat().as_str(),
|
||||||
|
&members,
|
||||||
|
)
|
||||||
|
.await
|
||||||
}
|
}
|
||||||
*/
|
|
||||||
|
|
||||||
pub async fn idm_group_purge_members(&self, id: &str) -> Result<bool, ClientError> {
|
pub async fn idm_group_purge_members(&self, id: &str) -> Result<bool, ClientError> {
|
||||||
self.perform_delete_request(format!("/v1/group/{}/_attr/member", id).as_str())
|
self.perform_delete_request(format!("/v1/group/{}/_attr/member", id).as_str())
|
||||||
|
|
|
@ -457,11 +457,13 @@ impl KanidmClient {
|
||||||
tokio_block_on(self.asclient.idm_group_add_members(id, members))
|
tokio_block_on(self.asclient.idm_group_add_members(id, members))
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
pub fn idm_group_remove_members(
|
||||||
pub fn idm_group_remove_member(&self, id: &str, member: &str) -> Result<(), ClientError> {
|
&self,
|
||||||
unimplemented!();
|
group: &str,
|
||||||
|
members: &[&str],
|
||||||
|
) -> Result<bool, ClientError> {
|
||||||
|
tokio_block_on(self.asclient.idm_group_remove_members(group, members))
|
||||||
}
|
}
|
||||||
*/
|
|
||||||
|
|
||||||
pub fn idm_group_purge_members(&self, id: &str) -> Result<bool, ClientError> {
|
pub fn idm_group_purge_members(&self, id: &str) -> Result<bool, ClientError> {
|
||||||
tokio_block_on(self.asclient.idm_group_purge_members(id))
|
tokio_block_on(self.asclient.idm_group_purge_members(id))
|
||||||
|
|
|
@ -286,13 +286,11 @@ fn test_server_rest_group_lifecycle() {
|
||||||
);
|
);
|
||||||
|
|
||||||
// Remove a member from the group
|
// Remove a member from the group
|
||||||
/*
|
|
||||||
rsclient
|
rsclient
|
||||||
.idm_group_remove_member("demo_group", "demo_group")
|
.idm_group_remove_members("demo_group", &["demo_group"])
|
||||||
.unwrap();
|
.unwrap();
|
||||||
let members = rsclient.idm_group_get_members("demo_group").unwrap();
|
let members = rsclient.idm_group_get_members("demo_group").unwrap();
|
||||||
assert!(members == vec!["admin".to_string()]);
|
assert!(members == Some(vec!["admin@example.com".to_string()]));
|
||||||
*/
|
|
||||||
|
|
||||||
// purge members
|
// purge members
|
||||||
rsclient.idm_group_purge_members("demo_group").unwrap();
|
rsclient.idm_group_purge_members("demo_group").unwrap();
|
||||||
|
|
|
@ -9,6 +9,7 @@ impl GroupOpt {
|
||||||
GroupOpt::Delete(gcopt) => gcopt.copt.debug,
|
GroupOpt::Delete(gcopt) => gcopt.copt.debug,
|
||||||
GroupOpt::ListMembers(gcopt) => gcopt.copt.debug,
|
GroupOpt::ListMembers(gcopt) => gcopt.copt.debug,
|
||||||
GroupOpt::AddMembers(gcopt) => gcopt.copt.debug,
|
GroupOpt::AddMembers(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::Posix(gpopt) => match gpopt {
|
GroupOpt::Posix(gpopt) => match gpopt {
|
||||||
|
@ -74,6 +75,18 @@ impl GroupOpt {
|
||||||
eprintln!("Error -> {:?}", e);
|
eprintln!("Error -> {:?}", e);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
GroupOpt::RemoveMembers(gcopt) => {
|
||||||
|
let client = gcopt.copt.to_client();
|
||||||
|
let remove_members: Vec<&str> = gcopt.members.iter().map(|s| s.as_str()).collect();
|
||||||
|
|
||||||
|
if let Err(e) =
|
||||||
|
client.idm_group_remove_members(gcopt.name.as_str(), &remove_members)
|
||||||
|
{
|
||||||
|
eprintln!("Failed to remove members -> {:?}", e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
GroupOpt::SetMembers(gcopt) => {
|
GroupOpt::SetMembers(gcopt) => {
|
||||||
let client = gcopt.copt.to_client();
|
let client = gcopt.copt.to_client();
|
||||||
let new_members: Vec<&str> = gcopt.members.iter().map(|s| s.as_str()).collect();
|
let new_members: Vec<&str> = gcopt.members.iter().map(|s| s.as_str()).collect();
|
||||||
|
|
|
@ -64,6 +64,8 @@ pub enum GroupOpt {
|
||||||
PurgeMembers(Named),
|
PurgeMembers(Named),
|
||||||
#[structopt(name = "add_members")]
|
#[structopt(name = "add_members")]
|
||||||
AddMembers(GroupNamedMembers),
|
AddMembers(GroupNamedMembers),
|
||||||
|
#[structopt(name = "remove_members")]
|
||||||
|
RemoveMembers(GroupNamedMembers),
|
||||||
#[structopt(name = "posix")]
|
#[structopt(name = "posix")]
|
||||||
Posix(GroupPosix),
|
Posix(GroupPosix),
|
||||||
}
|
}
|
||||||
|
|
|
@ -211,12 +211,12 @@ pub struct PurgeAttributeMessage {
|
||||||
pub eventid: Uuid,
|
pub eventid: Uuid,
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Delete a single attribute-value pair from the entry.
|
/// Delete a set of attribute-value pair from the entry.
|
||||||
pub struct RemoveAttributeValueMessage {
|
pub struct RemoveAttributeValuesMessage {
|
||||||
pub uat: Option<UserAuthToken>,
|
pub uat: Option<UserAuthToken>,
|
||||||
pub uuid_or_name: String,
|
pub uuid_or_name: String,
|
||||||
pub attr: String,
|
pub attr: String,
|
||||||
pub value: String,
|
pub values: Vec<String>,
|
||||||
pub filter: Filter<FilterInvalid>,
|
pub filter: Filter<FilterInvalid>,
|
||||||
pub eventid: Uuid,
|
pub eventid: Uuid,
|
||||||
}
|
}
|
||||||
|
@ -886,33 +886,46 @@ impl QueryServerWriteV1 {
|
||||||
res
|
res
|
||||||
}
|
}
|
||||||
|
|
||||||
pub async fn handle_removeattributevalue(
|
pub async fn handle_removeattributevalues(
|
||||||
&self,
|
&self,
|
||||||
msg: RemoveAttributeValueMessage,
|
msg: RemoveAttributeValuesMessage,
|
||||||
) -> Result<(), OperationError> {
|
) -> Result<(), OperationError> {
|
||||||
let mut audit = AuditScope::new("remove_attribute_value", msg.eventid, self.log_level);
|
let RemoveAttributeValuesMessage {
|
||||||
|
uat,
|
||||||
|
uuid_or_name,
|
||||||
|
attr,
|
||||||
|
values,
|
||||||
|
filter,
|
||||||
|
eventid,
|
||||||
|
} = msg;
|
||||||
|
|
||||||
|
let mut audit = AuditScope::new("remove_attribute_values", eventid, self.log_level);
|
||||||
let idms_prox_write = self.idms.proxy_write_async(duration_from_epoch_now()).await;
|
let idms_prox_write = self.idms.proxy_write_async(duration_from_epoch_now()).await;
|
||||||
let res = lperf_op_segment!(
|
let res = lperf_op_segment!(
|
||||||
&mut audit,
|
&mut audit,
|
||||||
"actors::v1_write::handle<RemoveAttributeValueMessage>",
|
"actors::v1_write::handle<RemoveAttributeValuesMessage>",
|
||||||
|| {
|
|| {
|
||||||
let target_uuid = idms_prox_write
|
let target_uuid = idms_prox_write
|
||||||
.qs_write
|
.qs_write
|
||||||
.name_to_uuid(&mut audit, msg.uuid_or_name.as_str())
|
.name_to_uuid(&mut audit, uuid_or_name.as_str())
|
||||||
.map_err(|e| {
|
.map_err(|e| {
|
||||||
ladmin_error!(audit, "Error resolving id to target");
|
ladmin_error!(audit, "Error resolving id to target");
|
||||||
e
|
e
|
||||||
})?;
|
})?;
|
||||||
|
|
||||||
let proto_ml =
|
let proto_ml = ProtoModifyList::new_list(
|
||||||
ProtoModifyList::new_list(vec![ProtoModify::Removed(msg.attr, msg.value)]);
|
values
|
||||||
|
.into_iter()
|
||||||
|
.map(|v| ProtoModify::Removed(attr.clone(), v))
|
||||||
|
.collect(),
|
||||||
|
);
|
||||||
|
|
||||||
let mdf = match ModifyEvent::from_parts(
|
let mdf = match ModifyEvent::from_parts(
|
||||||
&mut audit,
|
&mut audit,
|
||||||
msg.uat.as_ref(),
|
uat.as_ref(),
|
||||||
target_uuid,
|
target_uuid,
|
||||||
&proto_ml,
|
&proto_ml,
|
||||||
msg.filter,
|
filter,
|
||||||
&idms_prox_write.qs_write,
|
&idms_prox_write.qs_write,
|
||||||
) {
|
) {
|
||||||
Ok(m) => m,
|
Ok(m) => m,
|
||||||
|
|
|
@ -11,7 +11,8 @@ use crate::actors::v1_write::{
|
||||||
IdmAccountSetPasswordMessage, IdmAccountUnixExtendMessage, IdmAccountUnixSetCredMessage,
|
IdmAccountSetPasswordMessage, IdmAccountUnixExtendMessage, IdmAccountUnixSetCredMessage,
|
||||||
IdmGroupUnixExtendMessage, InternalCredentialSetMessage, InternalDeleteMessage,
|
IdmGroupUnixExtendMessage, InternalCredentialSetMessage, InternalDeleteMessage,
|
||||||
InternalRegenerateRadiusMessage, InternalSshKeyCreateMessage, ModifyMessage,
|
InternalRegenerateRadiusMessage, InternalSshKeyCreateMessage, ModifyMessage,
|
||||||
PurgeAttributeMessage, RemoveAttributeValueMessage, ReviveRecycledMessage, SetAttributeMessage,
|
PurgeAttributeMessage, RemoveAttributeValuesMessage, ReviveRecycledMessage,
|
||||||
|
SetAttributeMessage,
|
||||||
};
|
};
|
||||||
use crate::config::TlsConfiguration;
|
use crate::config::TlsConfiguration;
|
||||||
use crate::event::AuthResult;
|
use crate::event::AuthResult;
|
||||||
|
@ -360,7 +361,6 @@ async fn json_rest_event_post_id_attr(
|
||||||
let id = req.get_url_param("id")?;
|
let id = req.get_url_param("id")?;
|
||||||
let attr = req.get_url_param("attr")?;
|
let attr = req.get_url_param("attr")?;
|
||||||
let values: Vec<String> = req.body_json().await?;
|
let values: Vec<String> = req.body_json().await?;
|
||||||
|
|
||||||
let (eventid, hvalue) = new_eventid!();
|
let (eventid, hvalue) = new_eventid!();
|
||||||
let m_obj = AppendAttributeMessage {
|
let m_obj = AppendAttributeMessage {
|
||||||
uat,
|
uat,
|
||||||
|
@ -408,17 +408,26 @@ async fn json_rest_event_put_id_attr(
|
||||||
}
|
}
|
||||||
|
|
||||||
async fn json_rest_event_delete_id_attr(
|
async fn json_rest_event_delete_id_attr(
|
||||||
req: tide::Request<AppState>,
|
mut req: tide::Request<AppState>,
|
||||||
filter: Filter<FilterInvalid>,
|
filter: Filter<FilterInvalid>,
|
||||||
// Seperate for account_delete_id_radius
|
// Seperate for account_delete_id_radius
|
||||||
attr: String,
|
attr: String,
|
||||||
) -> tide::Result {
|
) -> tide::Result {
|
||||||
let uat = req.get_current_uat();
|
let uat = req.get_current_uat();
|
||||||
let id = req.get_url_param("id")?;
|
let id = req.get_url_param("id")?;
|
||||||
|
|
||||||
let (eventid, hvalue) = new_eventid!();
|
let (eventid, hvalue) = new_eventid!();
|
||||||
|
|
||||||
// TODO #211: Attempt to get an option Vec<String> here?
|
// TODO #211: Attempt to get an option Vec<String> here?
|
||||||
// It's probably better to focus on SCIM instead, it seems richer than this.
|
// It's probably better to focus on SCIM instead, it seems richer than this.
|
||||||
|
let body = req.take_body();
|
||||||
|
let values: Vec<String> = if body.is_empty().unwrap_or(true) {
|
||||||
|
vec![]
|
||||||
|
} else {
|
||||||
|
// Must now be a valid list.
|
||||||
|
body.into_json().await?
|
||||||
|
};
|
||||||
|
|
||||||
|
if values.len() == 0 {
|
||||||
let m_obj = PurgeAttributeMessage {
|
let m_obj = PurgeAttributeMessage {
|
||||||
uat,
|
uat,
|
||||||
uuid_or_name: id,
|
uuid_or_name: id,
|
||||||
|
@ -433,6 +442,24 @@ async fn json_rest_event_delete_id_attr(
|
||||||
.await
|
.await
|
||||||
.map(|()| true);
|
.map(|()| true);
|
||||||
to_tide_response(res, hvalue)
|
to_tide_response(res, hvalue)
|
||||||
|
} else {
|
||||||
|
let obj = RemoveAttributeValuesMessage {
|
||||||
|
uat,
|
||||||
|
uuid_or_name: id,
|
||||||
|
attr,
|
||||||
|
values,
|
||||||
|
filter,
|
||||||
|
eventid,
|
||||||
|
};
|
||||||
|
|
||||||
|
let res = req
|
||||||
|
.state()
|
||||||
|
.qe_w_ref
|
||||||
|
.handle_removeattributevalues(obj)
|
||||||
|
.await
|
||||||
|
.map(|()| true);
|
||||||
|
to_tide_response(res, hvalue)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
async fn json_rest_event_credential_put(
|
async fn json_rest_event_credential_put(
|
||||||
|
@ -684,11 +711,11 @@ pub async fn account_delete_id_ssh_pubkey_tag(req: tide::Request<AppState>) -> t
|
||||||
let tag = req.get_url_param("tag")?;
|
let tag = req.get_url_param("tag")?;
|
||||||
|
|
||||||
let (eventid, hvalue) = new_eventid!();
|
let (eventid, hvalue) = new_eventid!();
|
||||||
let obj = RemoveAttributeValueMessage {
|
let obj = RemoveAttributeValuesMessage {
|
||||||
uat,
|
uat,
|
||||||
uuid_or_name: id,
|
uuid_or_name: id,
|
||||||
attr: "ssh_publickey".to_string(),
|
attr: "ssh_publickey".to_string(),
|
||||||
value: tag,
|
values: vec![tag],
|
||||||
filter: filter_all!(f_eq("class", PartialValue::new_class("account"))),
|
filter: filter_all!(f_eq("class", PartialValue::new_class("account"))),
|
||||||
eventid,
|
eventid,
|
||||||
};
|
};
|
||||||
|
@ -696,7 +723,7 @@ pub async fn account_delete_id_ssh_pubkey_tag(req: tide::Request<AppState>) -> t
|
||||||
let res = req
|
let res = req
|
||||||
.state()
|
.state()
|
||||||
.qe_w_ref
|
.qe_w_ref
|
||||||
.handle_removeattributevalue(obj)
|
.handle_removeattributevalues(obj)
|
||||||
.await
|
.await
|
||||||
.map(|()| true);
|
.map(|()| true);
|
||||||
to_tide_response(res, hvalue)
|
to_tide_response(res, hvalue)
|
||||||
|
@ -1449,10 +1476,10 @@ pub fn create_https_server(
|
||||||
.delete(group_id_delete);
|
.delete(group_id_delete);
|
||||||
group_route
|
group_route
|
||||||
.at("/:id/_attr/:attr")
|
.at("/:id/_attr/:attr")
|
||||||
|
.delete(group_id_delete_attr)
|
||||||
.get(group_id_get_attr)
|
.get(group_id_get_attr)
|
||||||
.put(group_id_put_attr)
|
.put(group_id_put_attr)
|
||||||
.post(group_id_post_attr)
|
.post(group_id_post_attr);
|
||||||
.delete(group_id_delete_attr);
|
|
||||||
group_route.at("/:id/_unix").post(group_post_id_unix);
|
group_route.at("/:id/_unix").post(group_post_id_unix);
|
||||||
group_route
|
group_route
|
||||||
.at("/:id/_unix/_token")
|
.at("/:id/_unix/_token")
|
||||||
|
|
Loading…
Reference in a new issue