Compare commits

..

4 commits
users2 ... main

Author SHA1 Message Date
ibizaman
973c18edb2 open-webui: more consistent example
Some checks failed
build / path-filter (push) Has been cancelled
format / format (push) Has been cancelled
build / manual (push) Has been cancelled
Demo / path-filter (push) Has been cancelled
Deploy docs / deploy (push) Has been cancelled
Demo / result (push) Has been cancelled
build / build-matrix (push) Has been cancelled
build / tests (push) Has been cancelled
build / Final Results (push) Has been cancelled
Demo / build (map[flake:basic name:homeassistant]) (push) Has been cancelled
Demo / build (map[flake:basic name:nextcloud]) (push) Has been cancelled
Demo / build (map[flake:ldap name:homeassistant]) (push) Has been cancelled
Demo / build (map[flake:ldap name:nextcloud]) (push) Has been cancelled
Demo / build (map[flake:lowlevel name:minimal]) (push) Has been cancelled
Demo / build (map[flake:minimal name:minimal]) (push) Has been cancelled
Demo / build (map[flake:sops name:minimal]) (push) Has been cancelled
Demo / build (map[flake:sso name:nextcloud]) (push) Has been cancelled
2026-06-25 22:33:29 +02:00
ibizaman
271bc1dc0b zfs: print snapshots on activation 2026-06-25 22:33:29 +02:00
ibizaman
3432df47f1 davfs: handle password file 2026-06-25 22:33:29 +02:00
ibizaman
ba0d3cac4a test: fix lib pretty printer 2026-06-25 22:33:29 +02:00
11 changed files with 54 additions and 143 deletions

View file

@ -4013,9 +4013,6 @@
"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.html#services-home-assistant-options-shb.home-assistant.ldap.enable"
],

View file

@ -352,12 +352,16 @@ let
"expected"
"result"
];
nativeBuildInputs = [
(pkgs.python3.withPackages (ps: [ ps.deepdiff ] ++ ps.deepdiff.optional-dependencies.cli))
];
}
''
echo "${name} failed (- expected, + result)" > $out
cp ''${expectedPath} ''${expectedPath}.json
cp ''${resultPath} ''${resultPath}.json
${pkgs.deepdiff}/bin/deep diff ''${expectedPath}.json ''${resultPath}.json >> $out
deep diff ''${expectedPath}.json ''${resultPath}.json >> $out
''
);

View file

@ -75,6 +75,30 @@ in
config = {
services.davfs2.enable = builtins.length cfg.mounts > 0;
systemd.services = lib.optionalAttrs (builtins.length cfg.mounts > 0) {
davfs2-password-generation = {
wantedBy = [ "multi-user.target" ];
serviceConfig.Type = "oneshot";
script = ''
mkdir -p /etc/davfs2
[ -f /etc/davfs2/secrets ] && mv /etc/davfs2/secrets /etc/davfs2/secrets.prev
touch /etc/davfs2/secrets
chown root: /etc/davfs2
chown root: /etc/davfs2/secrets
chmod 700 /etc/davfs2
chmod 600 /etc/davfs2/secrets
''
+ (
let
mkPasswordCmd = cfg': ''
printf "%s %s %s\n" "${cfg'.mountPoint}" "${cfg'.username}" "$(cat ${cfg'.passwordFile})" >> /etc/davfs2/secrets
'';
in
lib.concatStringsSep "\n" (map mkPasswordCmd cfg.mounts)
);
};
};
systemd.mounts =
let
mkMountCfg = c: {
@ -82,6 +106,7 @@ in
description = "Webdav mount point";
after = [ "network-online.target" ];
wants = [ "network-online.target" ];
wantedBy = [ "multi-user.target" ];
what = c.remoteUrl;
where = c.mountPoint;

View file

@ -249,9 +249,10 @@ in
let
recursiveFlag = lib.optionalString cfg.snapshotBeforeActivation.recursive "-r";
in
lib.concatMapStringsSep "\n" (ds: "zfs snapshot ${recursiveFlag} ${ds}@\"$name\"") (
lib.uniqueStrings datasets
)
lib.concatMapStringsSep "\n" (ds: ''
echo "Taking ZFS snapshot of ${ds}@$name"
zfs snapshot ${recursiveFlag} ${ds}@"$name"
'') (lib.uniqueStrings datasets)
)
);
};

View file

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

View file

@ -127,34 +127,16 @@ shb.home-assistant.ldap
enable = true;
host = "127.0.0.1";
port = config.shb.lldap.webUIListenPort;
# 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
];
userGroup = "homeassistant_user";
};
```
This will create a LDAP group declaratively
and add the user `homeAssitantUser` as a user
and the user `homeAssitantAdmin` as an admin.
LDAP users can be added to the groups imperatively too through the LLDAP web UI.
And that's it.
Now, go to the LDAP server at `http://ldap.example.com`,
create the `home-assistant_user` group,
create a user and add it to one or both groups.
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}

View file

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

View file

@ -59,12 +59,12 @@ in
WEBUI_NAME = "SelfHostBlocks";
OLLAMA_BASE_URL = "http://127.0.0.1:''${toString config.services.ollama.port}";
RAG_EMBEDDING_ENGINE = "ollama";
RAG_EMBEDDING_MODEL = "nomic-embed-text:v1.5";
ENABLE_OPENAI_API = "True";
OPENAI_API_BASE_URL = "http://127.0.0.1:''${toString config.services.llama-cpp.port}";
ENABLE_WEB_SEARCH = "True";
RAG_EMBEDDING_ENGINE = "openai";
}
'';
};

View file

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

View file

@ -34,6 +34,7 @@ in
expected = {
services.davfs2.enable = false;
systemd.mounts = [ ];
systemd.services = { };
};
expr = testConfig { };
};

View file

@ -1,9 +1,4 @@
{
pkgs,
shb,
lib,
...
}:
{ pkgs, shb, ... }:
let
commonTestScript = shb.test.mkScripts {
hasSSL = { node, ... }: !(isNull node.config.shb.home-assistant.ssl);
@ -92,9 +87,6 @@ let
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 = [
{
nextPageExpect = [
@ -117,71 +109,6 @@ 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 =
{ config, ... }:
{
@ -193,13 +120,12 @@ let
ldap =
{ config, ... }:
{
shb.lldap.debug = lib.mkForce true;
shb.home-assistant = {
ldap = {
enable = true;
host = "127.0.0.1";
port = config.shb.lldap.webUIListenPort;
userGroup = "user_group";
userGroup = "homeassistant_user";
};
};
};
@ -322,11 +248,6 @@ in
ldap = shb.test.runNixOSTest {
name = "homeassistant_ldap";
nodes.client = {
imports = [
clientLdapLogin
];
};
nodes.server = {
imports = [
basic
@ -335,14 +256,9 @@ in
];
};
testScript = commonTestScript.access.override {
waitForPorts =
{ node, ... }:
[
8123
node.config.shb.lldap.webUIListenPort
];
};
nodes.client = { };
testScript = commonTestScript.access;
};
# Not yet supported