diff --git a/src/context.rs b/src/context.rs index 16628de..8b7bcc2 100644 --- a/src/context.rs +++ b/src/context.rs @@ -39,6 +39,10 @@ pub trait ControllerEvents { async fn user_created(&self, obj: &T, username: &str) -> Result<(), Self::Error> where T: Resource + Sync; + + async fn user_deleted(&self, obj: &T, username: &str) -> Result<(), Self::Error> + where + T: Resource + Sync; } #[async_trait] @@ -78,4 +82,21 @@ impl ControllerEvents for Recorder { ) .await } + + async fn user_deleted(&self, obj: &T, username: &str) -> Result<(), Self::Error> + where + T: Resource + Sync, + { + self.publish( + &Event { + type_: EventType::Normal, + reason: "UserDeleted".into(), + note: Some(format!("Deleted user '{username}'")), + action: "UserDeleted".into(), + secondary: None, + }, + &obj.object_ref(&()), + ) + .await + } } diff --git a/src/lldap.rs b/src/lldap.rs index 4c70c39..f6918ad 100644 --- a/src/lldap.rs +++ b/src/lldap.rs @@ -9,7 +9,7 @@ use tracing::debug; use cynic::http::{CynicReqwestError, ReqwestExt}; use cynic::{GraphQlError, GraphQlResponse, MutationBuilder, QueryBuilder}; use lldap_auth::login::{ClientSimpleLoginRequest, ServerLoginResponse}; -use queries::{CreateUser, CreateUserVariables, ListUsers}; +use queries::{CreateUser, CreateUserVariables, DeleteUser, DeleteUserVariables, ListUsers}; #[derive(thiserror::Error, Debug)] pub enum Error { @@ -116,7 +116,18 @@ impl LldapClient { pub async fn create_user(&self, username: &str) -> Result<()> { let operation = CreateUser::build(CreateUserVariables { id: username }); - // TODO: Check the response? + let response = self + .client + .post(format!("{}/api/graphql", self.url)) + .run_graphql(operation) + .await?; + + check_graphql_errors(&response) + } + + pub async fn delete_user(&self, username: &str) -> Result<()> { + let operation = DeleteUser::build(DeleteUserVariables { id: username }); + let response = self .client .post(format!("{}/api/graphql", self.url)) diff --git a/src/resources.rs b/src/resources.rs index 90eb310..6c648b7 100644 --- a/src/resources.rs +++ b/src/resources.rs @@ -132,6 +132,10 @@ where ) } +fn format_username(name: &str, namespace: &str) -> String { + format!("{name}.{namespace}") +} + #[async_trait] impl Reconcile for ServiceUser { async fn reconcile(self: Arc, ctx: Arc) -> Result { @@ -152,7 +156,7 @@ impl Reconcile for ServiceUser { debug!(name, "Apply"); let secret_name = format!("{name}-lldap-credentials"); - let username = format!("{name}.{namespace}"); + let username = format_username(&name, &namespace); let client = &ctx.client; let secrets = Api::::namespaced(client.clone(), &namespace); @@ -221,8 +225,27 @@ impl Reconcile for ServiceUser { Ok(Action::requeue(Duration::from_secs(3600))) } - async fn cleanup(self: Arc, _ctx: Arc) -> Result { - debug!(name = self.name_any(), "Cleanup"); + async fn cleanup(self: Arc, ctx: Arc) -> Result { + let name = self + .metadata + .name + .clone() + .ok_or(Error::MissingObjectKey(".metadata.name"))?; + let namespace = self + .metadata + .namespace + .clone() + .ok_or(Error::MissingObjectKey(".metadata.namespace"))?; + + debug!(name, "Cleanup"); + + let username = format_username(&name, &namespace); + + let lldap_client = ctx.lldap_config.build_client().await?; + + trace!(name, username, "Deleting user"); + lldap_client.delete_user(&username).await?; + ctx.recorder.user_deleted(self.as_ref(), &username).await?; Ok(Action::await_change()) }