defmodule GitGud.SshKeys do
@moduledoc """
SSH key management. Stores user public keys for git-over-SSH access.
The `authorized_keys` file the system OpenSSH reads is generated from
these rows via `mix gitgud.ssh.authorized_keys` (or programmatically
via `authorized_keys/0`). Each entry pins the user's command to our
SSH wrapper, mapping back to a git operation against the right repo.
"""
import Ecto.Query, warn: false
alias GitGud.Accounts.User
alias GitGud.Repo
alias GitGud.SshKeys.SshKey
def list_keys(%User{id: uid}) do
SshKey
|> where([k], k.user_id == ^uid)
|> order_by([k], desc: k.inserted_at)
|> Repo.all()
end
def get_key!(id), do: Repo.get!(SshKey, id)
def get_by_fingerprint(fp) when is_binary(fp),
do: Repo.get_by(SshKey, fingerprint: fp)
def create_key(%User{id: uid}, attrs) do
%SshKey{user_id: uid}
|> SshKey.changeset(attrs)
|> Repo.insert()
end
def delete_key(%SshKey{} = key), do: Repo.delete(key)
def touch(%SshKey{} = key) do
key
|> Ecto.Changeset.change(last_used_at: DateTime.utc_now(:second))
|> Repo.update()
end
@doc """
Build the contents of `~git/.ssh/authorized_keys`. Each row is the
user's key prefixed with restrictive options that lock the session to
our wrapper:
command="/usr/local/bin/gitgud-ssh <key_id>",no-port-forwarding,
no-X11-forwarding,no-agent-forwarding,no-pty <user_key>
"""
def authorized_keys do
wrapper =
Application.get_env(:git_gud, __MODULE__, [])
|> Keyword.get(:wrapper_path, "/usr/local/bin/gitgud-ssh")
user_lines =
Repo.all(SshKey)
|> Enum.map(&render_line(wrapper, "u:#{&1.id}", &1.key))
deploy_lines =
Repo.all(GitGud.DeployKeys.DeployKey)
|> Enum.map(&render_line(wrapper, "d:#{&1.id}", &1.key))
(user_lines ++ deploy_lines)
|> Enum.join("\n")
|> Kernel.<>("\n")
end
defp render_line(wrapper, key_arg, ssh_key) do
"command=\"#{wrapper} #{key_arg}\"" <>
",no-port-forwarding,no-X11-forwarding,no-agent-forwarding,no-pty " <>
ssh_key
end
end
2.1 KiB · text
5af55d9