Compare commits

...

2 commits
main ... users2

Author SHA1 Message Date
ibizaman
b26023a4ce home-assistant: actually test ldap login 2026-06-23 16:30:10 +02:00
ibizaman
ff60a64131 test: allow to set login button selector 2026-06-22 20:37:54 +02:00
6 changed files with 138 additions and 18 deletions

View file

@ -4013,6 +4013,9 @@
"services-home-assistant-options-shb.home-assistant.ldap": [ "services-home-assistant-options-shb.home-assistant.ldap": [
"services-home-assistant.html#services-home-assistant-options-shb.home-assistant.ldap" "services-home-assistant.html#services-home-assistant-options-shb.home-assistant.ldap"
], ],
"services-home-assistant-options-shb.home-assistant.ldap.adminGroup": [
"services-home-assistant.html#services-home-assistant-options-shb.home-assistant.ldap.adminGroup"
],
"services-home-assistant-options-shb.home-assistant.ldap.enable": [ "services-home-assistant-options-shb.home-assistant.ldap.enable": [
"services-home-assistant.html#services-home-assistant-options-shb.home-assistant.ldap.enable" "services-home-assistant.html#services-home-assistant-options-shb.home-assistant.ldap.enable"
], ],

View file

@ -139,10 +139,16 @@ in
userGroup = lib.mkOption { userGroup = lib.mkOption {
type = lib.types.str; type = lib.types.str;
description = "Group users must belong to to be able to login to Nextcloud."; description = "Group users must belong to to be able to login as a user.";
default = "homeassistant_user"; default = "homeassistant_user";
}; };
adminGroup = lib.mkOption {
type = lib.types.str;
description = "Group users must belong to to be able to login as an admin.";
default = "homeassistant_admin";
};
keepDefaultAuth = lib.mkOption { keepDefaultAuth = lib.mkOption {
type = lib.types.bool; type = lib.types.bool;
description = '' description = ''
@ -277,6 +283,7 @@ in
args = [ args = [
"http://${cfg.ldap.host}:${toString cfg.ldap.port}" "http://${cfg.ldap.host}:${toString cfg.ldap.port}"
cfg.ldap.userGroup cfg.ldap.userGroup
cfg.ldap.adminGroup
]; ];
meta = true; meta = true;
} }

View file

@ -127,16 +127,34 @@ shb.home-assistant.ldap
enable = true; enable = true;
host = "127.0.0.1"; host = "127.0.0.1";
port = config.shb.lldap.webUIListenPort; port = config.shb.lldap.webUIListenPort;
userGroup = "homeassistant_user";
# This is the default:
# userGroup = "homeassistant_user";
# adminGroup = "homeassistant_admin";
};
shb.lldap.ensureGroups = {
${config.shb.home-assistant.ldap.userGroup} = { };
${config.shb.home-assistant.ldap.adminGroup} = { };
};
shb.lldap.ensureUsers.homeAssistantAdmin = {
groups = [
config.shb.home-assistant.ldap.adminGroup
];
};
shb.lldap.ensureUsers.homeAssistantUser = {
groups = [
config.shb.home-assistant.ldap.userGroup
];
}; };
``` ```
And that's it. This will create a LDAP group declaratively
Now, go to the LDAP server at `http://ldap.example.com`, and add the user `homeAssitantUser` as a user
create the `home-assistant_user` group, and the user `homeAssitantAdmin` as an admin.
create a user and add it to one or both groups. LDAP users can be added to the groups imperatively too through the LLDAP web UI.
When that's done, go back to the Home-Assistant server at
`http://home-assistant.example.com` and login with that user.
### With SSO Support {#services-home-assistant-usage-sso} ### With SSO Support {#services-home-assistant-usage-sso}

View file

