Merge pull request #34 from poanetwork/ferigis.33.adding_mnesia_and_users

[#33] adding mnesia and setting up users
This commit is contained in:
Joseph Yiasemides 2018-08-13 14:48:53 +02:00 committed by GitHub
commit a709efb5b3
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
13 changed files with 249 additions and 3 deletions

View File

@ -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:

View File

@ -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

View File

@ -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"

View File

@ -35,4 +35,7 @@ config :poa_backend,
:subscriptions,
[
{:dashboard_receiver, [:ethereum_metrics]}
]
]
config :mnesia,
dir: '_build/test' # make sure this directory exists!

View File

@ -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, [])

70
lib/poa_backend/auth.ex Normal file
View File

@ -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

View File

@ -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

View File

@ -0,0 +1,4 @@
defmodule POABackend.Auth.Repo do
use Ecto.Repo, otp_app: :poa_backend
@moduledoc false
end

View File

@ -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},

View File

@ -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"},

View File

@ -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

8
test/ancillary/utils.ex Normal file
View File

@ -0,0 +1,8 @@
defmodule POABackend.Ancillary.Utils do
@moduledoc false
def clear_db do
:mnesia.clear_table(:users)
end
end

63
test/auth/user_test.exs Normal file
View File

@ -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