diff --git a/pkgs/by-name/ka/kanidm-provision/01-memberless.patch b/pkgs/by-name/ka/kanidm-provision/01-memberless.patch new file mode 100644 index 000000000000..b501a3f16828 --- /dev/null +++ b/pkgs/by-name/ka/kanidm-provision/01-memberless.patch @@ -0,0 +1,85 @@ +From ab3fa7d59b76658ba98ccf50c2910329896dab6f Mon Sep 17 00:00:00 2001 +From: Tom Hubrecht +Date: Tue, 4 Feb 2025 14:32:43 +0100 +Subject: [PATCH] feat: Allow declaring memberless groups + +When a group is "memberless", then the list of members is left intact, +which allows managing it imperatively. +--- + src/main.rs | 2 +- + src/state.rs | 2 ++ + tests/kanidm.nix | 18 +++++++++++++++++- + 3 files changed, 20 insertions(+), 2 deletions(-) + +diff --git a/src/main.rs b/src/main.rs +index 206a86a..6e48f59 100644 +--- a/src/main.rs ++++ b/src/main.rs +@@ -406,7 +406,7 @@ fn main() -> Result<()> { + // Sync group members + log_status("Syncing group members"); + for (name, group) in &state.groups { +- if group.present { ++ if group.present && !group.memberless { + update_attrs!(kanidm_client, ENDPOINT_GROUP, &existing_groups, &name, [ + "member": group.members.clone(), + ]); +diff --git a/src/state.rs b/src/state.rs +index 206c6f4..a8bfba2 100644 +--- a/src/state.rs ++++ b/src/state.rs +@@ -10,6 +10,8 @@ pub struct Group { + #[serde(default = "default_true")] + pub present: bool, + pub members: Vec, ++ #[serde(default = "default_false")] ++ pub memberless: bool, + } + + #[derive(Debug, Deserialize)] +diff --git a/tests/kanidm.nix b/tests/kanidm.nix +index a28beae..cb20257 100644 +--- a/tests/kanidm.nix ++++ b/tests/kanidm.nix +@@ -91,6 +91,8 @@ let + }; + + filterPresent = lib.filterAttrs (_: v: v.present); ++ filterMemberless = lib.filterAttrs (_: v: v.present && v.memberless); ++ filterMemberful = lib.filterAttrs (_: v: v.present && !v.memberless); + + provisionStateJson = pkgs.writeText "provision-state.json" ( + builtins.toJSON { inherit (cfg.provision) groups persons systems; } +@@ -391,6 +393,12 @@ in + apply = lib.unique; + default = [ ]; + }; ++ ++ memberless = lib.mkOption { ++ description = "Whether this group is considered memberless, i.e. the list of members is managed imperatively."; ++ type = lib.types.bool; ++ default = false; ++ }; + }; + config.members = lib.concatLists ( + lib.flip lib.mapAttrsToList cfg.provision.persons ( +@@ -708,10 +716,18 @@ in + person: personCfg: + assertGroupsKnown "services.kanidm.provision.persons.${person}.groups" personCfg.groups + ) +- ++ lib.flip lib.mapAttrsToList (filterPresent cfg.provision.groups) ( ++ ++ lib.flip lib.mapAttrsToList (filterMemberful cfg.provision.groups) ( + group: groupCfg: + assertEntitiesKnown "services.kanidm.provision.groups.${group}.members" groupCfg.members + ) ++ ++ lib.flip lib.mapAttrsToList (filterMemberless cfg.provision.groups) ( ++ group: groupCfg: { ++ assertion = cfg.provision.enable -> groupCfg.members == [ ]; ++ message = '' ++ services.kanidm.groups.${group} is declared as memberless but contains members: ${toString groupCfg.members} ++ ''; ++ } ++ ) + ++ lib.concatLists ( + lib.flip lib.mapAttrsToList (filterPresent cfg.provision.systems.oauth2) ( + oauth2: oauth2Cfg: diff --git a/pkgs/by-name/ka/kanidm-provision/package.nix b/pkgs/by-name/ka/kanidm-provision/package.nix index 63d7e85ba8a8..5ebd69cb91ee 100644 --- a/pkgs/by-name/ka/kanidm-provision/package.nix +++ b/pkgs/by-name/ka/kanidm-provision/package.nix @@ -14,6 +14,10 @@ rustPlatform.buildRustPackage rec { hash = "sha256-pgPjkj0nMb5j3EvyJTTDpfmh0WigAcMzoleF5EOqBAM="; }; + patches = [ + ./01-memberless.patch + ]; + cargoHash = "sha256-tQ3uVsy5Dw4c4CbSeASv1TWkqxVYjl/Cjkr00OQEo9c="; meta = with lib; {