Commit bc88a347bb21ea20d160bfc2124052cd1fee4db0

Thomas de Grivel 2021-12-28T08:46:11

init otp secret

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