From 882ca4f70098c2a3dce023f55d90794a9bc22eca Mon Sep 17 00:00:00 2001 From: Thelonius Kort Date: Tue, 24 Jun 2025 19:11:09 +0200 Subject: [PATCH] Add add- and remove-secret commands --- totp-code | 55 ++++++++++++++++++++++++++++++++++++++++++++++++++----- 1 file changed, 50 insertions(+), 5 deletions(-) diff --git a/totp-code b/totp-code index 98a64da..7ed30bd 100755 --- a/totp-code +++ b/totp-code @@ -1,5 +1,5 @@ #!/usr/bin/env -S ERL_FLAGS=+B elixir -Mix.install([{:nimble_totp, ">= 1.0.0"},{:yaml_elixir, "~> 2.9"}]) +Mix.install([{:nimble_totp, ">= 1.0.0"},{:yaml_elixir, "~> 2.9"},{:ymlr, "~> 5.0"}]) if System.get_env("DEPS_ONLY") == "true" do System.halt(0) @@ -15,9 +15,14 @@ defmodule TOTP do $ totp-code list-identifiers - $ totp-code set-secret identifier secret # men at work + $ totp-code add-secret identifier secret + + $ totp-code remove-secret identifier """ + + @conffile ".totp-code.conf" + def main([]), do: cmd("help", []) def main(argv) do [command | args] = argv @@ -39,12 +44,38 @@ defmodule TOTP do |> IO.puts() end + defp cmd("add-secret", [identifier, secret]) do + secret = validize_secret(secret) + config = read_config() + secrets = config["secrets"] |> Map.put(identifier, secret) + config = Map.replace(config, "secrets", secrets) + |> Ymlr.document!() + conf_path() + |> File.write!(config) + end + defp cmd("add-secret", _), do: IO.puts("add-secret expects exactly 2 arguments: identifier and secret") + + defp cmd("remove-secret", [identifier]) do + config = read_config() + secrets = config["secrets"] |> Map.delete(identifier) + config = Map.replace(config, "secrets", secrets) + |> Ymlr.document!() + conf_path() + |> File.write!(config) + end + defp cmd("remove-secret", _), do: IO.puts("remove-secret expects exactly 1 argument: identifier") + defp cmd("help", _), do: IO.puts(@moduledoc) + defp cmd(command, _), do: IO.puts("no such command: '#{command}'") + defp read_config() do - System.user_home() - |> Path.join(".totp-code.conf") - |> YamlElixir.read_from_file!() + if File.exists?(conf_path()) do + conf_path() + |> YamlElixir.read_from_file!() + else + %{"secrets" => %{}} + end end defp get_secret(identifier) do @@ -52,6 +83,20 @@ defmodule TOTP do |> Map.get("secrets") |> Map.get(identifier) end + + defp validize_secret(secret) do + secret = String.replace(secret, ~r/\s/,"") + if Base.decode32(secret) == :error do + IO.puts("invalid secret: '#{secret}'") + System.halt(1) + end + secret + end + + defp conf_path() do + System.user_home() + |> Path.join(@conffile) + end end TOTP.main(System.argv())