From b34a1e57a7dcb4881a12edede08897fd76ee6669 Mon Sep 17 00:00:00 2001 From: ibizaman Date: Thu, 18 Jul 2024 19:36:40 +0200 Subject: [PATCH] declarative creation of ldap users and groups --- flake.nix | 6 + modules/blocks/ldap.nix | 339 ++++++++++++++++++++++++++++++++++++++++ test/blocks/ldap.nix | 334 ++++++++++++++++++++++++++++++++++++++- 3 files changed, 676 insertions(+), 3 deletions(-) diff --git a/flake.nix b/flake.nix index bdfb889..8eaa69f 100644 --- a/flake.nix +++ b/flake.nix @@ -24,6 +24,12 @@ # Remove when this PR is merged: # https://github.com/NixOS/nixpkgs/pull/368325 ./patches/prometheusnodecertexporter.nix + + (originPkgs.fetchpatch { + url = "https://patch-diff.githubusercontent.com/raw/NixOS/nixpkgs/pull/374905.patch"; + hash = "sha256-9n3HYu0QM+MVLJ3FyOHAd1vdtoK+U5wNQiUwz3L6Bqs="; + }) + ]; patchedNixpkgs = originPkgs.applyPatches { name = "nixpkgs-patched"; diff --git a/modules/blocks/ldap.nix b/modules/blocks/ldap.nix index bda49e4..e0a5b55 100644 --- a/modules/blocks/ldap.nix +++ b/modules/blocks/ldap.nix @@ -6,6 +6,35 @@ let contracts = pkgs.callPackage ../contracts {}; fqdn = "${cfg.subdomain}.${cfg.domain}"; + + lldap-cli = pkgs.lldap-cli.overrideAttrs (f: p: { + version = "0-unstable-2024-01-19"; + + src = pkgs.fetchFromGitHub { + owner = "ibizaman"; + repo = "lldap-cli"; + rev = "e07e2cafaa68e926f1256449e4553b01a15f0a0c"; + hash = "sha256-qbXSJmx5spW9iViycRmQdYtI6KfysOxeAv7qI+sz25A="; + }; + }); + + lldap-cli-auth = pkgs.callPackage ({ stdenvNoCC, makeWrapper }: stdenvNoCC.mkDerivation { + name = "lldap-cli"; + + src = lldap-cli; + + nativeBuildInputs = [ + makeWrapper + ]; + + # No quotes around the value for LLDAP_PASSWORD because we want the value to not be enclosed in quotes. + installPhase = '' + makeWrapper ${lldap-cli}/bin/lldap-cli $out/bin/lldap-cli \ + --set LLDAP_USERNAME "admin" \ + --run 'export LLDAP_PASSWORD="$(cat ${cfg.ldapUserPassword.result.path})"' \ + --set LLDAP_HTTPURL "http://${config.services.lldap.settings.http_host}:${toString config.services.lldap.settings.http_port}" + ''; + }) {}; in { options.shb.ldap = { @@ -118,6 +147,133 @@ in }; }; }; + + groups = lib.mkOption { + description = "LDAP Groups to manage declaratively."; + default = {}; + example = lib.literalExpression '' + { + family = {}; + } + ''; + type = lib.types.attrsOf (lib.types.submodule { + options = {}; + }); + }; + + deleteUnmanagedUsers = lib.mkOption { + description = "Do not delete users that are not defined here."; + type = lib.types.bool; + default = false; + }; + + users = lib.mkOption { + description = '' + LDAP Users to manage declaratively. + + Each field is provided in two versions. + The first version, without prefix, sets the attribute + every time the configuration is applied, overwriting any changes in the UI. + + The second version, with the "initial" prefix, sets + the attribute only once, on user creation. Any changes + in the UI will survive next time the configuration is applied. + + If both are set, the one without the "initial" prefix wins. + ''; + default = {}; + type = lib.types.attrsOf (lib.types.submodule { + options = { + email = lib.mkOption { + description = '' + Email address. + + Must be set: set either "email" field or "initialEmail" field. + ''; + type = lib.types.nullOr lib.types.str; + default = null; + }; + + initialEmail = lib.mkOption { + description = '' + Email address. + + Must be set: set either "email" field or "initialEmail" field. + ''; + type = lib.types.nullOr lib.types.str; + default = null; + }; + + displayName = lib.mkOption { + description = "Display name. Optional"; + type = lib.types.nullOr lib.types.str; + default = null; + }; + + initialDisplayName = lib.mkOption { + description = "Display name. Optional"; + type = lib.types.nullOr lib.types.str; + default = null; + }; + + firstName = lib.mkOption { + description = "First name. Optional"; + type = lib.types.nullOr lib.types.str; + default = null; + }; + + initialFirstName = lib.mkOption { + description = "First name. Optional"; + type = lib.types.nullOr lib.types.str; + default = null; + }; + + lastName = lib.mkOption { + description = "Last name. Optional."; + type = lib.types.nullOr lib.types.str; + default = null; + }; + + initialLastName = lib.mkOption { + description = "Last name. Optional."; + type = lib.types.nullOr lib.types.str; + default = null; + }; + + groups = lib.mkOption { + description = "Groups this user is member of. The group must exist."; + type = lib.types.listOf lib.types.str; + default = []; + }; + + password = lib.mkOption { + description = "User password. Optional."; + type = lib.types.nullOr (lib.types.submodule { + options = contracts.secret.mkRequester { + mode = "0440"; + owner = "lldap"; + group = "lldap"; + restartUnits = [ "lldap.service" ]; + }; + }); + default = null; + }; + + initialPassword = lib.mkOption { + description = "User password. Optional."; + type = lib.types.nullOr (lib.types.submodule { + options = contracts.secret.mkRequester { + mode = "0440"; + owner = "lldap"; + group = "lldap"; + restartUnits = [ "lldap.service" ]; + }; + }); + default = null; + }; + }; + }); + }; }; @@ -171,5 +327,188 @@ in verbose = cfg.debug; }; }; + + environment.systemPackages = [ + lldap-cli-auth + ]; + + # $ lldap-cli schema attribute user list + # + # Name Type Is list Is visible Is editable + # ---- ---- ------- ---------- ----------- + # avatar JpegPhoto false true true + # creation_date DateTime false true false + # display_name String false true true + # first_name String false true true + # last_name String false true true + # mail String false true true + # user_id String false true false + # uuid String false true false + + + # $ lldap-cli schema attribute group list + # + # Name Type Is list Is visible Is editable + # ---- ---- ------- ---------- ----------- + # creation_date DateTime false true false + # display_name String false true true + # group_id Integer false true false + # uuid String false true false + + assertions = [ + (let + uppercases = builtins.filter (n: lib.strings.toLower n != n) (lib.mapAttrsToList (uid: u: uid) cfg.users); + in { + assertion = uppercases == []; + message = "Users ID for LLDAP can only be lowercase, found: ${lib.concatStringsSep "," uppercases}"; + }) + # (let + # unknownGroups = lib.flatten(lib.mapAttrsToList (uid: u: lib.subtractLists (map (gid: g: gid) cfg.groups) u.groups) cfg.users); + # in { + # assertion = unknownGroups == []; + # message = "All groups defined in user field must exist, the following ones are not defined: ${lib.concatStringsSep "," unknownGroups}"; + # }) + ]; + + systemd.services.lldap.postStart = + let + configFile = (pkgs.formats.toml {}).generate "lldap_config.toml" config.services.lldap.settings; + + login = ['' + set -euo pipefail + + sleep 3 + + export LLDAP_USERNAME=admin + export LLDAP_PASSWORD=$(cat ${cfg.ldapUserPassword.result.path}) + export LLDAP_HTTPURL=http://${config.services.lldap.settings.http_host}:${toString config.services.lldap.settings.http_port} + + eval $(${lldap-cli}/bin/lldap-cli login) + '']; + + deleteGroups = ['' + allUids=(${lib.concatStringsSep " " ( + (lib.mapAttrsToList (id: g: id) cfg.groups) + ++ [ "lldap_admin" "lldap_password_manager" "lldap_strict_readonly" ]) + }) + echo All managed groups are: ''${allUids[*]} + echo Other groups will be deleted if any + + for uid in $(${lldap-cli}/bin/lldap-cli group list | ${pkgs.jq}/bin/jq -r 'map(.displayName)[]'); do + if [[ ! " ''${allUids[*]} " =~ [[:space:]]''${uid}[[:space:]] ]]; then + echo Deleting group $uid + ${lldap-cli}/bin/lldap-cli group del $uid + fi + done + '']; + + createGroups = ['' + existingUids=$(${lldap-cli}/bin/lldap-cli group list | ${pkgs.jq}/bin/jq -r 'map(.displayName)[]') + managedUids=(${lib.concatStringsSep " " ( + (lib.mapAttrsToList (id: g: id) cfg.groups) + ++ [ "lldap_admin" "lldap_password_manager" "lldap_strict_readonly" ]) + }) + echo All managed groups are: ''${managedUids[*]} + for uid in ''${managedUids[*]}; do + if [[ ! " ''${existingUids[*]} " =~ [[:space:]]''${uid}[[:space:]] ]]; then + echo "Creating group $uid" + ${lldap-cli}/bin/lldap-cli group add $uid + fi + done + '']; + + deleteUsers = ['' + allUids=(${lib.concatStringsSep " " ( + (lib.mapAttrsToList (uid: u: uid) cfg.users) + ++ [ "admin" ]) + }) + echo All managed users are: ''${allUids[*]} + echo Other users will be deleted if any + for uid in $(${lldap-cli}/bin/lldap-cli user list all | ${pkgs.jq}/bin/jq -r 'map(.id)[]'); do + if [[ ! " ''${allUids[*]} " =~ [[:space:]]''${uid}[[:space:]] ]]; then + echo Deleting user $uid + ${lldap-cli}/bin/lldap-cli user del $uid + fi + done + '']; + + createUsers = lib.mapAttrsToList (uid: u: let + email = if u.email != null then u.email else u.initialEmail; + + password = if u.password != null then u.password else u.initialPassword; + displayName = if u.displayName != null then u.displayName else u.initialDisplayName; + firstName = if u.firstName != null then u.firstName else u.initialFirstName; + lastName = if u.lastName != null then u.lastName else u.initialLastName; + + allCreateAttributes = + lib.optionals (password != null) [''-p "$(cat ${password.result.path})"''] + ++ lib.optionals (displayName != null) [''-d "${displayName}"''] + ++ lib.optionals (firstName != null) [''-f "${firstName}"''] + ++ lib.optionals (lastName != null) [''-l "${lastName}"'']; + + allUpdateAttributes = + lib.optionals (u.email != null) [''mail "${u.email}"''] + ++ lib.optionals (u.password != null) [''password "$(cat ${u.password.result.path})"''] + ++ lib.optionals (u.displayName != null) [''display_name "${u.displayName}"''] + ++ lib.optionals (u.firstName != null) [''first_name "${u.firstName}"''] + ++ lib.optionals (u.lastName != null) [''last_name "${u.lastName}"'']; + in '' + existingUids=($(${lldap-cli}/bin/lldap-cli user list all | ${pkgs.jq}/bin/jq -r 'map(.id)[]')) + managedUids=(${lib.concatStringsSep " " ( + (lib.mapAttrsToList (id: u: id) cfg.users) + ++ [ "admin" ]) + }) + echo All managed users are: ''${managedUids[*]} + echo Existing managed users are: ''${existingUids[*]} + set -x + for uid in ''${managedUids[*]}; do + echo "Checking user $uid" + if [[ ! " ''${existingUids[*]} " =~ [[:space:]]''${uid}[[:space:]] ]]; then + echo "Creating user $uid" + ${lldap-cli}/bin/lldap-cli user add ${uid} "${email}" ${lib.concatStringsSep " " allCreateAttributes} + else + echo "Updating user $uid" + ${lib.concatMapStringsSep "\n " (x: "${lldap-cli}/bin/lldap-cli user update set ${uid} ${x}") allUpdateAttributes} + fi + done + set +x + '') cfg.users; + + addToGroups = lib.mapAttrsToList (uid: u: '' + existingUids=$(${lldap-cli}/bin/lldap-cli user group list ${uid}) + managedUids=(${lib.concatStringsSep " " u.groups}) + echo All managed groups for user ${uid} are: ''${managedUids[*]} + for gid in ''${managedUids[*]}; do + if [[ ! " ''${existingUids[*]} " =~ [[:space:]]''${gid}[[:space:]] ]]; then + echo "Adding group $gid to user ${uid}" + ${lldap-cli}/bin/lldap-cli user group add \ + ${uid} \ + $gid + fi + done + '') cfg.users; + + deleteFromGroups = lib.mapAttrsToList (uid: u: '' + managedUids=(${lib.concatStringsSep " " u.groups}) + echo All managed groups for user ${uid} are: ''${managedUids[*]} + for gid in $(${lldap-cli}/bin/lldap-cli user group list ${uid}); do + if [[ ! " ''${managedUids[*]} " =~ [[:space:]]''${gid}[[:space:]] ]]; then + echo "Removing group $gid from user $uid" + ${lldap-cli}/bin/lldap-cli user group del \ + ${uid} \ + $gid + fi + done + '') cfg.users; + in + lib.concatStringsSep "\n\n" ( + login + ++ deleteGroups + ++ createGroups + ++ lib.optionals cfg.deleteUnmanagedUsers deleteUsers + ++ createUsers + ++ addToGroups + ++ deleteFromGroups + ); }; } diff --git a/test/blocks/ldap.nix b/test/blocks/ldap.nix index eeb23cc..4d3bb95 100644 --- a/test/blocks/ldap.nix +++ b/test/blocks/ldap.nix @@ -28,7 +28,8 @@ in domain = "example.com"; ldapUserPassword.result = config.shb.hardcodedsecret.ldapUserPassword.result; jwtSecret.result = config.shb.hardcodedsecret.jwtSecret.result; - debug = true; + deleteUnmanagedUsers = true; + debug = false; # Really verbose. }; shb.hardcodedsecret.ldapUserPassword = { request = config.shb.ldap.ldapUserPassword.request; @@ -39,13 +40,131 @@ in settings.content = "jwtSecret"; }; + specialisation.withGroupA.configuration = { + shb.ldap = { + groups."groupA" = {}; + }; + }; + specialisation.withUserA.configuration = { + shb.ldap = { + groups."groupA" = {}; + users = { + "user_a" = { + email = "userA@test.com"; + displayName = "UserA"; + firstName = "User"; + lastName = "A"; + groups = [ "groupA" ]; + password.result = config.specialisation.withUserA.configuration.shb.hardcodedsecret.user_a.result; + }; + }; + }; + shb.hardcodedsecret.user_a = { + request = config.specialisation.withUserA.configuration.shb.ldap.users."user_a".password.request; + settings.content = "userpasswordA"; # must be longer than 8 characters. + }; + }; + specialisation.withOtherGroup.configuration = { + shb.ldap = { + groups."groupB" = {}; + groups."groupC" = {}; + users = { + "user_a" = { + email = "userA@test.com"; + displayName = "UserA"; + firstName = "User"; + lastName = "A"; + groups = [ "groupB" "groupC" ]; + password.result = config.specialisation.withUserA.configuration.shb.hardcodedsecret.user_a.result; + }; + }; + }; + shb.hardcodedsecret.user_a = { + request = config.specialisation.withUserA.configuration.shb.ldap.users."user_a".password.request; + settings.content = "userpasswordA"; # must be longer than 8 characters. + }; + }; + specialisation.removeFromGroup.configuration = { + shb.ldap = { + groups."groupB" = {}; + groups."groupC" = {}; + users = { + "user_a" = { + email = "userA@test.com"; + displayName = "UserA"; + firstName = "User"; + lastName = "A"; + groups = [ "groupB" ]; + password.result = config.specialisation.withUserA.configuration.shb.hardcodedsecret.user_a.result; + }; + }; + }; + shb.hardcodedsecret.user_a = { + request = config.specialisation.withUserA.configuration.shb.ldap.users."user_a".password.request; + settings.content = "userpasswordA"; # must be longer than 8 characters. + }; + }; + specialisation.changeAttributes.configuration = { + shb.ldap = { + groups."groupB" = {}; + users = { + "user_a" = { + email = "userA_2@test.com"; + displayName = "UserA_2"; + firstName = "User_2"; + lastName = "A_2"; + groups = [ "groupB" ]; + password.result = config.specialisation.withUserA.configuration.shb.hardcodedsecret.user_a.result; + }; + }; + }; + shb.hardcodedsecret.user_a = { + request = config.specialisation.withUserA.configuration.shb.ldap.users."user_a".password.request; + settings.content = "userpasswordA_2"; # must be longer than 8 characters. + }; + }; + specialisation.noChangeAttributes.configuration = { + shb.ldap = { + groups."groupB" = {}; + users = { + "user_a" = { + initialEmail = "userA_3@test.com"; + initialDisplayName = "UserA_3"; + initialFirstName = "User_3"; + initialLastName = "A_3"; + groups = [ "groupB" ]; + initialPassword.result = config.specialisation.withUserA.configuration.shb.hardcodedsecret.user_a.result; + }; + }; + }; + shb.hardcodedsecret.user_a = { + request = config.specialisation.withUserA.configuration.shb.ldap.users."user_a".password.request; + settings.content = "userpasswordA_3"; # must be longer than 8 characters. + }; + }; + specialisation.leaveUnmanagedUser.configuration = { + shb.ldap = { + deleteUnmanagedUsers = lib.mkForce false; + groups."groupA" = {}; + users = {}; + }; + }; + networking.firewall.allowedTCPPorts = [ 80 ]; # nginx port }; nodes.client = {}; # Inspired from https://github.com/lldap/lldap/blob/33f50d13a2e2d24a3e6bb05a148246bc98090df0/example_configs/lldap-ha-auth.sh - testScript = { nodes, ... }: '' + testScript = { nodes, ... }: let + withGroupA = "${nodes.server.system.build.toplevel}/specialisation/withGroupA"; + withUserA = "${nodes.server.system.build.toplevel}/specialisation/withUserA"; + withOtherGroup = "${nodes.server.system.build.toplevel}/specialisation/withOtherGroup"; + removeFromGroup = "${nodes.server.system.build.toplevel}/specialisation/removeFromGroup"; + changeAttributes = "${nodes.server.system.build.toplevel}/specialisation/changeAttributes"; + noChangeAttributes = "${nodes.server.system.build.toplevel}/specialisation/noChangeAttributes"; + leaveUnmanagedUser = "${nodes.server.system.build.toplevel}/specialisation/leaveUnmanagedUser"; + in '' import json start_all() @@ -82,13 +201,222 @@ in "curl -f -s -X POST " + """ -H "Content-type: application/json" """ + """ -H "Host: ldap.example.com" """ - + """ -H "Authorization: Bearer {token}" """.format(token=token) + + f""" -H "Authorization: Bearer {token}" """ + " http://server/api/graphql " + """ -d '{"variables": {"id": "admin"}, "query":"query($id:String!){user(userId:$id){displayName groups{displayName}}}"}' """ ))['data'] assert data['user']['displayName'] == "Administrator" assert data['user']['groups'][0]['displayName'] == "lldap_admin" + + data = json.loads(client.succeed( + "curl -f -s -X POST " + + """ -H "Content-type: application/json" """ + + """ -H "Host: ldap.example.com" """ + + f""" -H "Authorization: Bearer {token}" """ + + " http://server/api/graphql " + + """ -d '{"variables": {"id": "user_a"}, "query":"query($id:String!){user(userId:$id){displayName groups{displayName}}}"}' """ + ))['data'] + + + + print(server.succeed("cat /run/current-system/sw/bin/lldap-cli")) + + with subtest("Add groupA"): + groups = [x["displayName"] for x in json.loads(server.succeed("lldap-cli group list"))] + print("GROUPS=", groups) + if "groupA" in groups: + raise Exception("Shouldn't have found groupA yet.") + + server.succeed( + "${withGroupA}/bin/switch-to-configuration test >&2" + ) + + groups = [x["displayName"] for x in json.loads(server.succeed("lldap-cli group list"))] + print("GROUPS=", groups) + if "groupA" not in groups: + raise Exception("Should have found groupA.") + + with subtest("Add user_a"): + users = [x["id"] for x in json.loads(server.succeed("lldap-cli user list all"))] + print("USERS=", users) + if "user_a" in users: + raise Exception("Shouldn't have found user_a yet.") + + server.succeed( + "${withUserA}/bin/switch-to-configuration test >&2" + ) + + users = [x["id"] for x in json.loads(server.succeed("lldap-cli user list all"))] + print("USERS=", users) + if "user_a" not in users: + raise Exception("Should have found user_a.") + + groups = server.succeed("lldap-cli user group list user_a").splitlines() + if "groupA" not in groups: + raise Exception("Should have found groupA.") + + with subtest("auth with user_a"): + token = json.loads(client.succeed( + "curl -f -s -X POST " + + """ -H "Content-type: application/json" """ + + """ -H "Host: ldap.example.com" """ + + " http://server/auth/simple/login " + + """ -d '{"username": "user_a", "password": "userpasswordA"}' """ + ))['token'] + + data = json.loads(client.succeed( + "curl -f -s -X POST " + + """ -H "Content-type: application/json" """ + + """ -H "Host: ldap.example.com" """ + + f""" -H "Authorization: Bearer {token}" """ + + " http://server/api/graphql " + + """ -d '{"variables": {"id": "user_a"}, "query":"query($id:String!){user(userId:$id){displayName groups{displayName}}}"}' """ + ))['data'] + + if data['user']['displayName'] != "UserA": + raise Exception("DisplayName is not UserA, got:", data['user']['displayName']) + + with subtest("With other group"): + server.succeed( + "${withOtherGroup}/bin/switch-to-configuration test >&2" + ) + + groups = [x["displayName"] for x in json.loads(server.succeed("lldap-cli group list"))] + print("GROUPS=", groups) + if "groupA" in groups: + raise Exception("Should not have found groupA.") + if "groupB" not in groups: + raise Exception("Should have found groupB.") + if "groupC" not in groups: + raise Exception("Should have found groupC.") + + groups = server.succeed("lldap-cli user group list user_a").splitlines() + if "groupA" in groups: + raise Exception("Should not have found groupA.") + if "groupB" not in groups: + raise Exception("Should have found groupB.") + if "groupC" not in groups: + raise Exception("Should have found groupC.") + + with subtest("Remove from group"): + server.succeed( + "${removeFromGroup}/bin/switch-to-configuration test >&2" + ) + + groups = [x["displayName"] for x in json.loads(server.succeed("lldap-cli group list"))] + print("GROUPS=", groups) + if "groupA" in groups: + raise Exception("Should not have found groupA.") + if "groupB" not in groups: + raise Exception("Should have found groupB.") + if "groupC" not in groups: + raise Exception("Should have found groupC.") + + groups = server.succeed("lldap-cli user group list user_a").splitlines() + if "groupA" in groups: + raise Exception("Should not have found groupA.") + if "groupB" not in groups: + raise Exception("Should have found groupB.") + if "groupC" in groups: + raise Exception("Should not have found groupC.") + + with subtest("change attributes"): + server.succeed( + "${changeAttributes}/bin/switch-to-configuration test >&2" + ) + + token = json.loads(client.succeed( + "curl -f -s -X POST " + + """ -H "Content-type: application/json" """ + + """ -H "Host: ldap.example.com" """ + + " http://server/auth/simple/login " + + """ -d '{"username": "user_a", "password": "userpasswordA_2"}' """ + ))['token'] + + data = json.loads(client.succeed( + "curl -f -s -X POST " + + """ -H "Content-type: application/json" """ + + """ -H "Host: ldap.example.com" """ + + f""" -H "Authorization: Bearer {token}" """ + + " http://server/api/graphql " + + """ -d '{"variables": {"id": "user_a"}, "query":"query($id:String!){user(userId:$id){email displayName firstName lastName}}"}' """ + ))['data'] + + if data['user']['email'] != "userA_2@test.com": + raise Exception("email is not userA_2@test.com_2, got:", data['user']['email']) + + if data['user']['displayName'] != "UserA_2": + raise Exception("displayName is not UserA_2, got:", data['user']['displayName']) + + if data['user']['firstName'] != "User_2": + raise Exception("firstName is not User_2_2, got:", data['user']['firstName']) + + if data['user']['lastName'] != "A_2": + raise Exception("lastName is not A_2, got:", data['user']['lastName']) + + with subtest("do not change attributes"): + server.succeed( + "${noChangeAttributes}/bin/switch-to-configuration test >&2" + ) + + token = json.loads(client.succeed( + "curl -f -s -X POST " + + """ -H "Content-type: application/json" """ + + """ -H "Host: ldap.example.com" """ + + " http://server/auth/simple/login " + + """ -d '{"username": "user_a", "password": "userpasswordA_2"}' """ + ))['token'] + + data = json.loads(client.succeed( + "curl -f -s -X POST " + + """ -H "Content-type: application/json" """ + + """ -H "Host: ldap.example.com" """ + + f""" -H "Authorization: Bearer {token}" """ + + " http://server/api/graphql " + + """ -d '{"variables": {"id": "user_a"}, "query":"query($id:String!){user(userId:$id){email displayName firstName lastName}}"}' """ + ))['data'] + + if data['user']['email'] != "userA_2@test.com": + raise Exception("email is not userA_2@test.com_2, got:", data['user']['email']) + + if data['user']['displayName'] != "UserA_2": + raise Exception("displayName is not UserA_2, got:", data['user']['displayName']) + + if data['user']['firstName'] != "User_2": + raise Exception("firstName is not User_2_2, got:", data['user']['firstName']) + + if data['user']['lastName'] != "A_2": + raise Exception("lastName is not A_2, got:", data['user']['lastName']) + + with subtest("Delete user"): + server.succeed( + "${withGroupA}/bin/switch-to-configuration test >&2" + ) + + users = [x["id"] for x in json.loads(server.succeed("lldap-cli user list all"))] + print("USERS=", users) + if "user_a" in users: + raise Exception("Should not have found user_a.") + + with subtest("Add again user_a"): + server.succeed( + "${withUserA}/bin/switch-to-configuration test >&2" + ) + + users = [x["id"] for x in json.loads(server.succeed("lldap-cli user list all"))] + print("USERS=", users) + if "user_a" not in users: + raise Exception("Should have found user_a.") + + server.succeed( + "${leaveUnmanagedUser}/bin/switch-to-configuration test >&2" + ) + + users = [x["id"] for x in json.loads(server.succeed("lldap-cli user list all"))] + print("USERS=", users) + if "user_a" not in users: + raise Exception("Should have found user_a.") ''; }; }