Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 4 additions & 2 deletions apps/authenticator/lib/crypto/commands/fake_verify_hash.ex
Original file line number Diff line number Diff line change
Expand Up @@ -3,9 +3,11 @@ defmodule Authenticator.Crypto.Commands.FakeVerifyHash do
Simulates a hash verification using the given algorithm.
"""

@behaviour ResourceManager.Credentials.Ports.FakeVerifyHash
@typedoc "All possible hash algorithms"
@type algorithms :: :argon2 | :bcrypt | :pbkdf2

@impl true
@doc "Fake a hash verification using the given algorithm"
@spec execute(algorithm :: algorithms()) :: false
def execute(:argon2), do: Argon2.no_user_verify()
def execute(:bcrypt), do: Bcrypt.no_user_verify()
def execute(:pbkdf2), do: Pbkdf2.no_user_verify()
Expand Down
6 changes: 4 additions & 2 deletions apps/authenticator/lib/crypto/commands/generate_hash.ex
Original file line number Diff line number Diff line change
Expand Up @@ -23,9 +23,11 @@ defmodule Authenticator.Crypto.Commands.GenerateHash do
with a sliding computational cost and generally used to reduce vulnerabilities to brute force attacks.
"""

@behaviour ResourceManager.Credentials.Ports.GenerateHash
@typedoc "All possible hash algorithms"
@type algorithms :: :argon2 | :bcrypt | :pbkdf2

