diff --git a/REUSE.toml b/REUSE.toml index 388cc61..256b780 100644 --- a/REUSE.toml +++ b/REUSE.toml @@ -23,6 +23,12 @@ SPDX-License-Identifier = "EUPL-1.2" path = ["machines/nixos/compute01/ds-fr/01-smtp-tls.patch", "machines/nixos/compute01/librenms/kanidm.patch", "machines/nixos/compute01/stirling-pdf/*.patch", "machines/nixos/vault01/k-radius/packages/01-python_path.patch", "machines/nixos/web01/crabfit/*.patch", "machines/nixos/web02/cas-eleves/01-pytest-cas.patch", "patches/lix/01-disable-installChecks.patch", "patches/nixpkgs/01-pretalx-environment-file.patch", "patches/nixpkgs/03-crabfit-karla.patch", "patches/nixpkgs/05-netbird-relay.patch"] precedence = "closest" +[[annotations]] +SPDX-FileCopyrightText = ["2024 Tom Hubrecht ", "2024 Maurice Debray "] +SPDX-License-Identifier = "EUPL-1.2" +path = ["patches/nixpkgs/07-kanidm-groups-module.patch", "patches/nixpkgs/08-kanidm-groups-pkgs.patch"] +precedence = "closest" + [[annotations]] SPDX-FileCopyrightText = "2024 Maurice Debray " SPDX-License-Identifier = "EUPL-1.2" diff --git a/default.nix b/default.nix index f12d53a..4bf0416 100644 --- a/default.nix +++ b/default.nix @@ -101,6 +101,16 @@ let ]; copyright = "2024 Tom Hubrecht "; } + { + path = [ + "patches/nixpkgs/07-kanidm-groups-module.patch" + "patches/nixpkgs/08-kanidm-groups-pkgs.patch" + ]; + copyright = [ + "2024 Tom Hubrecht " + "2024 Maurice Debray " + ]; + } { path = [ "patches/nixpkgs/06-netbox-qrcode.patch" ]; copyright = "2024 Maurice Debray "; diff --git a/patches/default.nix b/patches/default.nix index 0c15679..17b705d 100644 --- a/patches/default.nix +++ b/patches/default.nix @@ -30,6 +30,10 @@ in # pretalx env file option (local ./nixpkgs/01-pretalx-environment-file.patch) + + # Kanidm memberless groups provisionning + (local ./nixpkgs/07-kanidm-groups-module.patch) + (local ./nixpkgs/08-kanidm-groups-pkgs.patch) ]; "nixos-unstable" = [ diff --git a/patches/nixpkgs/07-kanidm-groups-module.patch b/patches/nixpkgs/07-kanidm-groups-module.patch new file mode 100644 index 0000000..6a7d347 --- /dev/null +++ b/patches/nixpkgs/07-kanidm-groups-module.patch @@ -0,0 +1,47 @@ +diff --git a/nixos/modules/services/security/kanidm.nix b/nixos/modules/services/security/kanidm.nix +index ab85eed34eea..48722af7332a 100644 +--- a/nixos/modules/services/security/kanidm.nix ++++ b/nixos/modules/services/security/kanidm.nix +@@ -139,6 +139,9 @@ let + + filterPresent = filterAttrs (_: v: v.present); + ++ filterMemberless = filterAttrs (_: v: v.present && v.memberless); ++ filterMemberful = filterAttrs (_: v: v.present && !v.memberless); ++ + provisionStateJson = pkgs.writeText "provision-state.json" ( + builtins.toJSON { inherit (cfg.provision) groups persons systems; } + ); +@@ -442,6 +445,12 @@ in + apply = unique; + default = [ ]; + }; ++ ++ memberless = mkOption { ++ description = "Whether this group is considered memberless, i.e. the list of members is managed imperatively."; ++ type = types.bool; ++ default = false; ++ }; + }; + config.members = concatLists ( + flip mapAttrsToList cfg.provision.persons ( +@@ -757,10 +766,18 @@ in + person: personCfg: + assertGroupsKnown "services.kanidm.provision.persons.${person}.groups" personCfg.groups + ) +- ++ flip mapAttrsToList (filterPresent cfg.provision.groups) ( ++ ++ flip 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} ++ ''; ++ } ++ ) + ++ concatLists ( + flip mapAttrsToList (filterPresent cfg.provision.systems.oauth2) ( + oauth2: oauth2Cfg: diff --git a/patches/nixpkgs/08-kanidm-groups-pkgs.patch b/patches/nixpkgs/08-kanidm-groups-pkgs.patch new file mode 100644 index 0000000..83f4dbf --- /dev/null +++ b/patches/nixpkgs/08-kanidm-groups-pkgs.patch @@ -0,0 +1,106 @@ +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; {