diff options
author | Jonas Heinrich <onny@project-insanity.org> | 2023-01-06 23:26:50 +0100 |
---|---|---|
committer | GitHub <noreply@github.com> | 2023-01-06 23:26:50 +0100 |
commit | cff8fd9c6cfefadd5ceea6717c03c66fdee235f8 (patch) | |
tree | c3e627844278f10f9a82e4158a49c08b3a759c1d /nixos | |
parent | 2f84082de7cb5e0de57f02332c82a9dce73b1e5e (diff) | |
parent | 236d90fde0b376f00e8eea32ea6af37ec3de4766 (diff) |
Merge pull request #208299 from e1mo/dokuwkiki-makeover
nixos/dokuwiki: Overhaul for structured settings
Diffstat (limited to 'nixos')
-rw-r--r-- | nixos/doc/manual/from_md/release-notes/rl-2305.section.xml | 14 | ||||
-rw-r--r-- | nixos/doc/manual/release-notes/rl-2305.section.md | 4 | ||||
-rw-r--r-- | nixos/modules/services/web-apps/dokuwiki.nix | 269 | ||||
-rw-r--r-- | nixos/tests/dokuwiki.nix | 48 |
4 files changed, 260 insertions, 75 deletions
diff --git a/nixos/doc/manual/from_md/release-notes/rl-2305.section.xml b/nixos/doc/manual/from_md/release-notes/rl-2305.section.xml index 12e4d490300e..97d6a89759c8 100644 --- a/nixos/doc/manual/from_md/release-notes/rl-2305.section.xml +++ b/nixos/doc/manual/from_md/release-notes/rl-2305.section.xml @@ -309,6 +309,20 @@ </listitem> <listitem> <para> + The <literal>dokuwiki</literal> service now takes + configuration via the + <literal>services.dokuwiki.sites.<name>.settings</literal> + attribute set, <literal>extraConfig</literal> is deprecated + and will be removed. The + <literal>{aclUse,superUser,disableActions}</literal> + attributes have been renamed, <literal>pluginsConfig</literal> + now also accepts an attribute set of booleans, passing plain + PHP is deprecated. Same applies to <literal>acl</literal> + which now also accepts structured settings. + </para> + </listitem> + <listitem> + <para> To reduce closure size in <literal>nixos/modules/profiles/minimal.nix</literal> profile disabled installation documentations and manuals. Also diff --git a/nixos/doc/manual/release-notes/rl-2305.section.md b/nixos/doc/manual/release-notes/rl-2305.section.md index 07ee346c2c87..8fafe0a344c3 100644 --- a/nixos/doc/manual/release-notes/rl-2305.section.md +++ b/nixos/doc/manual/release-notes/rl-2305.section.md @@ -84,6 +84,10 @@ In addition to numerous new and upgraded packages, this release has the followin `services.dnsmasq.extraConfig` will be deprecated when NixOS 22.11 reaches end of life. +- The `dokuwiki` service now takes configuration via the `services.dokuwiki.sites.<name>.settings` attribute set, `extraConfig` is deprecated and will be removed. + The `{aclUse,superUser,disableActions}` attributes have been renamed, `pluginsConfig` now also accepts an attribute set of booleans, passing plain PHP is deprecated. + Same applies to `acl` which now also accepts structured settings. + - To reduce closure size in `nixos/modules/profiles/minimal.nix` profile disabled installation documentations and manuals. Also disabled `logrotate` and `udisks2` services. - The minimal ISO image now uses the `nixos/modules/profiles/minimal.nix` profile. diff --git a/nixos/modules/services/web-apps/dokuwiki.nix b/nixos/modules/services/web-apps/dokuwiki.nix index 1c40441bb55f..d90dd8ace412 100644 --- a/nixos/modules/services/web-apps/dokuwiki.nix +++ b/nixos/modules/services/web-apps/dokuwiki.nix @@ -15,30 +15,64 @@ let extraConfig = mkPhpIni cfg.phpOptions; }; - dokuwikiAclAuthConfig = hostName: cfg: pkgs.writeText "acl.auth-${hostName}.php" '' + dokuwikiAclAuthConfig = hostName: cfg: let + inherit (cfg) acl; + acl_gen = concatMapStringsSep "\n" (l: "${l.page} \t ${l.actor} \t ${toString l.level}"); + in pkgs.writeText "acl.auth-${hostName}.php" '' # acl.auth.php # <?php exit()?> # # Access Control Lists # - ${toString cfg.acl} + ${if isString acl then acl else acl_gen acl} ''; - dokuwikiLocalConfig = hostName: cfg: pkgs.writeText "local-${hostName}.php" '' - <?php - $conf['savedir'] = '${cfg.stateDir}'; - $conf['superuser'] = '${toString cfg.superUser}'; - $conf['useacl'] = '${toString cfg.aclUse}'; - $conf['disableactions'] = '${cfg.disableActions}'; + mergeConfig = cfg: { + useacl = false; # Dokuwiki default + savedir = cfg.stateDir; + } // cfg.settings; + + writePhpFile = name: text: pkgs.writeTextFile { + inherit name; + text = "<?php\n${text}"; + checkPhase = "${pkgs.php81}/bin/php --syntax-check $target"; + }; + + mkPhpValue = v: let + isHasAttr = s: isAttrs v && hasAttr s v; + in + if isString v then escapeShellArg v + # NOTE: If any value contains a , (comma) this will not get escaped + else if isList v && any lib.strings.isCoercibleToString v then escapeShellArg (concatMapStringsSep "," toString v) + else if isInt v then toString v + else if isBool v then toString (if v then 1 else 0) + else if isHasAttr "_file" then "trim(file_get_contents(${lib.escapeShellArg v._file}))" + else if isHasAttr "_raw" then v._raw + else abort "The dokuwiki localConf value ${lib.generators.toPretty {} v} can not be encoded." + ; + + mkPhpAttrVals = v: flatten (mapAttrsToList mkPhpKeyVal v); + mkPhpKeyVal = k: v: let + values = if (isAttrs v && (hasAttr "_file" v || hasAttr "_raw" v )) || !isAttrs v then + [" = ${mkPhpValue v};"] + else + mkPhpAttrVals v; + in map (e: "[${escapeShellArg k}]${e}") (flatten values); + + dokuwikiLocalConfig = hostName: cfg: let + conf_gen = c: map (v: "$conf${v}") (mkPhpAttrVals c); + in writePhpFile "local-${hostName}.php" '' + ${concatStringsSep "\n" (conf_gen cfg.mergedConfig)} ${toString cfg.extraConfig} ''; - dokuwikiPluginsLocalConfig = hostName: cfg: pkgs.writeText "plugins.local-${hostName}.php" '' - <?php - ${cfg.pluginsConfig} + dokuwikiPluginsLocalConfig = hostName: cfg: let + pc = cfg.pluginsConfig; + pc_gen = pc: concatStringsSep "\n" (mapAttrsToList (n: v: "$plugins['${n}'] = ${boolToString v};") pc); + in writePhpFile "plugins.local-${hostName}.php" '' + ${if isString pc then pc else pc_gen pc} ''; - pkg = hostName: cfg: pkgs.stdenv.mkDerivation rec { pname = "dokuwiki-${hostName}"; version = src.version; @@ -49,22 +83,82 @@ let cp -r * $out/ # symlink the dokuwiki config - ln -s ${dokuwikiLocalConfig hostName cfg} $out/share/dokuwiki/local.php + ln -sf ${dokuwikiLocalConfig hostName cfg} $out/share/dokuwiki/conf/local.php # symlink plugins config - ln -s ${dokuwikiPluginsLocalConfig hostName cfg} $out/share/dokuwiki/plugins.local.php + ln -sf ${dokuwikiPluginsLocalConfig hostName cfg} $out/share/dokuwiki/conf/plugins.local.php - # symlink acl - ln -s ${dokuwikiAclAuthConfig hostName cfg} $out/share/dokuwiki/acl.auth.php + # symlink acl (if needed) + ${optionalString (cfg.mergedConfig.useacl && cfg.acl != null) "ln -sf ${dokuwikiAclAuthConfig hostName cfg} $out/share/dokuwiki/acl.auth.php"} # symlink additional plugin(s) and templates(s) - ${concatMapStringsSep "\n" (template: "ln -s ${template} $out/share/dokuwiki/lib/tpl/${template.name}") cfg.templates} - ${concatMapStringsSep "\n" (plugin: "ln -s ${plugin} $out/share/dokuwiki/lib/plugins/${plugin.name}") cfg.plugins} + ${concatMapStringsSep "\n" (template: "ln -sf ${template} $out/share/dokuwiki/lib/tpl/${template.name}") cfg.templates} + ${concatMapStringsSep "\n" (plugin: "ln -sf ${plugin} $out/share/dokuwiki/lib/plugins/${plugin.name}") cfg.plugins} ''; }; + aclOpts = { ... }: { + options = { + + page = mkOption { + type = types.str; + description = "Page or namespace to restrict"; + example = "start"; + }; + + actor = mkOption { + type = types.str; + description = "User or group to restrict"; + example = "@external"; + }; + + level = let + available = { + "none" = 0; + "read" = 1; + "edit" = 2; + "create" = 4; + "upload" = 8; + "delete" = 16; + }; + in mkOption { + type = types.enum ((attrValues available) ++ (attrNames available)); + apply = x: if isInt x then x else available.${x}; + description = '' + Permission level to restrict the actor(s) to. + See <https://www.dokuwiki.org/acl#background_info> for explanation + ''; + example = "read"; + }; + + }; + }; + siteOpts = { config, lib, name, ... }: { + imports = [ + # NOTE: These will sadly not print the absolute argument path but only the name. Related to #96006 + (mkRenamedOptionModule [ "aclUse" ] [ "settings" "useacl" ] ) + (mkRenamedOptionModule [ "superUser" ] [ "settings" "superuser" ] ) + (mkRenamedOptionModule [ "disableActions" ] [ "settings" "disableactions" ] ) + ({ config, options, name, ...}: { + config.warnings = + (optional (isString config.pluginsConfig) '' + Passing plain strings to services.dokuwiki.sites.${name}.pluginsConfig has been deprecated and will not be continue to be supported in the future. + Please pass structured settings instead. + '') + ++ (optional (isString config.acl) '' + Passing a plain string to services.dokuwiki.sites.${name}.acl has been deprecated and will not continue to be supported in the future. + Please pass structured settings instead. + '') + ++ (optional (config.extraConfig != null) '' + services.dokuwiki.sites.${name}.extraConfig is deprecated and will be removed in the future. + Please pass structured settings to services.dokuwiki.sites.${name}.settings instead. + '') + ; + }) + ]; + options = { enable = mkEnableOption (lib.mdDoc "DokuWiki web application."); @@ -82,9 +176,22 @@ let }; acl = mkOption { - type = types.nullOr types.lines; + type = with types; nullOr (oneOf [ lines (listOf (submodule aclOpts)) ]); default = null; - example = "* @ALL 8"; + example = literalExpression '' + [ + { + page = "start"; + actor = "@external"; + level = "read"; + } + { + page = "*"; + actor = "@users"; + level = "upload"; + } + ] + ''; description = lib.mdDoc '' Access Control Lists: see <https://www.dokuwiki.org/acl> Mutually exclusive with services.dokuwiki.aclFile @@ -97,7 +204,7 @@ let aclFile = mkOption { type = with types; nullOr str; - default = if (config.aclUse && config.acl == null) then "/var/lib/dokuwiki/${name}/acl.auth.php" else null; + default = if (config.mergedConfig.useacl && config.acl == null) then "/var/lib/dokuwiki/${name}/acl.auth.php" else null; description = lib.mdDoc '' Location of the dokuwiki acl rules. Mutually exclusive with services.dokuwiki.acl Mutually exclusive with services.dokuwiki.acl which is preferred. @@ -107,42 +214,22 @@ let example = "/var/lib/dokuwiki/${name}/acl.auth.php"; }; - aclUse = mkOption { - type = types.bool; - default = true; - description = lib.mdDoc '' - Necessary for users to log in into the system. - Also limits anonymous users. When disabled, - everyone is able to create and edit content. - ''; - }; - pluginsConfig = mkOption { - type = types.lines; - default = '' - $plugins['authad'] = 0; - $plugins['authldap'] = 0; - $plugins['authmysql'] = 0; - $plugins['authpgsql'] = 0; - ''; + type = with types; oneOf [lines (attrsOf bool)]; + default = { + authad = false; + authldap = false; + authmysql = false; + authpgsql = false; + }; description = lib.mdDoc '' List of the dokuwiki (un)loaded plugins. ''; }; - superUser = mkOption { - type = types.nullOr types.str; - default = "@admin"; - description = lib.mdDoc '' - You can set either a username, a list of usernames (“admin1,admin2”), - or the name of a group by prepending an @ char to the groupname - Consult documentation <https://www.dokuwiki.org/config:superuser> for further instructions. - ''; - }; - usersFile = mkOption { type = with types; nullOr str; - default = if config.aclUse then "/var/lib/dokuwiki/${name}/users.auth.php" else null; + default = if config.mergedConfig.useacl then "/var/lib/dokuwiki/${name}/users.auth.php" else null; description = lib.mdDoc '' Location of the dokuwiki users file. List of users. Format: @@ -157,17 +244,6 @@ let example = "/var/lib/dokuwiki/${name}/users.auth.php"; }; - disableActions = mkOption { - type = types.nullOr types.str; - default = ""; - example = "search,register"; - description = lib.mdDoc '' - Disable individual action modes. Refer to - <https://www.dokuwiki.org/config:action_modes> - for details on supported values. - ''; - }; - plugins = mkOption { type = types.listOf types.path; default = []; @@ -266,7 +342,50 @@ let ''; }; + settings = mkOption { + type = types.attrsOf types.anything; + default = { + useacl = true; + superuser = "admin"; + }; + description = lib.mdDoc '' + Structural DokuWiki configuration. + Refer to <https://www.dokuwiki.org/config> + for details and supported values. + Settings can either be directly set from nix, + loaded from a file using `._file` or obtained from any + PHP function calls using `._raw`. + ''; + example = literalExpression '' + { + title = "My Wiki"; + userewrite = 1; + disableactions = [ "register" ]; # Will be concatenated with commas + plugin.smtp = { + smtp_pass._file = "/var/run/secrets/dokuwiki/smtp_pass"; + smtp_user._raw = "getenv('DOKUWIKI_SMTP_USER')"; + }; + } + ''; + }; + + mergedConfig = mkOption { + readOnly = true; + default = mergeConfig config; + defaultText = literalExpression '' + { + useacl = true; + } + ''; + description = lib.mdDoc '' + Read only representation of the final configuration. + ''; + }; + extraConfig = mkOption { + # This Option is deprecated and only kept until sometime before 23.05 for compatibility reasons + # FIXME (@e1mo): Actually remember removing this before 23.05. + visible = false; type = types.nullOr types.lines; default = null; example = '' @@ -277,15 +396,26 @@ let DokuWiki configuration. Refer to <https://www.dokuwiki.org/config> for details on supported values. + + **Note**: Please pass Structured settings via + `services.dokuwiki.sites.${name}.settings` instead. ''; }; + # Required for the mkRenamedOptionModule + # TODO: Remove me once https://github.com/NixOS/nixpkgs/issues/96006 is fixed + # or the aclUse, ... options are removed. + warnings = mkOption { + type = types.listOf types.unspecified; + default = [ ]; + visible = false; + internal = true; }; }; + }; in { - # interface options = { services.dokuwiki = { @@ -315,14 +445,16 @@ in # implementation config = mkIf (eachSite != {}) (mkMerge [{ + warnings = flatten (mapAttrsToList (_: cfg: cfg.warnings) eachSite); + assertions = flatten (mapAttrsToList (hostName: cfg: [{ - assertion = cfg.aclUse -> (cfg.acl != null || cfg.aclFile != null); - message = "Either services.dokuwiki.sites.${hostName}.acl or services.dokuwiki.sites.${hostName}.aclFile is mandatory if aclUse true"; + assertion = cfg.mergedConfig.useacl -> (cfg.acl != null || cfg.aclFile != null); + message = "Either services.dokuwiki.sites.${hostName}.acl or services.dokuwiki.sites.${hostName}.aclFile is mandatory if settings.useacl is true"; } { - assertion = cfg.usersFile != null -> cfg.aclUse != false; - message = "services.dokuwiki.sites.${hostName}.aclUse must must be true if usersFile is not null"; + assertion = cfg.usersFile != null -> cfg.mergedConfig.useacl != false; + message = "services.dokuwiki.sites.${hostName}.settings.useacl must must be true if usersFile is not null"; } ]) eachSite); @@ -332,12 +464,9 @@ in group = webserver.group; phpPackage = mkPhpPackage cfg; - phpEnv = { - DOKUWIKI_LOCAL_CONFIG = "${dokuwikiLocalConfig hostName cfg}"; - DOKUWIKI_PLUGINS_LOCAL_CONFIG = "${dokuwikiPluginsLocalConfig hostName cfg}"; - } // optionalAttrs (cfg.usersFile != null) { + phpEnv = optionalAttrs (cfg.usersFile != null) { DOKUWIKI_USERS_AUTH_CONFIG = "${cfg.usersFile}"; - } //optionalAttrs (cfg.aclUse) { + } // optionalAttrs (cfg.mergedConfig.useacl) { DOKUWIKI_ACL_AUTH_CONFIG = if (cfg.acl != null) then "${dokuwikiAclAuthConfig hostName cfg}" else "${toString cfg.aclFile}"; }; diff --git a/nixos/tests/dokuwiki.nix b/nixos/tests/dokuwiki.nix index d7c9e483f690..034d6f94af29 100644 --- a/nixos/tests/dokuwiki.nix +++ b/nixos/tests/dokuwiki.nix @@ -41,15 +41,39 @@ let sites = { "site1.local" = { - aclUse = false; - superUser = "admin"; + templates = [ template-bootstrap3 ]; + settings = { + useacl = false; + userewrite = true; + template = "bootstrap3"; + }; }; "site2.local" = { package = dwWithAcronyms; usersFile = "/var/lib/dokuwiki/site2.local/users.auth.php"; - superUser = "admin"; - templates = [ template-bootstrap3 ]; plugins = [ plugin-icalevents ]; + settings = { + useacl = true; + superuser = "admin"; + title._file = titleFile; + plugin.dummy.empty = "This is just for testing purposes"; + }; + acl = [ + { page = "*"; + actor = "@ALL"; + level = "read"; } + { page = "acl-test"; + actor = "@ALL"; + level = "none"; } + ]; + pluginsConfig = { + authad = false; + authldap = false; + authmysql = false; + authpgsql = false; + tag = false; + icalevents = true; + }; }; }; }; @@ -58,6 +82,7 @@ let networking.hosts."127.0.0.1" = [ "site1.local" "site2.local" ]; }; + titleFile = pkgs.writeText "dokuwiki-title" "DokuWiki on site2"; in { name = "dokuwiki"; meta = with pkgs.lib; { @@ -88,7 +113,7 @@ in { machine.succeed("curl -sSfL http://site1.local/ | grep 'DokuWiki'") machine.fail("curl -sSfL 'http://site1.local/doku.php?do=login' | grep 'Login'") - machine.succeed("curl -sSfL http://site2.local/ | grep 'DokuWiki'") + machine.succeed("curl -sSfL http://site2.local/ | grep 'DokuWiki on site2'") machine.succeed("curl -sSfL 'http://site2.local/doku.php?do=login' | grep 'Login'") with subtest("ACL Operations"): @@ -98,12 +123,25 @@ in { "curl -sSfL --cookie cjar --cookie-jar cjar 'http://site2.local/doku.php?do=login' | grep 'Logged in as: <bdi>Admin</bdi>'", ) + # Ensure the generated ACL is valid + machine.succeed( + "echo 'No Hello World! for @ALL here' >> /var/lib/dokuwiki/site2.local/data/pages/acl-test.txt", + "curl -sSL 'http://site2.local/doku.php?id=acl-test' | grep 'Permission Denied'" + ) + with subtest("Customizing Dokuwiki"): machine.succeed( "echo 'r13y is awesome!' >> /var/lib/dokuwiki/site2.local/data/pages/acronyms-test.txt", "curl -sSfL 'http://site2.local/doku.php?id=acronyms-test' | grep '<abbr title=\"reproducibility\">r13y</abbr>'", ) + # Testing if plugins (a) be correctly loaded and (b) configuration to enable them works + machine.succeed( + "echo '~~INFO:syntaxplugins~~' >> /var/lib/dokuwiki/site2.local/data/pages/plugin-list.txt", + "curl -sSfL 'http://site2.local/doku.php?id=plugin-list' | grep 'plugin:icalevents'", + "curl -sSfL 'http://site2.local/doku.php?id=plugin-list' | (! grep 'plugin:tag')", + ) + # Just to ensure both Webserver configurations are consistent in allowing that with subtest("Rewriting"): machine.succeed( |