[#37] adding endpoint for creating users

This commit is contained in:
Felipe Ripoll 2018-08-07 05:52:11 -06:00
parent 7c80ed298b
commit b5519a7d25
7 changed files with 385 additions and 66 deletions

2
.gitignore vendored
View File

@ -1,6 +1,8 @@
/_build
/cover
/deps
/priv/keys
/priv/data
erl_crash.dump
*.ez
*.beam

View File

@ -47,9 +47,19 @@ config :poa_backend,
{:certfile, "priv/keys/localhost.cert"}
]
# this configuration is needed for Guardian, the Auth library
config :poa_backend, POABackend.Auth.Guardian,
issuer: "poa_backend",
secret_key: "LQYmeqQfrphbxUjJltkwH4xnosLc+2S2e8KuYWctMenNY9bmgwnrH8r3ii9FP/8V"
# this is a list of admins/passwords for authorisation endpoints
config :poa_backend,
:admins,
[
{"admin1", "password12345678"},
{"admin2", "password87654321"}
]
# configuration for mnesia DB
config :mnesia,
dir: '_build/test' # make sure this directory exists!

View File

@ -71,11 +71,12 @@ defmodule POABackend.Auth do
This function authenticates a user/password pair
"""
@spec authenticate_user(String.t, String.t) :: {:ok, User.t} | {:error, :notvalid}
def authenticate_user(user, password) do
def authenticate_user(user_name, password) do
alias Comeonin.Bcrypt
with user <- get_user(user),
true <- Bcrypt.checkpw(password, user.password_hash)
with user <- get_user(user_name),
true <- Bcrypt.checkpw(password, user.password_hash),
true <- user_active?(user)
do
{:ok, user}
else
@ -83,4 +84,69 @@ defmodule POABackend.Auth do
end
end
@doc """
Authenticates an Admin
"""
@spec authenticate_admin(String.t, String.t) :: {:ok, :valid} | {:error, :notvalid}
def authenticate_admin(admin_name, password) do
with admins <- Application.get_env(:poa_backend, :admins),
true <- Enum.member?(admins, {admin_name, password})
do
{:ok, :valid}
else
_error -> {:error, :notvalid}
end
end
@doc """
Generates a valid user name randomply
"""
@spec generate_user_name() :: String.t
def generate_user_name do
8
|> random_string()
|> generate_user_name()
end
@doc """
This function is exported for testing purposes
"""
@spec generate_user_name(String.t) :: String.t
def generate_user_name(user_name) do
case valid_user_name?(user_name) do
true -> user_name
false -> generate_user_name()
end
end
@doc """
Generates a password randomply
"""
@spec generate_password() :: String.t
def generate_password do
random_string(15)
end
@doc """
Validates if a given user name is valid or not. It is valid if doesn't exist a user
with that name in the database already
"""
@spec valid_user_name?(String.t) :: Boolean.t
def valid_user_name?(user_name) do
case get_user(user_name) do
nil -> true
_ -> false
end
end
# ---------------------------------------
# Private Functions
# ---------------------------------------
defp random_string(length) do
length
|> :crypto.strong_rand_bytes()
|> Base.url_encode64
|> binary_part(0, length)
end
end

View File

@ -32,6 +32,39 @@ defmodule POABackend.Auth.Router do
end
end
post "/user" do
with {"authorization", "Basic " <> base64} <- List.keyfind(conn.req_headers, "authorization", 0),
{:ok, decoded64} <- Base.decode64(base64),
[admin_name, admin_password] <- String.split(decoded64, ":"),
{:ok, :valid} <- Auth.authenticate_admin(admin_name, admin_password)
do
user_name = Map.get(conn.params, "user-name", Auth.generate_user_name())
password = Map.get(conn.params, "password", Auth.generate_password())
case Auth.valid_user_name?(user_name) do
true ->
{:ok, _user} = Auth.create_user(user_name, password)
{:ok, result} =
%{:'user-name' => user_name,
:password => password}
|> Poison.encode
send_resp(conn, 200, result)
false ->
conn
|> send_resp(409, "")
|> halt
end
else
_error ->
conn
|> send_resp(401, "")
|> halt
end
end
match _ do
send_resp(conn, 404, "")
end

View File

@ -6,6 +6,8 @@ defmodule Auth.APITest do
@base_url "https://localhost:4003"
@user "ferigis"
@password "1234567890"
@admin "admin1"
@admin_pwd "password12345678"
setup do
Utils.clear_db()
@ -107,6 +109,182 @@ defmodule Auth.APITest do
assert {404, :nobody} == result
end
# ----------------------------------------
# /user Endpoint Tests
# ----------------------------------------
test "trying to create a user with wrong Admin Credentials [JSON]" do
url = @base_url <> "/user"
mime_type = "application/json"
headers = [
{"Content-Type", mime_type},
{"authorization", "Basic " <> Base.encode64(@admin <> ":" <> "wrongpassword")}
]
result =
%{:'agent-id' => "agentID"}
|> Poison.encode!()
|> post(url, headers)
assert {401, :nobody} == result
end
test "trying to create a user with wrong Admin Credentials [MSGPACK]" do
url = @base_url <> "/user"
mime_type = "application/msgpack"
headers = [
{"Content-Type", mime_type},
{"authorization", "Basic " <> Base.encode64(@admin <> ":" <> "wrongpassword")}
]
result =
%{:'agent-id' => "agentID"}
|> Msgpax.pack!()
|> post(url, headers)
assert {401, :nobody} == result
end
test "create a user without credentials [JSON]" do
url = @base_url <> "/user"
mime_type = "application/json"
headers = [
{"Content-Type", mime_type},
{"authorization", "Basic " <> Base.encode64(@admin <> ":" <> @admin_pwd)}
]
{200, %{"user-name" => user_name, "password" => password}} =
%{:'agent-id' => "agentID"}
|> Poison.encode!()
|> post(url, headers)
assert Auth.authenticate_user(user_name, password)
end
test "create a user without credentials [MSGPACK]" do
url = @base_url <> "/user"
mime_type = "application/msgpack"
headers = [
{"Content-Type", mime_type},
{"authorization", "Basic " <> Base.encode64(@admin <> ":" <> @admin_pwd)}
]
{200, %{"user-name" => user_name, "password" => password}} =
%{:'agent-id' => "agentID"}
|> Msgpax.pack!()
|> post(url, headers)
assert Auth.authenticate_user(user_name, password)
end
test "create a user with user_name [JSON]" do
url = @base_url <> "/user"
mime_type = "application/json"
headers = [
{"Content-Type", mime_type},
{"authorization", "Basic " <> Base.encode64(@admin <> ":" <> @admin_pwd)}
]
user_name = "newUserName"
{200, %{"user-name" => ^user_name, "password" => password}} =
%{:'agent-id' => "agentID", :'user-name' => user_name}
|> Poison.encode!()
|> post(url, headers)
assert Auth.authenticate_user(user_name, password)
end
test "create a user with user_name [MSGPACK]" do
url = @base_url <> "/user"
mime_type = "application/msgpack"
headers = [
{"Content-Type", mime_type},
{"authorization", "Basic " <> Base.encode64(@admin <> ":" <> @admin_pwd)}
]
user_name = "newUserName"
{200, %{"user-name" => ^user_name, "password" => password}} =
%{:'agent-id' => "agentID", :'user-name' => user_name}
|> Msgpax.pack!()
|> post(url, headers)
assert Auth.authenticate_user(user_name, password)
end
test "create a user with user_name and password [JSON]" do
url = @base_url <> "/user"
mime_type = "application/json"
headers = [
{"Content-Type", mime_type},
{"authorization", "Basic " <> Base.encode64(@admin <> ":" <> @admin_pwd)}
]
user_name = "newUserName2"
password = "mypasswordfornewuser"
{200, %{"user-name" => ^user_name, "password" => ^password}} =
%{:'agent-id' => "agentID",
:'user-name' => user_name,
:password => password}
|> Poison.encode!()
|> post(url, headers)
assert Auth.authenticate_user(user_name, password)
end
test "create a user with user_name and password [MSGPACK]" do
url = @base_url <> "/user"
mime_type = "application/msgpack"
headers = [
{"Content-Type", mime_type},
{"authorization", "Basic " <> Base.encode64(@admin <> ":" <> @admin_pwd)}
]
user_name = "newUserName2"
password = "mypasswordfornewuser"
{200, %{"user-name" => ^user_name, "password" => ^password}} =
%{:'agent-id' => "agentID",
:'user-name' => user_name,
:password => password}
|> Msgpax.pack!()
|> post(url, headers)
assert Auth.authenticate_user(user_name, password)
end
test "create user which already exists [JSON]" do
url = @base_url <> "/user"
mime_type = "application/json"
headers = [
{"Content-Type", mime_type},
{"authorization", "Basic " <> Base.encode64(@admin <> ":" <> @admin_pwd)}
]
result =
%{:'agent-id' => "agentID",
:'user-name' => @user}
|> Poison.encode!()
|> post(url, headers)
assert {409, :nobody} == result
end
test "create user which already exists [MSGPACK]" do
url = @base_url <> "/user"
mime_type = "application/msgpack"
headers = [
{"Content-Type", mime_type},
{"authorization", "Basic " <> Base.encode64(@admin <> ":" <> @admin_pwd)}
]
result =
%{:'agent-id' => "agentID",
:'user-name' => @user}
|> Msgpax.pack!()
|> post(url, headers)
assert {409, :nobody} == result
end
# ----------------------------------------
# Internal functions
# ----------------------------------------

93
test/auth/auth_test.exs Normal file
View File

@ -0,0 +1,93 @@
defmodule Auth.AuthTest 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
user_name = "ferigis"
password = "mypassword"
{:ok, user} = Auth.create_user(user_name, password)
assert Bcrypt.checkpw(password, user.password_hash)
{:error, :already_exists} = Auth.create_user(user_name, "mypassword")
end
test "get a user" do
user_name = "ferigis"
password = "mypassword"
{:ok, user} = Auth.create_user(user_name, password)
assert user == Auth.get_user(user_name)
assert nil == Auth.get_user("otheruser")
end
test "remove a user" do
user_name = "ferigis"
password = "mypassword"
assert nil == Auth.get_user(user_name)
{:ok, user} = Auth.create_user(user_name, password)
assert user == Auth.get_user(user_name)
:ok = Auth.remove_user(user)
assert nil == Auth.get_user(user_name)
end
test "activate/deactivate a user" do
user_name = "ferigis"
password = "mypassword"
{:ok, user} = Auth.create_user(user_name, password)
assert Auth.user_active?(user)
assert {:ok, user} == Auth.authenticate_user(user_name, password)
{:ok, _} = Auth.deactivate_user(user)
user = Auth.get_user(user_name)
refute Auth.user_active?(user)
assert {:error, :notvalid} == Auth.authenticate_user(user_name, password)
{:ok, _} = Auth.activate_user(user)
user = Auth.get_user(user_name)
assert Auth.user_active?(user)
assert {:ok, user} == Auth.authenticate_user(user_name, password)
end
test "authenticate an admin" do
{:ok, :valid} = Auth.authenticate_admin("admin1", "password12345678")
{:ok, :valid} = Auth.authenticate_admin("admin2", "password87654321")
{:error, :notvalid} = Auth.authenticate_admin("admin2", "wrong_password")
end
test "generate user_name [coverage]" do
user_name = "ferigis"
password = "mypassword"
{:ok, _user} = Auth.create_user(user_name, password)
user_name2 = Auth.generate_user_name(user_name)
refute user_name == user_name2
end
end

View File

@ -1,63 +0,0 @@
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