diff --git a/lib/kmxgit/user_manager.ex b/lib/kmxgit/user_manager.ex
index 78e1e90..42e2637 100644
--- a/lib/kmxgit/user_manager.ex
+++ b/lib/kmxgit/user_manager.ex
@@ -259,6 +259,14 @@ defmodule Kmxgit.UserManager do
end
end
+ def otp_init do
+ Repo.transaction fn ->
+ Enum.each list_users(), fn u ->
+ {:ok, _} = User.otp_changeset(u) |> Repo.update()
+ end
+ end
+ end
+
def admin_user_present? do
if Repo.one(from user in User,
where: [is_admin: true],
diff --git a/lib/kmxgit/user_manager/user.ex b/lib/kmxgit/user_manager/user.ex
index 889622a..dc5707c 100644
--- a/lib/kmxgit/user_manager/user.ex
+++ b/lib/kmxgit/user_manager/user.ex
@@ -15,6 +15,8 @@ defmodule Kmxgit.UserManager.User do
field :hashed_password, :string, redact: true
field :is_admin, :boolean, null: false, default: false
field :name, :string
+ field :otp_last, :integer, default: 0, redact: true
+ field :otp_secret, :string, redact: true
has_many :owned_repositories, Repository
field :password, :string, virtual: true, redact: true
field :password_confirmation, :string, virtual: true, redact: true
@@ -45,11 +47,19 @@ defmodule Kmxgit.UserManager.User do
def registration_changeset(user, attrs, opts \\ []) do
user
|> cast(attrs, [:email, :password])
+ |> generate_otp_secret()
|> validate_email()
|> validate_password(opts)
|> common_changeset()
end
+ def otp_changeset(user) do
+ user
+ |> cast(%{}, [])
+ |> generate_otp_secret()
+ |> common_changeset()
+ end
+
defp validate_email(changeset) do
changeset
|> validate_required([:email])
@@ -167,12 +177,17 @@ defmodule Kmxgit.UserManager.User do
defp common_changeset(changeset) do
changeset
|> cast_assoc(:slug)
- |> validate_required([:deploy_only, :email, :hashed_password, :is_admin, :slug])
+ |> validate_required([:deploy_only, :email, :hashed_password, :is_admin, :otp_secret, :slug])
|> validate_email()
|> Markdown.validate_markdown(:description)
|> foreign_key_constraint(:owned_repositories, name: :repositories_user_id_fkey)
end
+ defp generate_otp_secret(changeset) do
+ secret = :crypto.strong_rand_bytes(10) |> Base.encode32()
+ put_change(changeset, :otp_secret, secret)
+ end
+
def changeset(user, attrs \\ %{}) do
user
|> cast(attrs, [:deploy_only, :description, :name, :ssh_keys])
@@ -187,6 +202,15 @@ defmodule Kmxgit.UserManager.User do
|> common_changeset()
end
+ def admin_create_user_changeset(user, attrs \\ %{}, opts \\ []) do
+ user
+ |> cast(attrs, [:deploy_only, :description, :email, :is_admin, :name, :password, :ssh_keys])
+ |> generate_otp_secret()
+ |> validate_email()
+ |> maybe_validate_password(opts)
+ |> common_changeset()
+ end
+
def display_name(user) do
user.name || user.login
end
diff --git a/mix.exs b/mix.exs
index b30afba..d5b490a 100644
--- a/mix.exs
+++ b/mix.exs
@@ -52,6 +52,7 @@ defmodule Kmxgit.MixProject do
{:plug_cowboy, "~> 2.5"},
{:plug_recaptcha, git: "https://github.com/thodg/plug_recaptcha.git"},
{:postgrex, ">= 0.0.0"},
+ {:pot, "~> 1.0"},
{:swoosh, "~> 1.3"},
{:telemetry_metrics, "~> 0.6"},
{:telemetry_poller, "~> 1.0"},
diff --git a/mix.lock b/mix.lock
index 41ce5c8..f3f0a43 100644
--- a/mix.lock
+++ b/mix.lock
@@ -46,6 +46,7 @@
"plug_recaptcha": {:git, "https://github.com/thodg/plug_recaptcha.git", "191eda9f0bc6424e9a1a06da4be24b2a5a84d8e2", []},
"poison": {:hex, :poison, "3.1.0", "d9eb636610e096f86f25d9a46f35a9facac35609a7591b3be3326e99a0484665", [:mix], [], "hexpm", "fec8660eb7733ee4117b85f55799fd3833eb769a6df71ccf8903e8dc5447cfce"},
"postgrex": {:hex, :postgrex, "0.15.13", "7794e697481799aee8982688c261901de493eb64451feee6ea58207d7266d54a", [:mix], [{:connection, "~> 1.0", [hex: :connection, repo: "hexpm", optional: false]}, {:db_connection, "~> 2.1", [hex: :db_connection, repo: "hexpm", optional: false]}, {:decimal, "~> 1.5 or ~> 2.0", [hex: :decimal, repo: "hexpm", optional: false]}, {:jason, "~> 1.0", [hex: :jason, repo: "hexpm", optional: true]}], "hexpm", "3ffb76e1a97cfefe5c6a95632a27ffb67f28871c9741fb585f9d1c3cd2af70f1"},
+ "pot": {:hex, :pot, "1.0.2", "13abb849139fdc04ab8154986abbcb63bdee5de6ed2ba7e1713527e33df923dd", [:rebar3], [], "hexpm", "78fe127f5a4f5f919d6ea5a2a671827bd53eb9d37e5b4128c0ad3df99856c2e0"},
"ranch": {:hex, :ranch, "1.8.0", "8c7a100a139fd57f17327b6413e4167ac559fbc04ca7448e9be9057311597a1d", [:make, :rebar3], [], "hexpm", "49fbcfd3682fab1f5d109351b61257676da1a2fdbe295904176d5e521a2ddfe5"},
"ssl_verify_fun": {:hex, :ssl_verify_fun, "1.1.6", "cf344f5692c82d2cd7554f5ec8fd961548d4fd09e7d22f5b62482e5aeaebd4b0", [:make, :mix, :rebar3], [], "hexpm", "bdb0d2471f453c88ff3908e7686f86f9be327d065cc1ec16fa4540197ea04680"},
"swoosh": {:hex, :swoosh, "1.5.1", "ec1b3fa6a092597ac02444c36c6e3c2bc90c89c02e4e0cb262725d07d610c989", [:mix], [{:cowboy, "~> 1.1 or ~> 2.4", [hex: :cowboy, repo: "hexpm", optional: true]}, {:finch, "~> 0.6", [hex: :finch, repo: "hexpm", optional: true]}, {:gen_smtp, "~> 0.13 or ~> 1.0", [hex: :gen_smtp, repo: "hexpm", optional: true]}, {:hackney, "~> 1.9", [hex: :hackney, repo: "hexpm", optional: true]}, {:jason, "~> 1.0", [hex: :jason, repo: "hexpm", optional: false]}, {:mail, "~> 0.2", [hex: :mail, repo: "hexpm", optional: true]}, {:mime, "~> 1.1 or ~> 2.0", [hex: :mime, repo: "hexpm", optional: false]}, {:plug_cowboy, ">= 1.0.0", [hex: :plug_cowboy, repo: "hexpm", optional: true]}, {:telemetry, "~> 0.4.2 or ~> 1.0", [hex: :telemetry, repo: "hexpm", optional: false]}], "hexpm", "867395cbf0d764b24b6bf3c375137b98432cedb8c7f91ef9bd1c379cf626ac42"},
diff --git a/priv/repo/migrations/20211228071859_add_otp_to_users.exs b/priv/repo/migrations/20211228071859_add_otp_to_users.exs
new file mode 100644
index 0000000..e620b5f
--- /dev/null
+++ b/priv/repo/migrations/20211228071859_add_otp_to_users.exs
@@ -0,0 +1,10 @@
+defmodule Kmxgit.Repo.Migrations.AddOtpToUsers do
+ use Ecto.Migration
+
+ def change do
+ alter table(:users) do
+ add :otp_last, :integer, null: false, default: 0
+ add :otp_secret, :string
+ end
+ end
+end