@impl true
@doc "Generates a hash using the given algorithm"
@spec execute(value :: String.t(), algorithm :: algorithms()) :: String.t()
def execute(value, :argon2) when is_binary(value), do: Argon2.hash_pwd_salt(value)
def execute(value, :bcrypt) when is_binary(value), do: Bcrypt.hash_pwd_salt(value)
def execute(value, :pbkdf2) when is_binary(value), do: Pbkdf2.hash_pwd_salt(value)
Expand Down
9 changes: 6 additions & 3 deletions apps/authenticator/lib/crypto/commands/verify_hash.ex
Original file line number Diff line number Diff line change
Expand Up @@ -3,14 +3,17 @@ defmodule Authenticator.Crypto.Commands.VerifyHash do
Verify if a given hash matches the given value.
"""

@behaviour ResourceManager.Credentials.Ports.VerifyHash
@typedoc "All possible hash algorithms"
@type algorithms :: :argon2 | :bcrypt | :pbkdf2

@impl true
@doc "Verifies if a credential matches the given secret"
@spec execute(credential :: map(), value :: String.t()) :: boolean()
def execute(%{password: %{password_hash: hash, algorithm: algorithm}}, password)
when is_binary(password),
do: execute(password, hash, String.to_atom(algorithm))

@impl true
@doc "Verifies if a hash matches the given credential using the passed algorithm"
@spec execute(value :: String.t(), hash :: String.t(), algorithm :: algorithms()) :: boolean()
def execute(value, hash, :argon2)
when is_binary(value) and is_binary(hash),
do: Argon2.verify_pass(value, hash)
Expand Down
18 changes: 0 additions & 18 deletions apps/resource_manager/lib/credentials/ports/fake_verify_hash.ex

This file was deleted.

21 changes: 0 additions & 21 deletions apps/resource_manager/lib/credentials/ports/generate_hash.ex

This file was deleted.

41 changes: 0 additions & 41 deletions apps/resource_manager/lib/credentials/ports/verify_hash.ex

This file was deleted.

6 changes: 3 additions & 3 deletions apps/resource_manager/lib/identities/manager.ex
Original file line number Diff line number Diff line change
Expand Up @@ -11,8 +11,8 @@ defmodule ResourceManager.Identities.Manager do
require Logger

alias Ecto.Multi
alias ResourceManager.Identities.Ports.GetTemporarillyBlocked
alias ResourceManager.Identities.Schemas.{ClientApplication, User}
alias ResourceManager.Ports.Authenticator
alias ResourceManager.Repo

@typedoc "Identities manager supervisor state"
Expand Down Expand Up @@ -86,7 +86,7 @@ defmodule ResourceManager.Identities.Manager do
defp manage_identities do
Multi.new()
|> Multi.run(:get_user_identities, fn _repo, _changes ->
GetTemporarillyBlocked.execute(:user)
Authenticator.get_temporarilly_blocked(:user)
end)
|> Multi.run(:block_user_identities, fn _repo, %{get_user_identities: usernames} ->
block_user_identities(usernames)
Expand All @@ -95,7 +95,7 @@ defmodule ResourceManager.Identities.Manager do
unblock_user_identities()
end)
|> Multi.run(:get_application_identities, fn _repo, _changes ->
GetTemporarillyBlocked.execute(:application)
Authenticator.get_temporarilly_blocked(:application)
end)
|> Multi.run(:block_application_identities, fn _, %{get_application_identities: client_ids} ->
block_application_identities(client_ids)
Expand Down

This file was deleted.

Original file line number Diff line number Diff line change
Expand Up @@ -9,9 +9,9 @@ defmodule ResourceManager.Identities.Schemas.ClientApplication do

import Ecto.Changeset

alias ResourceManager.Credentials.Ports.GenerateHash
alias ResourceManager.Credentials.Schemas.PublicKey
alias ResourceManager.Permissions.Schemas.Scope
alias ResourceManager.Ports.Authenticator

@typedoc "User schema fields"
@type t :: %__MODULE__{
Expand Down Expand Up @@ -73,7 +73,7 @@ defmodule ResourceManager.Identities.Schemas.ClientApplication do
defp generate_secret(%{valid?: false} = changeset), do: changeset

defp generate_secret(changeset) do
secret = GenerateHash.execute(Ecto.UUID.generate(), :bcrypt)
secret = Authenticator.generate_hash(Ecto.UUID.generate(), :bcrypt)
put_change(changeset, :secret, secret)
end

Expand Down
37 changes: 37 additions & 0 deletions apps/resource_manager/lib/ports/authenticator.ex
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
defmodule ResourceManager.Ports.Authenticator do
@moduledoc """
Port to access Authenticator domain commands.
"""

@typedoc "All possible hash algorithms"
@type algorithms :: :argon2 | :bcrypt | :pbkdf2

@doc "Delegates to #{__MODULE__}.fake_verify_hash/1 command"
@callback fake_verify_hash(algorithm :: algorithms()) :: false

@doc "Delegates to #{__MODULE__}.generate_hash/2 command"
@callback generate_hash(secret :: map() | String.t(), algorithm :: algorithms()) :: String.t()

@doc "Delegates to #{__MODULE__}.get_temporarilly_blocked/1 command"
@callback get_temporarilly_blocked(subject_type :: :user | :application) :: list(String.t())

@doc "Gets the hash and algorithm from the input and verifies if it matches the hash"
@spec fake_verify_hash(algorithm :: algorithms()) :: false
def fake_verify_hash(algorithm), do: implementation().fake_verify_hash(algorithm)

@doc "Delegates execution to generate hash command"
@spec generate_hash(secret :: String.t(), algorithm :: algorithms()) :: String.t()
def generate_hash(secret, algorithm \\ :argon2),
do: implementation().generate_hash(secret, algorithm)

@doc "Gets the temporarilly blocked subjects"
@spec get_temporarilly_blocked(subject_type :: :user | :application) :: false
def get_temporarilly_blocked(subject_type),
do: implementation().get_temporarilly_blocked(subject_type)

defp implementation do
:resource_manager
|> Application.get_env(__MODULE__)
|> Keyword.get(:command)
end
end
Original file line number Diff line number Diff line change
@@ -1,9 +1,9 @@
defmodule ResourceManager.Identities.ClientApplicationsTest do
use ResourceManager.DataCase, async: true

alias ResourceManager.Credentials.Ports.GenerateHashMock
alias ResourceManager.Identities.ClientApplications
alias ResourceManager.Identities.Schemas.ClientApplication
alias ResourceManager.Ports.AuthenticatorMock

setup do
{:ok, client_application: insert!(:client_application)}
Expand All @@ -13,7 +13,7 @@ defmodule ResourceManager.Identities.ClientApplicationsTest do
test "succeed if params are valid" do
params = %{name: "my-test-application"}

expect(GenerateHashMock, :execute, fn secret, :bcrypt ->
expect(AuthenticatorMock, :generate_hash, fn secret, :bcrypt ->
assert is_binary(secret)
gen_hashed_password(Ecto.UUID.generate())
end)
Expand Down
Original file line number Diff line number Diff line change
@@ -1,9 +1,9 @@
defmodule ResourceManager.Identities.Commands.CreateIdentityTest do
use ResourceManager.DataCase, async: true

alias ResourceManager.Credentials.Ports.GenerateHashMock
alias ResourceManager.Identities.Commands.CreateIdentity
alias ResourceManager.Identities.Schemas.{ClientApplication, User}
alias ResourceManager.Ports.AuthenticatorMock
alias ResourceManager.Repo

setup do
Expand All @@ -30,7 +30,7 @@ defmodule ResourceManager.Identities.Commands.CreateIdentityTest do
scopes: ctx.scopes
}

expect(GenerateHashMock, :execute, fn secret, :bcrypt ->
expect(AuthenticatorMock, :generate_hash, fn secret, :bcrypt ->
assert is_binary(secret)
gen_hashed_password(Ecto.UUID.generate())
end)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ defmodule ResourceManager.Identities.ManagerTest do
use ResourceManager.DataCase, async: true

alias ResourceManager.Identities.Manager
alias ResourceManager.Identities.Ports.GetTemporarillyBlockedMock
alias ResourceManager.Ports.AuthenticatorMock
alias ResourceManager.Identities.Schemas.{ClientApplication, User}

describe "#{Manager}.execute/0" do
Expand All @@ -11,16 +11,19 @@ defmodule ResourceManager.Identities.ManagerTest do
end

test "succeeds and temporary blocks users", %{user: %{id: id, username: username}} do
expect(GetTemporarillyBlockedMock, :execute, fn :user -> {:ok, [username]} end)
expect(GetTemporarillyBlockedMock, :execute, fn :application -> {:ok, []} end)
expect(AuthenticatorMock, :get_temporarilly_blocked, fn :user -> {:ok, [username]} end)
expect(AuthenticatorMock, :get_temporarilly_blocked, fn :application -> {:ok, []} end)

assert {:ok, :managed} == Manager.execute()
assert %{status: "temporary_blocked", blocked_until: %{}} = Repo.get(User, id)
end

test "succeeds and temporary blocks applications", %{app: %{id: id, client_id: client_id}} do
expect(GetTemporarillyBlockedMock, :execute, fn :user -> {:ok, []} end)
expect(GetTemporarillyBlockedMock, :execute, fn :application -> {:ok, [client_id]} end)
expect(AuthenticatorMock, :get_temporarilly_blocked, fn :user -> {:ok, []} end)

expect(AuthenticatorMock, :get_temporarilly_blocked, fn :application ->
{:ok, [client_id]}
end)

assert {:ok, :managed} == Manager.execute()
assert %{status: "temporary_blocked", blocked_until: %{}} = Repo.get(ClientApplication, id)
Expand All @@ -29,8 +32,8 @@ defmodule ResourceManager.Identities.ManagerTest do
test "succeeds and unblock users" do
user = insert!(:user, status: "temporary_blocked", blocked_until: blocked_until())

expect(GetTemporarillyBlockedMock, :execute, fn :user -> {:ok, []} end)
expect(GetTemporarillyBlockedMock, :execute, fn :application -> {:ok, []} end)
expect(AuthenticatorMock, :get_temporarilly_blocked, fn :user -> {:ok, []} end)
expect(AuthenticatorMock, :get_temporarilly_blocked, fn :application -> {:ok, []} end)

assert {:ok, :managed} == Manager.execute()
assert %{status: "active", blocked_until: nil} = Repo.get(User, user.id)
Expand All @@ -44,8 +47,11 @@ defmodule ResourceManager.Identities.ManagerTest do
blocked_until: blocked_until()
)

expect(GetTemporarillyBlockedMock, :execute, fn :user -> {:ok, []} end)
expect(GetTemporarillyBlockedMock, :execute, fn :application -> {:ok, [app.client_id]} end)
expect(AuthenticatorMock, :get_temporarilly_blocked, fn :user -> {:ok, []} end)

expect(AuthenticatorMock, :get_temporarilly_blocked, fn :application ->
{:ok, [app.client_id]}
end)

assert {:ok, :managed} == Manager.execute()
assert %{status: "active", blocked_until: nil} = Repo.get(ClientApplication, app.id)
Expand Down
9 changes: 2 additions & 7 deletions apps/resource_manager/test/support/mocks.ex
Original file line number Diff line number Diff line change
@@ -1,11 +1,6 @@
for module <- [
# Credential ports mocks
ResourceManager.Credentials.Ports.GenerateHash,
ResourceManager.Credentials.Ports.VerifyHash,
ResourceManager.Credentials.Ports.FakeVerifyHash,

# Identity ports mocks
ResourceManager.Identities.Ports.GetTemporarillyBlocked
# Authenticator
ResourceManager.Ports.Authenticator
] do
Mox.defmock(:"#{module}Mock", for: module)
end
12 changes: 1 addition & 11 deletions config/config.exs
Original file line number Diff line number Diff line change
Expand Up @@ -28,17 +28,7 @@ config :resource_manager, ResourceManager.Repo,
port: 5432,
pool_size: 10

config :resource_manager, ResourceManager.Credentials.Ports.GenerateHash,
command: Authenticator.Crypto.Commands.GenerateHash

config :resource_manager, ResourceManager.Credentials.Ports.VerifyHash,
command: Authenticator.Crypto.Commands.VerifyHash

config :resource_manager, ResourceManager.Credentials.Ports.FakeVerifyHash,
command: Authenticator.Crypto.Commands.FakeVerifyHash

config :resource_manager, ResourceManager.Identities.Ports.GetTemporarillyBlocked,
command: Authenticator.SignIn.Commands.GetTemporarillyBlocked
config :resource_manager, ResourceManager.Ports.Authenticator, command: Authenticator

################
# Authenticator
Expand Down
13 changes: 2 additions & 11 deletions config/test.exs
Original file line number Diff line number Diff line change
Expand Up @@ -17,17 +17,8 @@ config :resource_manager, ResourceManager.Repo,
config :resource_manager, ResourceManager.Application,
children: [ResourceManager.Repo, ResourceManager.Credentials.BlocklistPasswordCache]

config :resource_manager, ResourceManager.Credentials.Ports.GenerateHash,
command: ResourceManager.Credentials.Ports.GenerateHashMock

config :resource_manager, ResourceManager.Credentials.Ports.VerifyHash,
command: ResourceManager.Credentials.Ports.VerifyHashMock

config :resource_manager, ResourceManager.Credentials.Ports.FakeVerifyHash,
command: ResourceManager.Credentials.Ports.FakeVerifyHashMock

config :resource_manager, ResourceManager.Identities.Ports.GetTemporarillyBlocked,
command: ResourceManager.Identities.Ports.GetTemporarillyBlockedMock
config :resource_manager, ResourceManager.Ports.Authenticator,
command: ResourceManager.Ports.AuthenticatorMock

################
# Authenticator
Expand Down