Merge pull request #34 from poanetwork/ferigis.33.adding_mnesia_and_users
[#33] adding mnesia and setting up users
This commit is contained in:
commit
a709efb5b3
|
@ -14,6 +14,8 @@ jobs:
|
|||
- run: mix local.hex --force
|
||||
- run: mix local.rebar --force
|
||||
- run: mix deps.get
|
||||
- run: env MIX_ENV=test mix ecto.create
|
||||
- run: env MIX_ENV=test mix ecto.migrate
|
||||
- run: mix test
|
||||
- run: mix credo
|
||||
- restore_cache:
|
||||
|
|
16
README.md
16
README.md
|
@ -21,7 +21,21 @@ That command will create a `doc/` folder with the actual Documentation.
|
|||
|
||||
## Run Tests
|
||||
|
||||
In order to run the tests we have to run the command
|
||||
`POABackend` uses [Mnesia](http://erlang.org/doc/man/mnesia.html) as a local database with [Ecto](https://hexdocs.pm/ecto/Ecto.html). In order to have this running we have to create a folder where `Mnesia` will store our data. In order to do that we have to define it in the `config/test.exs` file like this:
|
||||
|
||||
```
|
||||
config :mnesia,
|
||||
dir: 'your/local/path' # make sure this directory exists!
|
||||
```
|
||||
|
||||
once we have the path defined we have to create the database (those commands must be run only once if you are going to use always this same path for testing). In your root folder run:
|
||||
|
||||
```
|
||||
MIX_ENV=test mix ecto.create
|
||||
MIX_ENV=test mix ecto.migrate
|
||||
```
|
||||
|
||||
Now the environment is set. We can run the tests with:
|
||||
|
||||
```
|
||||
mix test
|
||||
|
|
|
@ -7,4 +7,16 @@ config :plug, :statuses, %{
|
|||
422 => "Unprocessable Entity"
|
||||
}
|
||||
|
||||
config :poa_backend,
|
||||
ecto_repos: [POABackend.Auth.Repo]
|
||||
|
||||
# here we configure the needed data for Ecto and Mnesia (DB)
|
||||
config :poa_backend, POABackend.Auth.Repo,
|
||||
adapter: EctoMnesia.Adapter,
|
||||
host: Kernel.node(),
|
||||
storage_type: :disc_copies # this will store the data on disk and memory
|
||||
|
||||
config :mnesia,
|
||||
dir: 'priv/data/mnesia' # make sure this directory exists!
|
||||
|
||||
import_config "#{Mix.env}.exs"
|
||||
|
|
|
@ -36,3 +36,6 @@ config :poa_backend,
|
|||
[
|
||||
{:dashboard_receiver, [:ethereum_metrics]}
|
||||
]
|
||||
|
||||
config :mnesia,
|
||||
dir: '_build/test' # make sure this directory exists!
|
|
@ -7,6 +7,7 @@ defmodule POABackend.Application do
|
|||
import Supervisor.Spec
|
||||
|
||||
children = [
|
||||
supervisor(POABackend.Auth.Repo, []),
|
||||
supervisor(POABackend.CustomHandler.Supervisor, []),
|
||||
supervisor(POABackend.Metrics.Supervisor, []),
|
||||
supervisor(POABackend.Receivers.Supervisor, [])
|
||||
|
|
|
@ -0,0 +1,70 @@
|
|||
defmodule POABackend.Auth do
|
||||
|
||||
@moduledoc """
|
||||
This module defines the API for the Authorisation
|
||||
"""
|
||||
|
||||
alias POABackend.Auth.Models.User
|
||||
alias POABackend.Auth.Repo
|
||||
|
||||
@doc """
|
||||
Registers a user in the system.
|
||||
"""
|
||||
@spec create_user(String.t, String.t, Boolean.t) :: {:ok, User.t} | {:error, :already_exists} | {:error, Ecto.Changeset.t}
|
||||
def create_user(user_name, password, active \\ true) do
|
||||
try do
|
||||
%User{}
|
||||
|> User.changeset(%{user: user_name,
|
||||
password: password,
|
||||
active: active})
|
||||
|> Repo.insert
|
||||
rescue
|
||||
x in CaseClauseError -> x.term
|
||||
end
|
||||
end
|
||||
|
||||
@doc """
|
||||
Get a user from the database based in the user name
|
||||
"""
|
||||
@spec get_user(String.t) :: User.t | nil
|
||||
def get_user(user) do
|
||||
Repo.get(User, user)
|
||||
end
|
||||
|
||||
@doc """
|
||||
Deletes a user from the database based in the given user
|
||||
"""
|
||||
@spec remove_user(User.t) :: :ok
|
||||
def remove_user(user) do
|
||||
_ = Repo.delete(user)
|
||||
:ok
|
||||
end
|
||||
|
||||
@doc """
|
||||
This function Activates a user, storing `active: true` in the database for the given user
|
||||
"""
|
||||
@spec activate_user(User.t) :: {:ok, User.t} | {:error, Ecto.Changeset.t}
|
||||
def activate_user(user) do
|
||||
user
|
||||
|> User.changeset(%{active: true})
|
||||
|> Repo.update
|
||||
end
|
||||
|
||||
@doc """
|
||||
This function Deactivates a user, storing `active: false` in the database for the given user
|
||||
"""
|
||||
@spec deactivate_user(User.t) :: {:ok, User.t} | {:error, Ecto.Changeset.t}
|
||||
def deactivate_user(user) do
|
||||
user
|
||||
|> User.changeset(%{active: false})
|
||||
|> Repo.update
|
||||
end
|
||||
|
||||
@doc """
|
||||
Checks if a user is active
|
||||
"""
|
||||
@spec user_active?(User.t) :: Boolean.t
|
||||
def user_active?(%User{active: true}), do: true
|
||||
def user_active?(%User{active: _}), do: false
|
||||
|
||||
end
|
|
@ -0,0 +1,43 @@
|
|||
defmodule POABackend.Auth.Models.User do
|
||||
use Ecto.Schema
|
||||
alias __MODULE__
|
||||
import Ecto.Changeset
|
||||
|
||||
@moduledoc """
|
||||
This module encapsulates the _User_ model
|
||||
"""
|
||||
|
||||
@primary_key {:user, :string, []}
|
||||
|
||||
schema "users" do
|
||||
# field :user, :string, primary_key: true
|
||||
field :password_hash, :string
|
||||
field :password, :string, virtual: true
|
||||
field :active, :boolean, default: true
|
||||
|
||||
timestamps()
|
||||
end
|
||||
|
||||
@type t :: %__MODULE__{user: String.t,
|
||||
password_hash: String.t,
|
||||
password: String.t,
|
||||
active: :boolean}
|
||||
|
||||
def changeset(%User{} = user, params \\ %{}) do
|
||||
user
|
||||
|> cast(params, ~w(user password active))
|
||||
|> validate_required([:user])
|
||||
|> validate_length(:password, min: 8)
|
||||
|> unique_constraint(:user)
|
||||
|> put_password_hash()
|
||||
end
|
||||
|
||||
defp put_password_hash(%{changes: %{password: password}} = changeset) do
|
||||
alias Comeonin.Bcrypt
|
||||
|
||||
changeset
|
||||
|> put_change(:password_hash, Bcrypt.hashpwsalt(password))
|
||||
|> put_change(:password, nil)
|
||||
end
|
||||
defp put_password_hash(%{changes: %{}} = changeset), do: changeset
|
||||
end
|
|
@ -0,0 +1,4 @@
|
|||
defmodule POABackend.Auth.Repo do
|
||||
use Ecto.Repo, otp_app: :poa_backend
|
||||
@moduledoc false
|
||||
end
|
8
mix.exs
8
mix.exs
|
@ -9,6 +9,7 @@ defmodule POABackend.MixProject do
|
|||
version: @version,
|
||||
elixir: "~> 1.6",
|
||||
start_permanent: Mix.env() == :prod,
|
||||
elixirc_paths: elixirc_paths(Mix.env),
|
||||
deps: deps(),
|
||||
aliases: aliases(),
|
||||
docs: docs(),
|
||||
|
@ -16,10 +17,13 @@ defmodule POABackend.MixProject do
|
|||
]
|
||||
end
|
||||
|
||||
defp elixirc_paths(:test), do: ["lib", "test/ancillary"]
|
||||
defp elixirc_paths(_), do: ["lib"]
|
||||
|
||||
# Run "mix help compile.app" to learn about applications.
|
||||
def application do
|
||||
[
|
||||
extra_applications: [:logger, :cowboy, :plug, :poison, :worker_pool],
|
||||
extra_applications: [:logger, :cowboy, :plug, :poison, :worker_pool, :ecto_mnesia],
|
||||
mod: {POABackend.Application, []}
|
||||
]
|
||||
end
|
||||
|
@ -35,6 +39,8 @@ defmodule POABackend.MixProject do
|
|||
{:ex_aws_dynamo, "~> 2.0"},
|
||||
{:hackney, "~> 1.12"},
|
||||
{:msgpax, "~> 2.1"},
|
||||
{:ecto_mnesia, "~> 0.9.1"},
|
||||
{:comeonin, "~> 3.2"},
|
||||
|
||||
# Tests
|
||||
{:credo, "~> 0.9", only: [:dev, :test], runtime: false},
|
||||
|
|
7
mix.lock
7
mix.lock
|
@ -1,12 +1,18 @@
|
|||
%{
|
||||
"bunt": {:hex, :bunt, "0.2.0", "951c6e801e8b1d2cbe58ebbd3e616a869061ddadcc4863d0a2182541acae9a38", [:mix], [], "hexpm"},
|
||||
"certifi": {:hex, :certifi, "2.3.1", "d0f424232390bf47d82da8478022301c561cf6445b5b5fb6a84d49a9e76d2639", [:rebar3], [{:parse_trans, "3.2.0", [hex: :parse_trans, repo: "hexpm", optional: false]}], "hexpm"},
|
||||
"comeonin": {:hex, :comeonin, "3.2.0", "cb10995a22aed6812667efb3856f548818c85d85394d8132bc116fbd6995c1ef", [], [{:elixir_make, "~> 0.4", [hex: :elixir_make, repo: "hexpm", optional: false]}], "hexpm"},
|
||||
"confex": {:hex, :confex, "3.3.1", "8febaf751bf293a16a1ed2cbd258459cdcc7ca53cfa61d3f83d49dd276a992b4", [], [], "hexpm"},
|
||||
"cowboy": {:hex, :cowboy, "1.0.4", "a324a8df9f2316c833a470d918aaf73ae894278b8aa6226ce7a9bf699388f878", [:make, :rebar], [{:cowlib, "~> 1.0.0", [hex: :cowlib, repo: "hexpm", optional: false]}, {:ranch, "~> 1.0", [hex: :ranch, repo: "hexpm", optional: false]}], "hexpm"},
|
||||
"cowlib": {:hex, :cowlib, "1.0.2", "9d769a1d062c9c3ac753096f868ca121e2730b9a377de23dec0f7e08b1df84ee", [:make], [], "hexpm"},
|
||||
"credo": {:hex, :credo, "0.9.3", "76fa3e9e497ab282e0cf64b98a624aa11da702854c52c82db1bf24e54ab7c97a", [:mix], [{:bunt, "~> 0.2.0", [hex: :bunt, repo: "hexpm", optional: false]}, {:poison, ">= 0.0.0", [hex: :poison, repo: "hexpm", optional: false]}], "hexpm"},
|
||||
"decimal": {:hex, :decimal, "1.5.0", "b0433a36d0e2430e3d50291b1c65f53c37d56f83665b43d79963684865beab68", [], [], "hexpm"},
|
||||
"dialyxir": {:hex, :dialyxir, "0.5.1", "b331b091720fd93e878137add264bac4f644e1ddae07a70bf7062c7862c4b952", [:mix], [], "hexpm"},
|
||||
"distillery": {:hex, :distillery, "1.5.3", "b2f4fc34ec71ab4f1202a796f9290e068883b042319aa8c9aa45377ecac8597a", [], [], "hexpm"},
|
||||
"earmark": {:hex, :earmark, "1.2.5", "4d21980d5d2862a2e13ec3c49ad9ad783ffc7ca5769cf6ff891a4553fbaae761", [:mix], [], "hexpm"},
|
||||
"ecto": {:hex, :ecto, "2.1.6", "29b45f393c2ecd99f83e418ea9b0a2af6078ecb30f401481abac8a473c490f84", [], [{:db_connection, "~> 1.1", [hex: :db_connection, repo: "hexpm", optional: true]}, {:decimal, "~> 1.2", [hex: :decimal, repo: "hexpm", optional: false]}, {:mariaex, "~> 0.8.0", [hex: :mariaex, repo: "hexpm", optional: true]}, {:poison, "~> 2.2 or ~> 3.0", [hex: :poison, repo: "hexpm", optional: true]}, {:poolboy, "~> 1.5", [hex: :poolboy, repo: "hexpm", optional: false]}, {:postgrex, "~> 0.13.0", [hex: :postgrex, repo: "hexpm", optional: true]}, {:sbroker, "~> 1.0", [hex: :sbroker, repo: "hexpm", optional: true]}], "hexpm"},
|
||||
"ecto_mnesia": {:hex, :ecto_mnesia, "0.9.1", "5887e3bcae972d9a26494a87cbd318d401a65b4af324480073d11df654d1b235", [], [{:confex, "~> 3.3", [hex: :confex, repo: "hexpm", optional: false]}, {:decimal, "~> 1.5", [hex: :decimal, repo: "hexpm", optional: false]}, {:ecto, "~> 2.1.6", [hex: :ecto, repo: "hexpm", optional: false]}], "hexpm"},
|
||||
"elixir_make": {:hex, :elixir_make, "0.4.2", "332c649d08c18bc1ecc73b1befc68c647136de4f340b548844efc796405743bf", [], [], "hexpm"},
|
||||
"ex_aws": {:hex, :ex_aws, "2.0.2", "8df2f96f58624a399abe5a0ce26db648ee848aca6393b9c65c939ece9ac07bfa", [:mix], [{:configparser_ex, "~> 2.0", [hex: :configparser_ex, repo: "hexpm", optional: true]}, {:hackney, "1.6.3 or 1.6.5 or 1.7.1 or 1.8.6 or ~> 1.9", [hex: :hackney, repo: "hexpm", optional: true]}, {:jsx, "~> 2.8", [hex: :jsx, repo: "hexpm", optional: true]}, {:poison, ">= 1.2.0", [hex: :poison, repo: "hexpm", optional: true]}, {:sweet_xml, "~> 0.6", [hex: :sweet_xml, repo: "hexpm", optional: true]}, {:xml_builder, "~> 0.1.0", [hex: :xml_builder, repo: "hexpm", optional: true]}], "hexpm"},
|
||||
"ex_aws_dynamo": {:hex, :ex_aws_dynamo, "2.0.0", "07b1117bbd1b1d04e2598190834c69c271db1d357cc21b82240d1a0b17194165", [:mix], [{:ex_aws, "~> 2.0.0", [hex: :ex_aws, repo: "hexpm", optional: false]}], "hexpm"},
|
||||
"ex_doc": {:hex, :ex_doc, "0.18.3", "f4b0e4a2ec6f333dccf761838a4b253d75e11f714b85ae271c9ae361367897b7", [:mix], [{:earmark, "~> 1.1", [hex: :earmark, repo: "hexpm", optional: false]}], "hexpm"},
|
||||
|
@ -25,6 +31,7 @@
|
|||
"parse_trans": {:hex, :parse_trans, "3.2.0", "2adfa4daf80c14dc36f522cf190eb5c4ee3e28008fc6394397c16f62a26258c2", [:rebar3], [], "hexpm"},
|
||||
"plug": {:hex, :plug, "1.6.0", "90d338a44c8cd762c32d3ea324f6728445c6145b51895403854b77f1536f1617", [:mix], [{:cowboy, "~> 1.0.1 or ~> 1.1 or ~> 2.4", [hex: :cowboy, repo: "hexpm", optional: true]}, {:mime, "~> 1.0", [hex: :mime, repo: "hexpm", optional: false]}], "hexpm"},
|
||||
"poison": {:hex, :poison, "3.1.0", "d9eb636610e096f86f25d9a46f35a9facac35609a7591b3be3326e99a0484665", [:mix], [], "hexpm"},
|
||||
"poolboy": {:hex, :poolboy, "1.5.1", "6b46163901cfd0a1b43d692657ed9d7e599853b3b21b95ae5ae0a777cf9b6ca8", [], [], "hexpm"},
|
||||
"ranch": {:hex, :ranch, "1.5.0", "f04166f456790fee2ac1aa05a02745cc75783c2bfb26d39faf6aefc9a3d3a58a", [:rebar3], [], "hexpm"},
|
||||
"ssl_verify_fun": {:hex, :ssl_verify_fun, "1.1.1", "28a4d65b7f59893bc2c7de786dec1e1555bd742d336043fe644ae956c3497fbe", [:make, :rebar], [], "hexpm"},
|
||||
"unicode_util_compat": {:hex, :unicode_util_compat, "0.3.1", "a1f612a7b512638634a603c8f401892afbf99b8ce93a45041f8aaca99cadb85e", [:rebar3], [], "hexpm"},
|
||||
|
|
|
@ -0,0 +1,13 @@
|
|||
defmodule POABackend.Auth.Repo.Migrations.CreateUsers do
|
||||
use Ecto.Migration
|
||||
|
||||
def change do
|
||||
create_if_not_exists table(:users, primary_key: false) do
|
||||
add :user, :string, primary_key: true
|
||||
add :password_hash, :string
|
||||
add :active, :boolean, default: true
|
||||
|
||||
timestamps()
|
||||
end
|
||||
end
|
||||
end
|
|
@ -0,0 +1,8 @@
|
|||
defmodule POABackend.Ancillary.Utils do
|
||||
@moduledoc false
|
||||
|
||||
def clear_db do
|
||||
:mnesia.clear_table(:users)
|
||||
end
|
||||
|
||||
end
|
|
@ -0,0 +1,63 @@
|
|||
defmodule Auth.UserTest do
|
||||
use ExUnit.Case
|
||||
alias POABackend.Auth
|
||||
alias POABackend.Ancillary.Utils
|
||||
|
||||
setup do
|
||||
Utils.clear_db()
|
||||
|
||||
on_exit fn ->
|
||||
Utils.clear_db()
|
||||
end
|
||||
|
||||
[]
|
||||
end
|
||||
|
||||
test "create a new user" do
|
||||
alias Comeonin.Bcrypt
|
||||
|
||||
password = "mypassword"
|
||||
|
||||
{:ok, user} = Auth.create_user("ferigis", password)
|
||||
|
||||
assert Bcrypt.checkpw(password, user.password_hash)
|
||||
|
||||
{:error, :already_exists} = Auth.create_user("ferigis", "mypassword")
|
||||
end
|
||||
|
||||
test "get a user" do
|
||||
{:ok, user} = Auth.create_user("ferigis", "mypassword")
|
||||
|
||||
assert user == Auth.get_user("ferigis")
|
||||
|
||||
assert nil == Auth.get_user("otheruser")
|
||||
end
|
||||
|
||||
test "remove a user" do
|
||||
assert nil == Auth.get_user("ferigis")
|
||||
|
||||
{:ok, user} = Auth.create_user("ferigis", "mypassword")
|
||||
|
||||
assert user == Auth.get_user("ferigis")
|
||||
|
||||
:ok = Auth.remove_user(user)
|
||||
|
||||
assert nil == Auth.get_user("ferigis")
|
||||
end
|
||||
|
||||
test "activate/deactivate a user" do
|
||||
{:ok, user} = Auth.create_user("ferigis", "mypassword")
|
||||
|
||||
assert Auth.user_active?(user)
|
||||
|
||||
{:ok, _} = Auth.deactivate_user(user)
|
||||
user = Auth.get_user("ferigis")
|
||||
|
||||
refute Auth.user_active?(user)
|
||||
|
||||
{:ok, _} = Auth.activate_user(user)
|
||||
user = Auth.get_user("ferigis")
|
||||
|
||||
assert Auth.user_active?(user)
|
||||
end
|
||||
end
|
Loading…
Reference in New Issue