diff --git a/components/default.nix b/components/default.nix new file mode 100644 index 0000000..0997543 --- /dev/null +++ b/components/default.nix @@ -0,0 +1,48 @@ +{ authentik-src +, authentik-version +, authentikPoetryOverrides +, buildNapalmPackage +, defaultPoetryOverrides +, mkPoetryEnv +, pkgs +}: + +pkgs.lib.makeScope pkgs.newScope (final: + let + docs = final.callPackage ./docs.nix { + inherit authentik-src authentik-version buildNapalmPackage; + }; + frontend = final.callPackage ./frontend.nix { + inherit authentik-src authentik-version buildNapalmPackage; + }; + pythonEnv = final.callPackage ./pythonEnv.nix { + inherit authentik-src mkPoetryEnv defaultPoetryOverrides authentikPoetryOverrides; + }; + # server + outposts + gopkgs = final.callPackage ./gopkgs.nix { + inherit authentik-src authentik-version; + }; + staticWorkdirDeps = final.callPackage ./staticWorkdirDeps.nix { + inherit authentik-src; + }; + migrate = final.callPackage ./migrate.nix { + inherit authentik-src; + }; + # worker + celery = final.callPackage ./celery.nix { + }; + in + { + authentikComponents = { + inherit + docs + frontend + pythonEnv + gopkgs + staticWorkdirDeps + migrate + celery; + }; + inherit authentik-src authentik-version; + } +) diff --git a/flake.nix b/flake.nix index bcff90b..9232420 100644 --- a/flake.nix +++ b/flake.nix @@ -53,7 +53,7 @@ "x86_64-linux" "aarch64-linux" # not tested ]; - flake = { + flake = { self, ... }: { nixosModules.default = { pkgs, ... }: { imports = [ ./module.nix ]; services.authentik.authentikComponents = pkgs.lib.mkDefault (withSystem pkgs.stdenv.hostPlatform.system ( @@ -61,41 +61,39 @@ { inherit (config.packages) celery staticWorkdirDeps migrate pythonEnv frontend gopkgs docs; } )); }; + + # returns a scope which includes the attrset `authentikComponents` + # + # the returned scope may be overridden using its `overrideScope` function to + # create a new scope with patched versions of individual authentik components + # + # see ./tests/override-scope.nix for a usage example + lib.mkAuthentikScope = let authentik-version' = authentik-version; in { + pkgs, + system ? pkgs.stdenv.hostPlatform.system, + authentik-version ? authentik-version', + mkPoetryEnv ? (import inputs.poetry2nix { inherit pkgs; }).mkPoetryEnv, + defaultPoetryOverrides ? (import inputs.poetry2nix { inherit pkgs; }).defaultPoetryOverrides, + authentikPoetryOverrides ? import ./poetry2nix-python-overrides.nix pkgs, + buildNapalmPackage ? napalm.legacyPackages.${system}.buildPackage + }: + import ./components { + inherit pkgs authentik-src authentik-version mkPoetryEnv defaultPoetryOverrides authentikPoetryOverrides buildNapalmPackage; + }; }; perSystem = { pkgs, system, self', ... }: let - inherit (import inputs.poetry2nix { inherit pkgs; }) - mkPoetryEnv - defaultPoetryOverrides; - authentikComponents = { - inherit (self'.packages) celery staticWorkdirDeps migrate pythonEnv frontend gopkgs docs; }; - authentikPoetryOverrides = import ./poetry2nix-python-overrides.nix pkgs; + inherit (self.lib.mkAuthentikScope { inherit pkgs; }) authentikComponents; in { packages = { - docs = pkgs.callPackage components/docs.nix { - buildNapalmPackage = napalm.legacyPackages.${system}.buildPackage; - inherit authentik-src authentik-version; - }; - frontend = pkgs.callPackage components/frontend.nix { - buildNapalmPackage = napalm.legacyPackages.${system}.buildPackage; - inherit authentik-src authentik-version authentikComponents; - }; - pythonEnv = pkgs.callPackage components/pythonEnv.nix { - inherit authentik-src mkPoetryEnv defaultPoetryOverrides authentikPoetryOverrides; - }; - # server + outposts - gopkgs = pkgs.callPackage components/gopkgs.nix { - inherit authentik-src authentik-version authentikComponents; - }; - staticWorkdirDeps = pkgs.callPackage components/staticWorkdirDeps.nix { - inherit authentik-src authentikComponents; - }; - migrate = pkgs.callPackage components/migrate.nix { - inherit authentik-src authentikComponents; - }; - # worker - celery = pkgs.callPackage components/celery.nix { - inherit authentikComponents; - }; + inherit (authentikComponents) + docs + frontend + pythonEnv + gopkgs + staticWorkdirDeps + migrate + celery; + # terraform provider terraform-provider-authentik = inputs.nixpkgs-23-05.legacyPackages.${system}.buildGo118Module rec { pname = "terraform-provider-authentik"; @@ -122,6 +120,11 @@ inherit pkgs authentik-version; inherit (self) nixosModules; }); + override-scope = (import tests/override-scope.nix { + inherit pkgs authentik-version; + inherit (self) nixosModules; + inherit (self.lib) mkAuthentikScope; + }); }; }; }); diff --git a/tests/override-scope.nix b/tests/override-scope.nix new file mode 100644 index 0000000..e9faf3b --- /dev/null +++ b/tests/override-scope.nix @@ -0,0 +1,126 @@ +{ pkgs +, authentik-version +, nixosModules +, mkAuthentikScope +}: + +/* + * This is just meant as a demonstration on how to override the scope which includes the + * authentik components. This is an extended version of ./minimal-vmtest.nix + * + * First, a new scope is created from the default one using `overrideScope` on the result + * from `mkAuthentikScope`. + * Components with overrides in that scope are used by their dependents, i.e. dependents + * of `pythonEnv` (e.g. gopkgs) also pull in that overridden `pythonEnv` + * Then, that scope is passed to the module via the `services.authentik.authentikComponents` option + * And finally, the test script checks if the patched welcome string is present. + */ + +let + # use a root-owned EnvironmentFile in production instead (services.authentik.environmentFile) + authentik-env = pkgs.writeText "authentik-test-secret-env" '' + AUTHENTIK_SECRET_KEY=thissecretwillbeinthenixstore + ''; + + customWelcome = "Welcome to custom authentik"; + + # creates a new scope using python 3.12 for mkPoetryEnv + # and overrides the welcome string for the default oobe intial-setup flow + customScope = (mkAuthentikScope { inherit pkgs; }).overrideScope + (final: prev: { + authentikComponents = prev.authentikComponents // { + pythonEnv = prev.authentikComponents.pythonEnv.overrideAttrs (_: { + python = pkgs.python312; + }); + staticWorkdirDeps = prev.authentikComponents.staticWorkdirDeps.overrideAttrs (oA: { + buildCommand = oA.buildCommand + '' + rm -v $out/blueprints + cp -vr ${prev.authentik-src}/blueprints $out/blueprints + substituteInPlace $out/blueprints/default/flow-oobe.yaml \ + --replace "Welcome to authentik" "${customWelcome}" + ''; + }); + }; + }); +in +pkgs.nixosTest { + name = "authentik"; + nodes = { + authentik = { + virtualisation = { + cores = 3; + memorySize = 2048; + }; + imports = [ + nixosModules.default + "${pkgs.path}/nixos/tests/common/user-account.nix" + "${pkgs.path}/nixos/tests/common/x11.nix" + ]; + + services.authentik = { + enable = true; + environmentFile = authentik-env; + nginx = { + enable = true; + host = "localhost"; + }; + # pass authentikComponents with patched pythonEnv and staticWorkdirDeps + inherit (customScope) authentikComponents; + }; + + services.xserver.enable = true; + test-support.displayManager.auto.user = "alice"; + environment.systemPackages = with pkgs; [ + firefox + xdotool + ]; + }; + }; + + enableOCR = true; + + # TODO maybe use bootstrap env vars instead of testing manual workflow? + testScript = '' + start_all() + + authentik.wait_for_unit("postgresql.service") + authentik.wait_for_unit("redis-authentik.service") + authentik.wait_for_unit("authentik-migrate.service") + authentik.wait_for_unit("authentik-worker.service") + authentik.wait_for_unit("authentik.service") + authentik.wait_for_open_port(9000) + authentik.wait_until_succeeds("curl -fL http://localhost:9000/if/flow/initial-setup/ >&2") + + with subtest("Frontend renders"): + machine.succeed("su - alice -c 'firefox http://localhost:9000/if/flow/initial-setup/' >&2 &") + machine.wait_for_text("${customWelcome}") + machine.screenshot("1_rendered_frontend") + + with subtest("admin account setup works"): + machine.send_key("tab") + machine.send_key("tab") + machine.send_chars("akadmin@localhost") + machine.send_key("tab") + machine.send_chars("foobar") + machine.send_key("tab") + machine.send_chars("foobar") + machine.send_key("ret") + machine.wait_for_text("My applications") + machine.send_key("esc") + machine.screenshot("2_initial_setup_successful") + + with subtest("admin settings render and version as expected"): + machine.succeed("su - alice -c 'firefox http://localhost:9000/if/admin/' >&2 &") + machine.wait_for_text("General system status") + machine.screenshot("3_rendered_admin_interface") + machine.succeed("su - alice -c 'xdotool click 1' >&2") + machine.succeed("su - alice -c 'xdotool key --delay 100 Page_Down Page_Down' >&2") + machine.wait_for_text("${authentik-version}") + machine.screenshot("4_correct_version_in_admin_interface") + + with subtest("nginx proxies to authentik"): + machine.succeed("su - alice -c 'firefox http://localhost/' >&2 &") + machine.wait_for_text("authentik") + machine.screenshot("5_nginx_proxies_requests") + ''; +}