2.0 KiB · text 5af55d9
defmodule GitGud.DeployKeys.DeployKey do
use Ecto.Schema
import Ecto.Changeset
alias GitGud.Repositories.Repository
@valid_algos ~w(ssh-ed25519 ssh-rsa ecdsa-sha2-nistp256 ecdsa-sha2-nistp384 ecdsa-sha2-nistp521)
schema "deploy_keys" do
field :title, :string
field :key, :string
field :fingerprint, :string
field :read_write, :boolean, default: false
field :last_used_at, :utc_datetime
belongs_to :repository, Repository
timestamps(type: :utc_datetime)
end
def changeset(key, attrs) do
key
|> cast(attrs, [:title, :key, :read_write])
|> validate_required([:title, :key])
|> validate_length(:title, max: 100)
|> update_change(:key, &normalize/1)
|> validate_key_format()
|> compute_fingerprint()
|> unique_constraint(:fingerprint)
end
defp normalize(nil), do: nil
defp normalize(k), do: String.trim(k)
defp validate_key_format(cs) do
case get_field(cs, :key) do
nil ->
cs
key ->
case String.split(key, " ", parts: 3) do
[algo, b64 | _] when algo in @valid_algos ->
case Base.decode64(b64) do
{:ok, _} -> cs
:error -> add_error(cs, :key, "invalid base64")
end
[algo, _] ->
add_error(cs, :key, "unsupported algorithm: #{algo}")
_ ->
add_error(cs, :key, "expected `<algo> <base64> [comment]`")
end
end
end
defp compute_fingerprint(cs) do
case {get_field(cs, :key), cs.valid?} do
{key, true} when is_binary(key) ->
[_algo, b64 | _] = String.split(key, " ", parts: 3)
case Base.decode64(b64) do
{:ok, bytes} ->
fp =
"SHA256:" <>
(bytes |> then(&:crypto.hash(:sha256, &1)) |> Base.encode64(padding: false))
put_change(cs, :fingerprint, fp)
:error ->
cs
end
_ ->
cs
end
end
def valid_algorithms, do: @valid_algos
end