{ pkgs, lib, config, ... }: let mod = config.xsession.windowManager.i3.config.modifier; in { config = { home.packages = with pkgs; [ pwgen (pkgs.writeShellApplication { name = "install-passwords"; runtimeInputs = [ yq gawk moreutils ]; text = (lib.strings.concatLines (map (file: '' ( echo "===== Preparing to write ${file.path}" temp="$(mktemp --tmpdir="${builtins.dirOf file.path}")" cat "${file.template}" > "$temp" '' + (lib.strings.concatLines (map (password: (if password.selector == null then '' echo "Reading ${password.path} for substituting ${password.variable}" value="$(pass "${password.path}" | head -n1)" '' else '' echo "Reading ${password.path} -> ${password.selector} for substituting ${password.variable}" value="$(pass "${password.path}" | tail -n +2 | yq -r '.${password.selector}')" '') + '' key="${password.variable}" K="$key" V="$value" awk '{ gsub (ENVIRON["K"], ENVIRON["V"]); print }' "$temp" | sponge "$temp" '') (lib.attrsets.attrValues file.passwords))) + '' echo "Moving the file in place" chown "${file.owner}" "$temp" chmod u=r "$temp" mv -f "$temp" "${file.path}" ) '') config.frogeye.passwordFiles) ); }) ]; programs = { bash.shellAliases = { pw = ''${pkgs.pwgen}/bin/pwgen 32 -y''; # Generate passwords. ln((26*2+10)**32)/ln(2) ≅ 190 bits of entropy }; password-store.enable = true; }; xsession.windowManager.i3.config.keybindings."${mod}+c" = "exec --no-startup-id ${config.programs.rofi.pass.package}/bin/rofi-pass --last-used"; # TODO Try autopass.cr }; options = { frogeye.passwordFiles = let defaultvar = "@PASSWORD@"; pwtype = { name, ... }: { options = { variable = lib.mkOption { type = lib.types.str; default = name; description = "String in the template that will be substituted by the actual password"; }; path = lib.mkOption { type = lib.types.str; description = "Path to the password store entry"; }; selector = lib.mkOption { type = lib.types.str; default = null; description = "If set, will parse the password metadata as YML and use selector (yq) instead of the password."; }; }; }; mainConfig = config; in lib.mkOption { default = [ ]; type = lib.types.listOf (lib.types.submodule ({ config, ... }: { options = { path = lib.mkOption { type = lib.types.str; description = "Where to place the file."; }; owner = lib.mkOption { type = lib.types.str; default = mainConfig.home.username; description = "Who will own the file."; }; template = lib.mkOption { type = lib.types.path; default = pkgs.writeTextFile { name = "pwfile-template"; text = config.text; }; description = "Path to the template used to make the file. Exclusive with `text`."; }; text = lib.mkOption { type = lib.types.str; default = defaultvar; description = "Content of the template used to make the file. Exclusive with `template`."; }; passwords = lib.mkOption { default = lib.optionalAttrs (config.password != null) { ${defaultvar} = config.password; }; type = lib.types.attrsOf (lib.types.submodule pwtype); description = "Paths to passwords that will substitute the variables in the template. Exclusive with `password`"; }; password = lib.mkOption { type = lib.types.submodule ({ ... }@args: pwtype (args // { name = defaultvar; })); description = "Path to password that will substitute '@PASSWORD@' in the template. Exclusive with `passwords`."; }; }; })); }; }; }