From 8ae8a5cda70e62be4517bb05ad272ae6c96cf8de Mon Sep 17 00:00:00 2001 From: Lennart <18233294+lennart-k@users.noreply.github.com> Date: Sat, 26 Apr 2025 11:23:15 +0200 Subject: [PATCH] cli: Add basic functionality to assign membership --- src/commands/membership.rs | 72 ++++++++++++++++++++++++++++++++++++++ src/commands/mod.rs | 1 + src/commands/principals.rs | 6 ++-- 3 files changed, 77 insertions(+), 2 deletions(-) create mode 100644 src/commands/membership.rs diff --git a/src/commands/membership.rs b/src/commands/membership.rs new file mode 100644 index 0000000..6226cd5 --- /dev/null +++ b/src/commands/membership.rs @@ -0,0 +1,72 @@ +use clap::{Parser, Subcommand}; +use rustical_store::auth::AuthenticationProvider; + +#[derive(Debug, Parser)] +pub struct AssignArgs { + id: String, + #[arg(long, help = "The principal to assign a membership to (e.g. a group)")] + to: String, +} + +#[derive(Debug, Parser)] +pub struct RemoveArgs { + id: String, + #[arg(long, help = "The membership to remove")] + to: String, +} + +#[derive(Debug, Parser)] +pub struct ListArgs { + id: String, +} + +#[derive(Debug, Subcommand)] +pub enum MembershipCommand { + Assign(AssignArgs), + Remove(RemoveArgs), + List(ListArgs), +} + +#[derive(Parser, Debug)] +pub struct MembershipArgs { + #[command(subcommand)] + command: MembershipCommand, +} + +pub async fn handle_membership_command( + user_store: impl AuthenticationProvider, + MembershipArgs { command }: MembershipArgs, +) -> anyhow::Result<()> { + let id = match &command { + MembershipCommand::Assign(AssignArgs { id, .. }) => id, + MembershipCommand::Remove(RemoveArgs { id, .. }) => id, + MembershipCommand::List(ListArgs { id }) => id, + }; + let mut principal = user_store + .get_principal(id) + .await? + .unwrap_or_else(|| panic!("Principal {id} does not exist")); + + match command { + MembershipCommand::Assign(AssignArgs { to, .. }) => { + if principal.memberships.contains(&to) { + println!("Principal is already member of {to}"); + return Ok(()); + } + principal.memberships.push(to); + user_store.insert_principal(principal, true).await?; + println!("Membership assigned"); + } + MembershipCommand::Remove(RemoveArgs { to, .. }) => { + principal.memberships.retain(|principal| principal != &to); + user_store.insert_principal(principal, true).await?; + println!("Membership removed"); + } + MembershipCommand::List(ListArgs { .. }) => { + for membership in principal.memberships { + println!("{membership}"); + } + } + } + Ok(()) +} diff --git a/src/commands/mod.rs b/src/commands/mod.rs index 245411c..3600920 100644 --- a/src/commands/mod.rs +++ b/src/commands/mod.rs @@ -11,6 +11,7 @@ use crate::config::{ TracingConfig, }; +mod membership; pub mod principals; #[derive(Debug, Parser)] diff --git a/src/commands/principals.rs b/src/commands/principals.rs index 40b5a1c..d3b398a 100644 --- a/src/commands/principals.rs +++ b/src/commands/principals.rs @@ -1,3 +1,5 @@ +use super::membership::{MembershipArgs, handle_membership_command}; +use crate::config::{self, Config}; use clap::{Parser, Subcommand}; use figment::{ Figment, @@ -8,8 +10,6 @@ use password_hash::SaltString; use rand::rngs::OsRng; use rustical_store::auth::{AuthenticationProvider, TomlPrincipalStore, User, user::PrincipalType}; -use crate::config::{self, Config}; - #[derive(Parser, Debug)] pub struct PrincipalsArgs { #[arg(short, long, env, default_value = "/etc/rustical/config.toml")] @@ -57,6 +57,7 @@ enum Command { Create(CreateArgs), Remove(RemoveArgs), Edit(EditArgs), + Membership(MembershipArgs), } pub async fn cmd_principals(args: PrincipalsArgs) -> anyhow::Result<()> { @@ -155,6 +156,7 @@ pub async fn cmd_principals(args: PrincipalsArgs) -> anyhow::Result<()> { user_store.insert_principal(principal, true).await?; println!("Principal {id} updated"); } + Command::Membership(args) => handle_membership_command(user_store, args).await?, } Ok(()) }