Compare commits
2 commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
b26023a4ce | ||
|
|
ff60a64131 |
11 changed files with 143 additions and 54 deletions
|
|
@ -4013,6 +4013,9 @@
|
|||
"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"
|
||||
],
|
||||
|
|
|
|||
|
|
@ -352,16 +352,12 @@ 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
|
||||
deep diff ''${expectedPath}.json ''${resultPath}.json >> $out
|
||||
${pkgs.deepdiff}/bin/deep diff ''${expectedPath}.json ''${resultPath}.json >> $out
|
||||
''
|
||||
);
|
||||
|
||||
|
|
|
|||
|
|
@ -75,30 +75,6 @@ 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: {
|
||||
|
|
@ -106,7 +82,6 @@ in
|
|||
description = "Webdav mount point";
|
||||
after = [ "network-online.target" ];
|
||||
wants = [ "network-online.target" ];
|
||||
wantedBy = [ "multi-user.target" ];
|
||||
|
||||
what = c.remoteUrl;
|
||||
where = c.mountPoint;
|
||||
|
|
|
|||
|
|
@ -249,10 +249,9 @@ in
|
|||
let
|
||||
recursiveFlag = lib.optionalString cfg.snapshotBeforeActivation.recursive "-r";
|
||||
in
|
||||
lib.concatMapStringsSep "\n" (ds: ''
|
||||
echo "Taking ZFS snapshot of ${ds}@$name"
|
||||
zfs snapshot ${recursiveFlag} ${ds}@"$name"
|
||||
'') (lib.uniqueStrings datasets)
|
||||
lib.concatMapStringsSep "\n" (ds: "zfs snapshot ${recursiveFlag} ${ds}@\"$name\"") (
|
||||
lib.uniqueStrings datasets
|
||||
)
|
||||
)
|
||||
);
|
||||
};
|
||||
|
|
|
|||
|
|
@ -139,10 +139,16 @@ in
|
|||
|
||||
userGroup = lib.mkOption {
|
||||
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";
|
||||
};
|
||||
|
||||
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 = ''
|
||||
|
|
@ -277,6 +283,7 @@ in
|
|||
args = [
|
||||
"http://${cfg.ldap.host}:${toString cfg.ldap.port}"
|
||||
cfg.ldap.userGroup
|
||||
cfg.ldap.adminGroup
|
||||
];
|
||||
meta = true;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -127,16 +127,34 @@ shb.home-assistant.ldap
|
|||
enable = true;
|
||||
host = "127.0.0.1";
|
||||
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.
|
||||
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.
|
||||
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.
|
||||
|
||||
### With SSO Support {#services-home-assistant-usage-sso}
|
||||
|
||||
|
|
|
|||
|
|
@ -7,10 +7,10 @@ stdenvNoCC.mkDerivation {
|
|||
name = "lldap-ha-auth";
|
||||
|
||||
src = pkgs.fetchFromGitHub {
|
||||
owner = "lldap";
|
||||
owner = "ibizaman";
|
||||
repo = "lldap";
|
||||
rev = "7d1f5abc137821c500de99c94f7579761fc949d8";
|
||||
sha256 = "sha256-8D+7ww70Ja6Qwdfa+7MpjAAHewtCWNf/tuTAExoUrg0=";
|
||||
rev = "adaf17c70336ec2562d23d1b9775579d62691b51";
|
||||
sha256 = "sha256-4FqfglEss5MlnxvjP40zbxqtwvB/GGMs7HwK9CBNBUQ=";
|
||||
};
|
||||
|
||||
nativeBuildInputs = [
|
||||
|
|
|
|||
|
|
@ -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";
|
||||
}
|
||||
'';
|
||||
};
|
||||
|
|
|
|||
|
|
@ -295,6 +295,10 @@ 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;
|
||||
|
|
@ -412,8 +416,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(f"Clicking button {testCfg['loginButtonNameRegex']}")
|
||||
page.get_by_role("button", name=re.compile(testCfg['loginButtonNameRegex'])).click()
|
||||
print("Clicking login button")
|
||||
page.${cfg.loginButtonSelector}.click()
|
||||
|
||||
for line in u['nextPageExpect']:
|
||||
print(f"Running: {line}")
|
||||
|
|
@ -529,11 +533,14 @@ 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.
|
||||
|
|
@ -542,6 +549,7 @@ in
|
|||
};
|
||||
charlie = {
|
||||
email = "charlie@example.com";
|
||||
displayName = "Charlie Charlie";
|
||||
groups = [ "other_group" ];
|
||||
password.result = config.shb.hardcodedsecret.charlie.result;
|
||||
};
|
||||
|
|
|
|||
|
|
@ -34,7 +34,6 @@ in
|
|||
expected = {
|
||||
services.davfs2.enable = false;
|
||||
systemd.mounts = [ ];
|
||||
systemd.services = { };
|
||||
};
|
||||
expr = testConfig { };
|
||||
};
|
||||
|
|
|
|||
|
|
@ -1,4 +1,9 @@
|
|||
{ pkgs, shb, ... }:
|
||||
{
|
||||
pkgs,
|
||||
shb,
|
||||
lib,
|
||||
...
|
||||
}:
|
||||
let
|
||||
commonTestScript = shb.test.mkScripts {
|
||||
hasSSL = { node, ... }: !(isNull node.config.shb.home-assistant.ssl);
|
||||
|
|
@ -87,6 +92,9 @@ 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 = [
|
||||
|
|
@ -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 =
|
||||
{ config, ... }:
|
||||
{
|
||||
|
|
@ -120,12 +193,13 @@ 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 = "homeassistant_user";
|
||||
userGroup = "user_group";
|
||||
};
|
||||
};
|
||||
};
|
||||
|
|
@ -248,6 +322,11 @@ in
|
|||
ldap = shb.test.runNixOSTest {
|
||||
name = "homeassistant_ldap";
|
||||
|
||||
nodes.client = {
|
||||
imports = [
|
||||
clientLdapLogin
|
||||
];
|
||||
};
|
||||
nodes.server = {
|
||||
imports = [
|
||||
basic
|
||||
|
|
@ -256,9 +335,14 @@ in
|
|||
];
|
||||
};
|
||||
|
||||
nodes.client = { };
|
||||
|
||||
testScript = commonTestScript.access;
|
||||
testScript = commonTestScript.access.override {
|
||||
waitForPorts =
|
||||
{ node, ... }:
|
||||
[
|
||||
8123
|
||||
node.config.shb.lldap.webUIListenPort
|
||||
];
|
||||
};
|
||||
};
|
||||
|
||||
# Not yet supported
|
||||
|
|
|
|||
Loading…
Reference in a new issue