Created
November 16, 2019 22:48
-
-
Save starbelly/fd5ed1ffb977e9535939b73d7ed1e777 to your computer and use it in GitHub Desktop.
recovery_wip
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
From ea1390c4b78175077b52e268842bcbae9af4fea5 Mon Sep 17 00:00:00 2001 | |
From: Bryan Paxton <[email protected]> | |
Date: Mon, 11 Nov 2019 14:22:02 -0600 | |
Subject: [PATCH] wip | |
--- | |
lib/hexpm/accounts/recovery.ex | 38 ++++++-------- | |
lib/hexpm/accounts/recovery_code.ex | 6 +-- | |
lib/hexpm/accounts/user.ex | 1 - | |
...91015010144_create_user_recovery_codes.exs | 8 +-- | |
test/hexpm/accounts/recovery_code_test.exs | 49 ++++++++++++++++--- | |
test/hexpm/accounts/recovery_test.exs | 3 +- | |
6 files changed, 65 insertions(+), 40 deletions(-) | |
diff --git a/lib/hexpm/accounts/recovery.ex b/lib/hexpm/accounts/recovery.ex | |
index 6f220450..1d8dc837 100644 | |
--- a/lib/hexpm/accounts/recovery.ex | |
+++ b/lib/hexpm/accounts/recovery.ex | |
@@ -2,6 +2,10 @@ defmodule Hexpm.Accounts.Recovery do | |
@moduledoc """ | |
Functions used for generating and verifying recovery codes. | |
+ Recovery codes are | |
+ Recovery codes are treated as passwords and thus encrypted using a one way hashing function. | |
+ | |
+ | |
### Examples: | |
iex(1)> codes = [code|_more_codes] = Hexpm.Accounts.Recovery.gen_recovery_codes | |
iex(2)> _hashes = [hash|_more_hashes] = Hexpm.Accounts.Recovery.hash_recovery_codes(codes) | |
@@ -20,33 +24,23 @@ defmodule Hexpm.Accounts.Recovery do | |
def gen_recovery_codes, do: Enum.map(1..5, fn _ -> gen_code() end) | |
- def hash_recovery_codes(codes), do: Enum.map(codes, &hash_code/1) | |
- | |
- # It is not clear whether this should take a binary or a list of binaries at this point. | |
- # It depends how the form in UI is implemented as well as plans for the CLI. | |
- def verify_code(<<code::binary-size(19)>>, hash) do | |
- code | |
- |> String.split("-") | |
- |> verify_code(hash) | |
- end | |
- | |
- def verify_code([_p1, _p2, _p3, _p4] = parts, hash) do | |
- parts | |
- |> Enum.join("-") | |
- |> Bcrypt.verify_pass(hash) | |
- end | |
+ # # It is not clear whether this should take a binary or a list of binaries at this point. | |
+ # # It depends how the form in UI is implemented as well as plans for the CLI. | |
+ # def verify_code(user, <<code::binary-size(19)>>) do | |
+ # code | |
+ # |> String.split("-") | |
+ # |> verify_code(hash) | |
+ # end | |
- def verify_code(_, _), do: false | |
- def hash_code(code), do: Bcrypt.hash_pwd_salt(code) | |
+ defp verify_code | |
defp gen_code do | |
:crypto.strong_rand_bytes(@rand_bytes) | |
- |> Base.hex_encode32() | |
- |> String.downcase() | |
- |> String.codepoints() | |
+ |> Base.hex_encode32(case: :lower) | |
+ |> String.to_charlist() | |
|> Enum.chunk_every(@part_size) | |
- |> Enum.reduce("", fn s, acc -> Enum.join(s) <> "-" <> acc end) | |
- |> String.trim_trailing("-") | |
+ |> Enum.intersperse("-") | |
+ |> List.to_string() | |
end | |
end | |
diff --git a/lib/hexpm/accounts/recovery_code.ex b/lib/hexpm/accounts/recovery_code.ex | |
index 3a3461ec..f0c0c747 100644 | |
--- a/lib/hexpm/accounts/recovery_code.ex | |
+++ b/lib/hexpm/accounts/recovery_code.ex | |
@@ -1,12 +1,11 @@ | |
defmodule Hexpm.Accounts.RecoveryCode do | |
use Hexpm.Schema | |
- schema "user_recovery_codes" do | |
+ @primary_key false | |
+ embedded_schema do | |
field :code_digest, :string | |
field :used_at, :utc_datetime_usec | |
timestamps() | |
- | |
- belongs_to :user, User | |
end | |
def changeset(recovery_code, params) do | |
@@ -14,5 +13,4 @@ defmodule Hexpm.Accounts.RecoveryCode do | |
|> validate_required([:code_digest, :used_at]) | |
|> unique_constraint(:code_digest) | |
end | |
- | |
end | |
diff --git a/lib/hexpm/accounts/user.ex b/lib/hexpm/accounts/user.ex | |
index d4fa84f6..ac5658ed 100644 | |
--- a/lib/hexpm/accounts/user.ex | |
+++ b/lib/hexpm/accounts/user.ex | |
@@ -24,7 +24,6 @@ defmodule Hexpm.Accounts.User do | |
has_many :keys, Key | |
has_many :audit_logs, AuditLog | |
has_many :password_resets, PasswordReset | |
- has_many :recovery_codes, Hexpm.Accounts.RecoveryCode | |
end | |
@username_regex ~r"^[a-z0-9_\-\.]+$" | |
diff --git a/priv/repo/migrations/20191015010144_create_user_recovery_codes.exs b/priv/repo/migrations/20191015010144_create_user_recovery_codes.exs | |
index b1da269d..c4e13166 100644 | |
--- a/priv/repo/migrations/20191015010144_create_user_recovery_codes.exs | |
+++ b/priv/repo/migrations/20191015010144_create_user_recovery_codes.exs | |
@@ -3,12 +3,12 @@ defmodule Hexpm.RepoBase.Migrations.CreateUserRecoveryCodes do | |
def change do | |
create table("user_recovery_codes") do | |
- add :user_id, references(:users), null: false | |
- add :code_digest, :string, null: false | |
- add :used_at, :utc_datetime_usec | |
+ add(:user_id, references(:users), null: false) | |
+ add(:code_digest, :string, null: false) | |
+ add(:used_at, :utc_datetime_usec) | |
timestamps() | |
end | |
- create unique_index(:user_recovery_codes, [:code_digest]) | |
+ create(unique_index(:user_recovery_codes, [:code_digest])) | |
end | |
end | |
diff --git a/test/hexpm/accounts/recovery_code_test.exs b/test/hexpm/accounts/recovery_code_test.exs | |
index 072003ca..a47122e4 100644 | |
--- a/test/hexpm/accounts/recovery_code_test.exs | |
+++ b/test/hexpm/accounts/recovery_code_test.exs | |
@@ -3,19 +3,54 @@ defmodule Hexpm.Accounts.RecoveryCodeTest do | |
alias Hexpm.Accounts.RecoveryCode | |
+ | |
+ defmodule TwoFactor do | |
+ use Ecto.Schema | |
+ | |
+ schema "two_factors" do | |
+ embeds_many :recovery_codes, RecoveryCode, on_replace: :delete | |
+ end | |
+ end | |
+ | |
setup do | |
+ Repo.query("create table two_factors (id serial primary key, recovery_codes jsonb, user_id integer references users(id));") | |
%{user: create_user("starbelly", "[email protected]", "hunter42")} | |
end | |
test "create recovery code and get", %{user: user} do | |
- recovery_code = %RecoveryCode{user_id: user.id, code_digest: "1234"} |> Hexpm.Repo.insert!() | |
- assert Hexpm.Repo.get(RecoveryCode, recovery_code.id).user_id == user.id | |
- end | |
+ two_factor = | |
+ %TwoFactor{} | |
+ |> Ecto.Changeset.change | |
+ |> Ecto.Changeset.put_embed(:recovery_codes, [%RecoveryCode{code_digest: "12345"}]) | |
+ |> Hexpm.Repo.insert!() | |
- test "create unique key name", %{user: user} do | |
- assert %RecoveryCode{} = %RecoveryCode{user_id: user.id, code_digest: "1234"} |> Hexpm.Repo.insert!() | |
- assert %RecoveryCode{} = %RecoveryCode{user_id: user.id, code_digest: "12345"} |> Hexpm.Repo.insert!() | |
- assert_raise Ecto.ConstraintError, fn -> %RecoveryCode{user_id: user.id, code_digest: "1234"} |> Hexpm.Repo.insert!() end | |
+ assert Enum.map(two_factor.recovery_codes, fn(x) -> x.code_digest end) == ["12345"] | |
+ #assert Hexpm.Repo.get(RecoveryCode, recovery_code.id).user_id == user.id | |
end | |
+# test "create unique reocvery code", %{user: user} do | |
+# assert %RecoveryCode{} = | |
+# %RecoveryCode{code_digest: "1234"} |> Hexpm.Repo.insert!() | |
+ | |
+# assert %RecoveryCode{} = | |
+# %RecoveryCode{code_digest: "12345"} |> Hexpm.Repo.insert!() | |
+ | |
+# assert_raise Ecto.ConstraintError, fn -> | |
+# %RecoveryCode{code_digest: "1234"} |> Hexpm.Repo.insert!() | |
+# end | |
+# end | |
+ | |
+# test "update recovery code", %{user: user} do | |
+# recovery_code = %RecoveryCode{code_digest: "1234"} |> Hexpm.Repo.insert!() | |
+# now = DateTime.utc_now() | |
+# changeset = RecoveryCode.changeset(recovery_code, %{used_at: now}) | |
+# assert {:ok, %RecoveryCode{used_at: ^now}} = Hexpm.Repo.update(changeset) | |
+# end | |
+ | |
+# test "delete recovery code", %{user: user} do | |
+# recovery_code = %RecoveryCode{code_digest: "1234"} |> Hexpm.Repo.insert!() | |
+# assert Hexpm.Repo.delete(recovery_code) | |
+# assert Hexpm.Repo.get(RecoveryCode, recovery_code.id) == nil | |
+# end | |
+ | |
end | |
diff --git a/test/hexpm/accounts/recovery_test.exs b/test/hexpm/accounts/recovery_test.exs | |
index 82c60522..7a488657 100644 | |
--- a/test/hexpm/accounts/recovery_test.exs | |
+++ b/test/hexpm/accounts/recovery_test.exs | |
@@ -1,5 +1,4 @@ | |
defmodule Hexpm.Accounts.RecoveryTest do | |
use ExUnit.Case, async: true | |
- doctest Hexpm.Accounts.Recovery | |
+ doctest Hexpm.Accounts.Recovery | |
end | |
- | |
-- | |
2.21.0 (Apple Git-122) |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment