Compare commits
4 commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
973c18edb2 | ||
|
|
271bc1dc0b | ||
|
|
3432df47f1 | ||
|
|
ba0d3cac4a |
11 changed files with 54 additions and 143 deletions
|
|
@ -4013,9 +4013,6 @@
|
||||||
"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"
|
||||||
],
|
],
|
||||||
|
|
|
||||||
|
|
@ -352,12 +352,16 @@ let
|
||||||
"expected"
|
"expected"
|
||||||
"result"
|
"result"
|
||||||
];
|
];
|
||||||
|
|
||||||
|
nativeBuildInputs = [
|
||||||
|
(pkgs.python3.withPackages (ps: [ ps.deepdiff ] ++ ps.deepdiff.optional-dependencies.cli))
|
||||||
|
];
|
||||||
}
|
}
|
||||||
''
|
''
|
||||||
echo "${name} failed (- expected, + result)" > $out
|
echo "${name} failed (- expected, + result)" > $out
|
||||||
cp ''${expectedPath} ''${expectedPath}.json
|
cp ''${expectedPath} ''${expectedPath}.json
|
||||||
cp ''${resultPath} ''${resultPath}.json
|
cp ''${resultPath} ''${resultPath}.json
|
||||||
${pkgs.deepdiff}/bin/deep diff ''${expectedPath}.json ''${resultPath}.json >> $out
|
deep diff ''${expectedPath}.json ''${resultPath}.json >> $out
|
||||||
''
|
''
|
||||||
);
|
);
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -75,6 +75,30 @@ in
|
||||||
config = {
|
config = {
|
||||||
services.davfs2.enable = builtins.length cfg.mounts > 0;
|
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 =
|
systemd.mounts =
|
||||||
let
|
let
|
||||||
mkMountCfg = c: {
|
mkMountCfg = c: {
|
||||||
|
|
@ -82,6 +106,7 @@ in
|
||||||
description = "Webdav mount point";
|
description = "Webdav mount point";
|
||||||
after = [ "network-online.target" ];
|
after = [ "network-online.target" ];
|
||||||
wants = [ "network-online.target" ];
|
wants = [ "network-online.target" ];
|
||||||
|
wantedBy = [ "multi-user.target" ];
|
||||||
|
|
||||||
what = c.remoteUrl;
|
what = c.remoteUrl;
|
||||||
where = c.mountPoint;
|
where = c.mountPoint;
|
||||||
|
|
|
||||||
|
|
@ -249,9 +249,10 @@ in
|
||||||
let
|
let
|
||||||
recursiveFlag = lib.optionalString cfg.snapshotBeforeActivation.recursive "-r";
|
recursiveFlag = lib.optionalString cfg.snapshotBeforeActivation.recursive "-r";
|
||||||
in
|
in
|
||||||
lib.concatMapStringsSep "\n" (ds: "zfs snapshot ${recursiveFlag} ${ds}@\"$name\"") (
|
lib.concatMapStringsSep "\n" (ds: ''
|
||||||
lib.uniqueStrings datasets
|
echo "Taking ZFS snapshot of ${ds}@$name"
|
||||||
)
|
zfs snapshot ${recursiveFlag} ${ds}@"$name"
|
||||||
|
'') (lib.uniqueStrings datasets)
|
||||||
)
|
)
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
|
||||||
|
|
@ -139,16 +139,10 @@ 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 as a user.";
|
description = "Group users must belong to to be able to login to Nextcloud.";
|
||||||
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 = ''
|
||||||
|
|
@ -283,7 +277,6 @@ 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;
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -127,34 +127,16 @@ 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
|
|
||||||
];
|
|
||||||
};
|
};
|
||||||
```
|
```
|
||||||
|
|
||||||
This will create a LDAP group declaratively
|
And that's it.
|
||||||
and add the user `homeAssitantUser` as a user
|
Now, go to the LDAP server at `http://ldap.example.com`,
|
||||||
and the user `homeAssitantAdmin` as an admin.
|
create the `home-assistant_user` group,
|
||||||
LDAP users can be added to the groups imperatively too through the LLDAP web UI.
|
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}
|
### With SSO Support {#services-home-assistant-usage-sso}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -7,10 +7,10 @@ stdenvNoCC.mkDerivation {
|
||||||
name = "lldap-ha-auth";
|
name = "lldap-ha-auth";
|
||||||
|
|
||||||
src = pkgs.fetchFromGitHub {
|
src = pkgs.fetchFromGitHub {
|
||||||
owner = "ibizaman";
|
owner = "lldap";
|
||||||
repo = "lldap";
|
repo = "lldap";
|
||||||
rev = "adaf17c70336ec2562d23d1b9775579d62691b51";
|
rev = "7d1f5abc137821c500de99c94f7579761fc949d8";
|
||||||
sha256 = "sha256-4FqfglEss5MlnxvjP40zbxqtwvB/GGMs7HwK9CBNBUQ=";
|
sha256 = "sha256-8D+7ww70Ja6Qwdfa+7MpjAAHewtCWNf/tuTAExoUrg0=";
|
||||||
};
|
};
|
||||||
|
|
||||||
nativeBuildInputs = [
|
nativeBuildInputs = [
|
||||||
|
|
|
||||||
|
|
@ -59,12 +59,12 @@ in
|
||||||
WEBUI_NAME = "SelfHostBlocks";
|
WEBUI_NAME = "SelfHostBlocks";
|
||||||
|
|
||||||
OLLAMA_BASE_URL = "http://127.0.0.1:''${toString config.services.ollama.port}";
|
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";
|
RAG_EMBEDDING_MODEL = "nomic-embed-text:v1.5";
|
||||||
|
|
||||||
ENABLE_OPENAI_API = "True";
|
ENABLE_OPENAI_API = "True";
|
||||||
OPENAI_API_BASE_URL = "http://127.0.0.1:''${toString config.services.llama-cpp.port}";
|
OPENAI_API_BASE_URL = "http://127.0.0.1:''${toString config.services.llama-cpp.port}";
|
||||||
ENABLE_WEB_SEARCH = "True";
|
ENABLE_WEB_SEARCH = "True";
|
||||||
RAG_EMBEDDING_ENGINE = "openai";
|
|
||||||
}
|
}
|
||||||
'';
|
'';
|
||||||
};
|
};
|
||||||
|
|
|
||||||
|
|
@ -295,10 +295,6 @@ 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;
|
||||||
|
|
@ -416,8 +412,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("Clicking login button")
|
print(f"Clicking button {testCfg['loginButtonNameRegex']}")
|
||||||
page.${cfg.loginButtonSelector}.click()
|
page.get_by_role("button", name=re.compile(testCfg['loginButtonNameRegex'])).click()
|
||||||
|
|
||||||
for line in u['nextPageExpect']:
|
for line in u['nextPageExpect']:
|
||||||
print(f"Running: {line}")
|
print(f"Running: {line}")
|
||||||
|
|
@ -533,14 +529,11 @@ 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.
|
||||||
|
|
@ -549,7 +542,6 @@ 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;
|
||||||
};
|
};
|
||||||
|
|
|
||||||
|
|
@ -34,6 +34,7 @@ in
|
||||||
expected = {
|
expected = {
|
||||||
services.davfs2.enable = false;
|
services.davfs2.enable = false;
|
||||||
systemd.mounts = [ ];
|
systemd.mounts = [ ];
|
||||||
|
systemd.services = { };
|
||||||
};
|
};
|
||||||
expr = testConfig { };
|
expr = testConfig { };
|
||||||
};
|
};
|
||||||
|
|
|
||||||
|
|
@ -1,9 +1,4 @@
|
||||||
{
|
{ 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);
|
||||||
|
|
@ -92,9 +87,6 @@ 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 = [
|
||||||
|
|
@ -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 =
|
https =
|
||||||
{ config, ... }:
|
{ config, ... }:
|
||||||
{
|
{
|
||||||
|
|
@ -193,13 +120,12 @@ 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 = "user_group";
|
userGroup = "homeassistant_user";
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
@ -322,11 +248,6 @@ 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
|
||||||
|
|
@ -335,14 +256,9 @@ in
|
||||||
];
|
];
|
||||||
};
|
};
|
||||||
|
|
||||||
testScript = commonTestScript.access.override {
|
nodes.client = { };
|
||||||
waitForPorts =
|
|
||||||
{ node, ... }:
|
testScript = commonTestScript.access;
|
||||||
[
|
|
||||||
8123
|
|
||||||
node.config.shb.lldap.webUIListenPort
|
|
||||||
];
|
|
||||||
};
|
|
||||||
};
|
};
|
||||||
|
|
||||||
# Not yet supported
|
# Not yet supported
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue