{ config, lib, libS, pkgs, ... }: let cfg = config.services.matrix-synapse; cfge = cfg.element-web; inherit (config.security) ldap; in { options = { services.matrix-synapse = { addAdditionalOembedProvider = libS.mkOpinionatedOption "add additional oembed providers from oembed.com"; element-web = { enable = lib.mkEnableOption (lib.mdDoc "the element-web client"); domain = lib.mkOption { type = lib.types.str; example = "element.example.org"; description = lib.mdDoc "The domain that element-web will use."; }; package = lib.mkPackageOptionMD pkgs "Element-Web" { default = [ "element-web" ]; }; enableConfigFeatures = libS.mkOpinionatedOption "enable most features available via config.json"; }; ldap = { enable = lib.mkEnableOption (lib.mdDoc "login via ldap"); userGroup = libS.ldap.mkUserGroupOption; bindPasswordFile = lib.mkOption { type = lib.types.str; example = "/var/lib/secrets/bind-password"; description = lib.mdDoc "Path to a file containing the bind password."; }; }; recommendedDefaults = libS.mkOpinionatedOption "set recommended and secure default settings"; }; }; config.environment.etc = lib.mkIf cfg.enable { "matrix-synapse/config.yaml".source = cfg.configFile; }; config.services.nginx = lib.mkIf cfge.enable { enable = true; virtualHosts."${cfge.domain}" = { forceSSL = true; enableACME = lib.mkDefault true; root = (cfge.package.override { conf = with config.services.matrix-synapse.settings; { default_server_config."m.homeserver" = { "base_url" = public_baseurl; "server_name" = server_name; }; default_theme = "dark"; room_directory.servers = [ server_name ]; } // lib.optionalAttrs cfge.enableConfigFeatures { features = { # https://github.com/matrix-org/matrix-react-sdk/blob/develop/src/settings/Settings.tsx # https://github.com/vector-im/element-web/blob/develop/docs/labs.md feature_ask_to_join = true; feature_bridge_state = true; feature_exploring_public_spaces = true; feature_jump_to_date = true; feature_mjolnir = true; feature_pinning = true; feature_presence_in_room_list = true; feature_report_to_moderators = true; feature_qr_signin_reciprocate_show = true; }; show_labs_settings = true; }; }).overrideAttrs ({ postInstall ? "", ... }: { # prevent 404 spam in nginx log postInstall = postInstall + '' ln -rs $out/config.json $out/config.${cfge.domain}.json ''; }); }; }; config.services.matrix-synapse = lib.mkMerge [ { settings = lib.mkIf cfge.enable rec { email.client_base_url = web_client_location; web_client_location = "https://${cfge.domain}"; }; } (lib.mkIf cfg.ldap.enable { plugins = with config.services.matrix-synapse.package.plugins; [ matrix-synapse-ldap3 ]; settings.modules = [{ module = "ldap_auth_provider.LdapAuthProviderModule"; config = { enabled = true; mode = "search"; uri = "ldaps://${ldap.domainName}:${toString ldap.port}"; base = ldap.userBaseDN; attributes = { uid = ldap.userField; mail = ldap.mailField; name = ldap.givenNameField; }; bind_dn = ldap.bindDN; bind_password_file = cfg.ldap.bindPasswordFile; tls_options.validate = true; } // lib.optionalAttrs (cfg.ldap.userGroup != null) { filter = ldap.groupFilter cfg.ldap.userGroup; }; }]; }) { settings.oembed.additional_providers = lib.mkIf cfg.addAdditionalOembedProvider [ ( let providers = pkgs.fetchurl { url = "https://oembed.com/providers.json?2023-03-23"; sha256 = "sha256-OdgBgkLbtNMn84ixKuC1gGzpyr+X+ORiLl6TAK3lYuQ="; }; in pkgs.runCommand "providers.json" { nativeBuildInputs = with pkgs; [ jq ]; } '' # filter out entries that do not contain a schemes entry # Error in configuration at 'oembed.additional_providers...endpoints.': 'schemes' is a required property # and have none http protocols: Unsupported oEmbed scheme (spotify) for pattern: spotify:* jq '[ ..|objects| select(.endpoints[0]|has("schemes")) | .endpoints[0].schemes=([ .endpoints[0].schemes[]|select(.|contains("http")) ]) ]' ${providers} > $out '' ) ]; } (lib.mkIf cfg.recommendedDefaults { settings = { federation_client_minimum_tls_version = "1.2"; suppress_key_server_warning = true; user_directory.prefer_local_users = true; }; withJemalloc = true; }) ]; config.services.portunus.seedSettings.groups = lib.optional (cfg.ldap.userGroup != null) { long_name = "Matrix Users"; name = cfg.ldap.userGroup; permissions = { }; }; }