@ -7,10 +7,10 @@ stdenvNoCC.mkDerivation {
name = "lldap-ha-auth"; name = "lldap-ha-auth";
src = pkgs.fetchFromGitHub { src = pkgs.fetchFromGitHub {
owner = "lldap"; owner = "ibizaman";
repo = "lldap"; repo = "lldap";
rev = "7d1f5abc137821c500de99c94f7579761fc949d8"; rev = "adaf17c70336ec2562d23d1b9775579d62691b51";
sha256 = "sha256-8D+7ww70Ja6Qwdfa+7MpjAAHewtCWNf/tuTAExoUrg0="; sha256 = "sha256-4FqfglEss5MlnxvjP40zbxqtwvB/GGMs7HwK9CBNBUQ=";
}; };
nativeBuildInputs = [ nativeBuildInputs = [

View file

@ -295,6 +295,10 @@ in
type = str; type = str;
default = "[Ll]ogin"; default = "[Ll]ogin";
}; };
loginButtonSelector = mkOption {
type = str;
default = ''get_by_role("button", name=re.compile('${cfg.loginButtonNameRegex}'))'';
};
loginSpawnsNewPage = mkOption { loginSpawnsNewPage = mkOption {
type = bool; type = bool;
default = false; default = false;
@ -412,8 +416,8 @@ in
# Assumes we don't need to login, so skip this. # Assumes we don't need to login, so skip this.
if u['username'] is not None or u['password'] is not None: if u['username'] is not None or u['password'] is not None:
print(f"Clicking button {testCfg['loginButtonNameRegex']}") print("Clicking login button")
page.get_by_role("button", name=re.compile(testCfg['loginButtonNameRegex'])).click() page.${cfg.loginButtonSelector}.click()
for line in u['nextPageExpect']: for line in u['nextPageExpect']:
print(f"Running: {line}") print(f"Running: {line}")
@ -529,11 +533,14 @@ in
ensureUsers = { ensureUsers = {
alice = { alice = {
email = "alice@example.com"; email = "alice@example.com";
# Display name is required by at least home-assistant.
displayName = "Alice Alice";
groups = [ "user_group" ]; groups = [ "user_group" ];
password.result = config.shb.hardcodedsecret.alice.result; password.result = config.shb.hardcodedsecret.alice.result;
}; };
bob = { bob = {
email = "bob@example.com"; email = "bob@example.com";
displayName = "Bob Bob";
# Purposely not adding bob to the user_group # Purposely not adding bob to the user_group
# so we can make sure users only part admins # so we can make sure users only part admins
# can also login normally. # can also login normally.
@ -542,6 +549,7 @@ in
}; };
charlie = { charlie = {
email = "charlie@example.com"; email = "charlie@example.com";
displayName = "Charlie Charlie";
groups = [ "other_group" ]; groups = [ "other_group" ];
password.result = config.shb.hardcodedsecret.charlie.result; password.result = config.shb.hardcodedsecret.charlie.result;
}; };

View file

@ -1,4 +1,9 @@
{ pkgs, shb, ... }: {
pkgs,
shb,
lib,
...
}:
let let
commonTestScript = shb.test.mkScripts { commonTestScript = shb.test.mkScripts {
hasSSL = { node, ... }: !(isNull node.config.shb.home-assistant.ssl); hasSSL = { node, ... }: !(isNull node.config.shb.home-assistant.ssl);
@ -87,6 +92,9 @@ let
test.login = { test.login = {
startUrl = "http://${config.test.fqdn}"; startUrl = "http://${config.test.fqdn}";
usernameFieldSelector = ''get_by_role("textbox", name="Username")'';
passwordFieldSelector = ''get_by_role("textbox", name="Password")'';
loginButtonSelector = ''get_by_role("button", name="Log in")'';
testLoginWith = [ testLoginWith = [
{ {
nextPageExpect = [ nextPageExpect = [
@ -109,6 +117,71 @@ let
}; };
}; };
clientLdapLogin =
{ config, ... }:
{
imports = [
shb.test.baseModule
shb.test.clientLoginModule
];
config = {
virtualisation.memorySize = 4096;
test = {
subdomain = "ha";
};
test.login = {
startUrl = "http://${config.test.fqdn}";
usernameFieldSelector = ''get_by_role("textbox", name="Username")'';
passwordFieldSelector = ''get_by_role("textbox", name="Password")'';
loginButtonSelector = ''get_by_role("button", name="Log in")'';
testLoginWith = [
{
username = "alice";
password = "AlicePassword";
nextPageExpect = [
"expect(page.get_by_text('All set!')).to_be_visible(timeout=30000)"
"page.get_by_role('button', name=re.compile('Finish')).click()"
"expect(page).to_have_title(re.compile('Overview'), timeout=15000)"
];
}
{
username = "alice";
password = "notAlicePassword";
nextPageExpect = [
"expect(page.get_by_text('Invalid!')).to_be_visible()"
];
}
{
username = "bob";
password = "BobPassword";
nextPageExpect = [
"expect(page.get_by_text('All set!')).to_be_visible(timeout=30000)"
"page.get_by_role('button', name=re.compile('Finish')).click()"
"expect(page).to_have_title(re.compile('Overview'), timeout=15000)"
];
}
{
username = "bob";
password = "notBobPassword";
nextPageExpect = [
"expect(page.get_by_text('Invalid!')).to_be_visible()"
];
}
{
username = "charlie";
password = "CharliePassword";
nextPageExpect = [
"expect(page.get_by_text('Invalid!')).to_be_visible()"
];
}
];
};
};
};
https = https =
{ config, ... }: { config, ... }:
{ {
@ -120,12 +193,13 @@ let
ldap = ldap =
{ config, ... }: { config, ... }:
{ {
shb.lldap.debug = lib.mkForce true;
shb.home-assistant = { shb.home-assistant = {
ldap = { ldap = {
enable = true; enable = true;
host = "127.0.0.1"; host = "127.0.0.1";
port = config.shb.lldap.webUIListenPort; port = config.shb.lldap.webUIListenPort;
userGroup = "homeassistant_user"; userGroup = "user_group";
}; };
}; };
}; };
@ -248,6 +322,11 @@ in
ldap = shb.test.runNixOSTest { ldap = shb.test.runNixOSTest {
name = "homeassistant_ldap"; name = "homeassistant_ldap";
nodes.client = {
imports = [
clientLdapLogin
];
};
nodes.server = { nodes.server = {
imports = [ imports = [
basic basic
@ -256,9 +335,14 @@ in
]; ];
}; };
nodes.client = { }; testScript = commonTestScript.access.override {
waitForPorts =
testScript = commonTestScript.access; { node, ... }:
[
8123
node.config.shb.lldap.webUIListenPort
];
};
}; };
# Not yet supported # Not yet supported