Implement tests for all token actions
This commit is contained in:
parent
89eea2d4c1
commit
64e9edec5a
|
@ -27,5 +27,6 @@ defmodule Hostas.Denizen do
|
|||
%{password_hash: hash} = Bcrypt.add_hash(password)
|
||||
change(changeset, password: hash)
|
||||
end
|
||||
|
||||
defp hash_password(changeset), do: changeset
|
||||
end
|
||||
|
|
|
@ -29,16 +29,19 @@ defmodule Hostas.Token do
|
|||
end
|
||||
|
||||
@doc """
|
||||
Creates a token
|
||||
Creates a token for denizen with the id `denizen_id` that expires in
|
||||
`duration` days.
|
||||
"""
|
||||
def new(denizen_id) do
|
||||
def new(denizen_id, duration \\ @duration_days) do
|
||||
# Create a random token
|
||||
token = Base.encode64(:crypto.strong_rand_bytes(256))
|
||||
|
||||
# Calculate the time of expiry
|
||||
{:ok, time_now} = DateTime.now("Etc/UTC")
|
||||
expiry = DateTime.add(time_now, @duration_days, :day)
|
||||
|> DateTime.truncate(:second)
|
||||
|
||||
expiry =
|
||||
DateTime.add(time_now, duration, :day)
|
||||
|> DateTime.truncate(:second)
|
||||
|
||||
# Register the token
|
||||
Repo.insert(changeset(%Token{}, %{denizen_id: denizen_id, token: token, expires: expiry}))
|
||||
|
|
|
@ -11,9 +11,11 @@ defmodule HostasWeb.Auth.TokenController do
|
|||
provides the correct password
|
||||
"""
|
||||
def create(conn, %{"handle" => handle, "password" => given_password}) do
|
||||
case Repo.one(from d in Denizen,
|
||||
where: d.handle == ^handle,
|
||||
select: %{id: d.id, password: d.password}) do
|
||||
case Repo.one(
|
||||
from d in Denizen,
|
||||
where: d.handle == ^handle,
|
||||
select: %{id: d.id, password: d.password}
|
||||
) do
|
||||
nil ->
|
||||
conn
|
||||
|> put_status(404)
|
||||
|
|
|
@ -26,8 +26,8 @@ defmodule HostasWeb.Router do
|
|||
# create, verify, renew, revoke
|
||||
post "/auth/token", Auth.TokenController, :create
|
||||
get "/auth/token", Auth.TokenController, :verify
|
||||
delete "/auth/token", Auth.TokenController, :revoke
|
||||
get "/auth/token/renew", Auth.TokenController, :renew
|
||||
delete "/auth/token/:id", Auth.TokenController, :revoke
|
||||
get "/auth/token/:id/renew", Auth.TokenController, :renew
|
||||
end
|
||||
|
||||
# Enable LiveDashboard and Swoosh mailbox preview in development
|
||||
|
|
|
@ -2,19 +2,23 @@ defmodule HostasWeb.Auth.TokenControllerTest do
|
|||
use HostasWeb.ConnCase
|
||||
|
||||
# For testing with Ecto
|
||||
import Ecto.Query, only: [from: 2]
|
||||
alias Hostas.Repo
|
||||
|
||||
alias Hostas.Denizen
|
||||
alias Hostas.Token
|
||||
|
||||
@denizen_data %{handle: "testuser", name: "Test User", password: "password"}
|
||||
|
||||
defp create_denizen, do: Repo.insert!(Denizen.changeset(%Denizen{}, @denizen_data))
|
||||
defp create_denizen(handle \\ "denizen") do
|
||||
Repo.insert!(
|
||||
Denizen.changeset(%Denizen{}, %{handle: handle, name: "Test Denizen", password: "password"})
|
||||
)
|
||||
end
|
||||
|
||||
describe "token create" do
|
||||
test "creates a token", %{conn: conn} do
|
||||
create_denizen()
|
||||
|
||||
conn = post(conn, ~p"/hostapi/auth/token", %{handle: "testuser", password: "password"})
|
||||
conn = post(conn, ~p"/hostapi/auth/token", %{handle: "denizen", password: "password"})
|
||||
assert Map.has_key?(json_response(conn, 201), "token")
|
||||
assert Map.has_key?(json_response(conn, 201), "expires")
|
||||
end
|
||||
|
@ -22,12 +26,12 @@ defmodule HostasWeb.Auth.TokenControllerTest do
|
|||
test "fails due to password mismatch", %{conn: conn} do
|
||||
create_denizen()
|
||||
|
||||
conn = post(conn, ~p"/hostapi/auth/token", %{handle: "testuser", password: "incorrect"})
|
||||
conn = post(conn, ~p"/hostapi/auth/token", %{handle: "denizen", password: "incorrect"})
|
||||
assert json_response(conn, 401)["error"] == "Password mismatch"
|
||||
end
|
||||
|
||||
test "fails due to non-existant denizen", %{conn: conn} do
|
||||
conn = post(conn, ~p"/hostapi/auth/token", %{handle: "testuser", password: "password"})
|
||||
conn = post(conn, ~p"/hostapi/auth/token", %{handle: "denizen", password: "password"})
|
||||
assert json_response(conn, 404)["error"] == "No user with handle testuser"
|
||||
end
|
||||
|
||||
|
@ -36,4 +40,113 @@ defmodule HostasWeb.Auth.TokenControllerTest do
|
|||
assert json_response(conn, 422)["error"] == "Missing required parameters"
|
||||
end
|
||||
end
|
||||
|
||||
describe "token verification" do
|
||||
test "succeeds", %{conn: conn} do
|
||||
{:ok, struct} = Token.new(create_denizen().id)
|
||||
|
||||
conn =
|
||||
conn
|
||||
|> put_req_header("authorization", "Bearer #{struct.token}")
|
||||
|> get(~p"/hostapi/auth/token")
|
||||
|
||||
assert Map.has_key?(json_response(conn, 200), "expires")
|
||||
end
|
||||
|
||||
test "fails because of expiry", %{conn: conn} do
|
||||
{:ok, struct} = Token.new(create_denizen().id, -10)
|
||||
|
||||
conn =
|
||||
conn
|
||||
|> put_req_header("authorization", "Bearer #{struct.token}")
|
||||
|> get(~p"/hostapi/auth/token")
|
||||
|
||||
assert json_response(conn, 401)["error"] == "Token expired"
|
||||
end
|
||||
|
||||
test "fails because of unrecognized credentials", %{conn: conn} do
|
||||
conn =
|
||||
conn
|
||||
|> put_req_header("authorization", "Bearer unknown_credential")
|
||||
|> get(~p"/hostapi/auth/token")
|
||||
|
||||
assert json_response(conn, 200)["error"] == "Token expired"
|
||||
end
|
||||
end
|
||||
|
||||
describe "token revocation" do
|
||||
test "succeeds", %{conn: conn} do
|
||||
{:ok, struct} = Token.new(create_denizen().id)
|
||||
|
||||
conn =
|
||||
conn
|
||||
|> put_req_header("authorization", "Bearer #{struct.token}")
|
||||
|> delete(~p"/hostapi/auth/token/#{struct.id}")
|
||||
|
||||
assert json_response(conn, 200)
|
||||
end
|
||||
|
||||
test "fails because it's someone else's token", %{conn: conn} do
|
||||
{:ok, struct1} = Token.new(create_denizen("denizen1").id)
|
||||
{:ok, struct2} = Token.new(create_denizen("denizen2").id)
|
||||
|
||||
conn =
|
||||
conn
|
||||
|> put_req_header("authorization", "Bearer #{struct1.token}")
|
||||
|> delete(~p"/hostapi/auth/token/#{struct2.id}")
|
||||
|
||||
assert json_response(conn, 404)["error"] == "Token not found"
|
||||
end
|
||||
|
||||
test "fails because the token doesn't exist", %{conn: conn} do
|
||||
{:ok, struct} = Token.new(create_denizen().id)
|
||||
|
||||
conn =
|
||||
conn
|
||||
|> put_req_header("authorization", "Bearer #{struct.token}")
|
||||
|> delete(~p"/hostapi/auth/token/#{struct.id + 1}")
|
||||
|
||||
assert json_response(conn, 404)["error"] == "Token not found"
|
||||
end
|
||||
end
|
||||
|
||||
describe "token renewal" do
|
||||
test "succeeds", %{conn: conn} do
|
||||
{:ok, struct} = Token.new(create_denizen().id)
|
||||
|
||||
conn =
|
||||
conn
|
||||
|> put_req_header("authorization", "Bearer #{struct.token}")
|
||||
|> get(~p"/hostapi/auth/token/${struct.id}/renew")
|
||||
|
||||
assert Map.has_key?(json_response(conn, 200), "token")
|
||||
assert Map.has_key?(json_response(conn, 200), "expires")
|
||||
assert not Repo.exists?(from t in Token, where: t.id == ^struct.id)
|
||||
end
|
||||
|
||||
test "fails because it's not the user's token", %{conn: conn} do
|
||||
{:ok, struct1} = Token.new(create_denizen("denizen1").id)
|
||||
{:ok, struct2} = Token.new(create_denizen("denizen2").id)
|
||||
|
||||
conn =
|
||||
conn
|
||||
|> put_req_header("authorization", "Bearer #{struct1.token}")
|
||||
|> get(~p"/hostapi/auth/token/#{struct2.id}/renew")
|
||||
|
||||
assert json_response(conn, 404)["error"] == "Token not found"
|
||||
assert Repo.exists?(from t in Token, where: t.id == ^struct2.id)
|
||||
end
|
||||
|
||||
test "fails because it doesn't exist", %{conn: conn} do
|
||||
{:ok, struct} = Token.new(create_denizen().id)
|
||||
|
||||
conn =
|
||||
conn
|
||||
|> put_req_header("authorization", "Bearer #{struct.token}")
|
||||
|> get(~p"/hostapi/auth/token/#{struct.id + 1}/renew")
|
||||
|
||||
assert json_response(conn, 404)["error"] == "Token not found"
|
||||
assert Repo.exists?(from t in Token, where: t.id == ^struct.id)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
Loading…
Reference in New Issue