{ config, lib, pkgs, ... }:
let
  domain = "auth.${config.networking.domain}";
  seed = {
    groups = [
      {
        name = "admins";
        long_name = "Portunus Admin";
        members = [ "admin" ];
        permissions.portunus.is_admin = true;
      }
      {
        name = "search";
        long_name = "LDAP search group";
        members = [ "search" ];
        permissions.ldap.can_read = true;
      }
      {
        name = "fsr";
        long_name = "Mitglieder des iFSR";
      }
    ];
    users = [
      {
        login_name = "admin";
        given_name = "admin";
        family_name = "admin";
        password.from_command = [
          "${pkgs.coreutils}/bin/cat"
          config.sops.secrets."portunus/admin-password".path
        ];
      }
      {
        login_name = "search";
        given_name = "search";
        family_name = "search";
        password.from_command = [
          "${pkgs.coreutils}/bin/cat"
          config.sops.secrets."portunus/search-password".path
        ];
      }
    ];
  };
in
{
  sops.secrets = {
    "portunus/admin-password".owner = config.services.portunus.user;
    "portunus/search-password".owner = config.services.portunus.user;
    "dex/environment".owner = config.systemd.services.dex.serviceConfig.User;
    nslcd_ldap_search = {
      key = "portunus/search-password";
      owner = config.systemd.services.nslcd.serviceConfig.User;
    };
  };

  services.portunus = {
    enable = true;
    package = pkgs.portunus.overrideAttrs (_old: {
      patches = [
        ./0001-update-user-validation-regex.patch
        ./0002-both-ldap-and-ldaps.patch
        ./0003-gecos-ascii-escape.patch
        ./0004-make-givenName-optional.patch
      ];
    });

    inherit domain;
    port = 8681;
    dex.enable = true;
    seedPath = pkgs.writeText "portunus-seed.json" (builtins.toJSON seed);

    ldap = {
      suffix = "dc=ifsr,dc=de";
      searchUserName = "search";

      # normally disables port 389 (but not with our patch), use 636 with tls
      # `portunus.domain` resolves to localhost
      tls = true;
    };
  };

  services.dex.settings.oauth2.skipApprovalScreen = true;
  services.dex.settings.frontend.issuer = "iFSR Schliboleth";
  services.dex.settings.frontend.logoURL = "https://wiki.ifsr.de/images/3/3b/LogoiFSR.png";
  services.dex.settings.frontend.theme = "dark";

  systemd.services.dex.serviceConfig = {
    DynamicUser = lib.mkForce false;
    EnvironmentFile = config.sops.secrets."dex/environment".path;
    StateDirectory = "dex";
    User = "dex";
  };

  users = {
    users.dex = {
      group = "dex";
      isSystemUser = true;
    };
    groups.dex = { };

    ldap =
      let portunus = config.services.portunus; in
      rec {
        enable = true;
        server = "ldap://localhost";
        base = "${portunus.ldap.suffix}";
        bind = {
          distinguishedName = "uid=${portunus.ldap.searchUserName},ou=users,${base}";
          passwordFile = config.sops.secrets.nslcd_ldap_search.path;
        };
        daemon.enable = true;
      };
  };

  security.pam.services.sshd.makeHomeDir = true;

  services.nginx = {
    enable = true;
    virtualHosts."${config.services.portunus.domain}" = {
      forceSSL = true;
      enableACME = true;
      locations = {
        "/".proxyPass = "http://localhost:${toString config.services.portunus.port}";
        "/dex".proxyPass = "http://localhost:${toString config.services.portunus.dex.port}";
      };
    };
  };
  networking.firewall = {
    extraInputRules = ''
      ip saddr { 141.30.86.192/26, 141.76.100.128/25 } tcp dport 636 accept comment "Allow ldaps access from office nets"
    '';
  };
}