# Copied from https://cyberchaos.dev/leona/nixfiles/-/blob/main/modules/vouch-proxy/default.nix?ref_type=heads
{ pkgs, lib, config, ... }:

with lib;

let
  cfg = config.services.vouch-proxy;
  format = pkgs.formats.yaml { };
  serverOptions = { ... }: {

    options = {
      clientId = mkOption {
        type = types.str;
        description = "OIDC Client ID for server.";
      };
      port = mkOption {
        type = types.int;
        description = "Port of the vouch-proxy server.";
      };
      environmentFiles = mkOption {
        type = with types; nullOr (listOf path);
        default = [ ];
        description = ''
          List of environment files set in the vouch-proxy-<literal>name</literal> systemd service.
          For example the jwt secret and oidc secrets should be set in one of these files.
        '';
      };
      addAuthRequestToMainLocation = mkOption {
        type = types.bool;
        default = false;
        description = "Add auth_request to / location.";
      };
    };
  };
  mkService = domain: serviceConfig:
    let
      settings = recursiveUpdate cfg.globalSettings {
        vouch.port = serviceConfig.port;
        vouch.cookie.domain = domain;
        oauth.client_id = serviceConfig.clientId;
        oauth.callback_url = "https://${domain}/_vouch/auth";
      };
      configFile = format.generate "vouch-proxy-config.yaml" settings;
    in nameValuePair "vouch-proxy-${domain}" {
      description = "vouch-proxy";
      after = [ "network.target" ];
      wantedBy = [ "multi-user.target" ];
      path = [ cfg.package ];

      serviceConfig = {
        Type = "simple";
        DynamicUser = true;
        ExecStart = "${cfg.package}/bin/vouch-proxy -config ${configFile}";
        Restart = "always";
        EnvironmentFile = serviceConfig.environmentFiles;
      };
    };
  mkVirtualHosts = domain: serviceConfig:
    nameValuePair domain {
      extraConfig = ''
        error_page 401 = @error401;
      '';
      locations."/".extraConfig = ''
        auth_request /_vouch/validate;
      '';
      locations."/_vouch" = {
        proxyPass = "http://127.0.0.1:${toString serviceConfig.port}";
        extraConfig = ''
          proxy_pass_request_body off;
          proxy_set_header Content-Length "";
          # these return values are used by the @error401 call
          auth_request_set $auth_resp_jwt $upstream_http_x_vouch_jwt;
          auth_request_set $auth_resp_err $upstream_http_x_vouch_err;
          auth_request_set $auth_resp_failcount $upstream_http_x_vouch_failcount;
        '';
      };

      locations."@error401".return =
        "302 https://${domain}/_vouch/login?url=https://$host$request_uri&vouch-failcount=$auth_resp_failcount&X-Vouch-Token=$auth_resp_jwt&error=$auth_resp_err";
    };
in {
  options.services.vouch-proxy = with lib; {
    enable = mkEnableOption "vouch-proxy service";
    package = mkOption {
      default = pkgs.vouch-proxy;
      type = types.package;
      defaultText = "pkgs.vouch-proxy";
      description = "vouch-proxy derivation to use.";
    };
    servers =
      mkOption { type = types.attrsOf (types.submodule serverOptions); };
    globalSettings = mkOption {
      type = format.type;
      default = {
        vouch = {
          listen = "127.0.0.1";
          jwt.issuer = "kloenk SSO";
          allowAllUsers = true;
          headers = { claims = [ "preferred_username" ]; };
          document_root = "/_vouch";
        };
        oauth = {
          provider = "oidc";
          auth_url =
            "https://auth.kloenk.dev/realms/kloenk/protocol/openid-connect/auth";
          token_url =
            "https://auth.kloenk.dev/realms/kloenk/protocol/openid-connect/token";
          user_info_url =
            "https://auth.kloenk.dev/realms/kloenk/protocol/openid-connect/userinfo";
          scopes = [ "openid" "email" "profile" ];
        };
      };
      description = ''
        Vouch-proxy configuration. Refer to
        <link xlink:href="https://github.com/vouch/vouch-proxy/blob/master/config/config.yml_example"/>
        for details on supported values.
      '';
    };
  };
  config = lib.mkIf cfg.enable {
    systemd.services = mapAttrs' mkService cfg.servers;
    services.nginx.virtualHosts = mapAttrs' mkVirtualHosts cfg.servers;
  };
}
