2023-06-06 16:38:52 +00:00
|
|
|
defmodule HostasWeb.Auth.TokenController do
|
2023-06-07 17:12:33 +00:00
|
|
|
import Ecto.Query, only: [from: 2]
|
2023-06-06 16:38:52 +00:00
|
|
|
use HostasWeb, :controller
|
|
|
|
|
2023-06-07 17:12:33 +00:00
|
|
|
alias Hostas.Repo
|
|
|
|
alias Hostas.Denizen
|
|
|
|
alias Hostas.Token
|
|
|
|
|
|
|
|
@doc """
|
|
|
|
Generates an API token. Responds with the token if the user
|
|
|
|
provides the correct password
|
|
|
|
"""
|
|
|
|
def create(conn, %{"handle" => handle, "password" => given_password}) do
|
2023-06-09 17:03:11 +00:00
|
|
|
case Repo.one(
|
|
|
|
from d in Denizen,
|
|
|
|
where: d.handle == ^handle,
|
|
|
|
select: %{id: d.id, password: d.password}
|
|
|
|
) do
|
2023-06-07 17:12:33 +00:00
|
|
|
nil ->
|
|
|
|
conn
|
|
|
|
|> put_status(404)
|
|
|
|
|> json(%{"error" => "No user with handle #{handle}"})
|
|
|
|
|
|
|
|
denizen ->
|
|
|
|
%{id: denizen_id, password: real_password_hash} = denizen
|
|
|
|
|
|
|
|
if Bcrypt.verify_pass(given_password, real_password_hash) do
|
2023-06-09 01:01:47 +00:00
|
|
|
{:ok, token_struct} = Token.new(denizen_id)
|
2023-06-07 17:12:33 +00:00
|
|
|
|
|
|
|
conn
|
|
|
|
|> put_status(201)
|
|
|
|
|> json(Map.take(token_struct, [:token, :expires]))
|
|
|
|
else
|
|
|
|
# Reject the request, as passwords don't match
|
|
|
|
conn
|
|
|
|
|> put_status(401)
|
|
|
|
|> json(%{"error" => "Password mismatch"})
|
|
|
|
end
|
|
|
|
end
|
|
|
|
end
|
|
|
|
|
2023-06-09 01:01:47 +00:00
|
|
|
def create(conn, _params) do
|
2023-06-07 17:12:33 +00:00
|
|
|
conn
|
|
|
|
|> put_status(422)
|
2023-06-08 16:41:02 +00:00
|
|
|
|> json(%{"error" => "Missing required parameters"})
|
2023-06-07 17:12:33 +00:00
|
|
|
end
|
|
|
|
|
|
|
|
@doc """
|
|
|
|
Responds with 200 OK if the requester's `Bearing` header
|
2023-06-14 17:02:10 +00:00
|
|
|
contains a valid, non-expired API token, along with a
|
|
|
|
payload with an `expires` key detailing when the key
|
|
|
|
expires.
|
2023-06-07 17:12:33 +00:00
|
|
|
"""
|
2023-06-14 17:02:10 +00:00
|
|
|
def verify(conn, _params) do
|
|
|
|
conn
|
|
|
|
|> put_status(200)
|
|
|
|
|> json(%{"expires" => conn.assigns[:token].expires})
|
2023-06-07 17:12:33 +00:00
|
|
|
end
|
|
|
|
|
|
|
|
@doc """
|
|
|
|
Deletes the token the requester used in the `Bearing` header
|
|
|
|
"""
|
2023-06-14 17:02:10 +00:00
|
|
|
def revoke(conn, %{"id" => id_param}) do
|
|
|
|
with {:parsed_id, {id, ""}} <- {:parsed_id, Integer.parse(id_param, 10)},
|
2023-06-14 17:12:28 +00:00
|
|
|
{:ok, token} <- fetch_token(id, conn) do
|
2023-06-14 17:02:10 +00:00
|
|
|
Repo.delete_all(from t in Token, where: t.id == ^token.id)
|
|
|
|
|
|
|
|
conn
|
|
|
|
|> send_resp(200, "")
|
|
|
|
else
|
|
|
|
_ ->
|
|
|
|
conn
|
|
|
|
|> put_status(404)
|
|
|
|
|> json(%{"error" => "Token not found"})
|
|
|
|
end
|
2023-06-07 17:12:33 +00:00
|
|
|
end
|
|
|
|
|
|
|
|
@doc """
|
|
|
|
Deletes the token the requester used in the `Bearing` header
|
|
|
|
and responds with a new one if the old one was valid and unexpired
|
|
|
|
"""
|
2023-06-14 17:12:28 +00:00
|
|
|
def renew(conn, %{"id" => id_param}) do
|
|
|
|
with {:parsed_id, {id, ""}} <- {:parsed_id, Integer.parse(id_param, 10)},
|
|
|
|
{:ok, token} <- fetch_token(id, conn) do
|
|
|
|
Repo.delete_all(from t in Token, where: t.id == ^token.id)
|
|
|
|
|
|
|
|
{:ok, new_token} = Token.new(conn.assigns[:denizen].id)
|
|
|
|
|
|
|
|
conn
|
|
|
|
|> put_status(201)
|
|
|
|
|> json(Map.take(new_token, [:token, :expires]))
|
|
|
|
else
|
|
|
|
_ ->
|
|
|
|
conn
|
|
|
|
|> put_status(404)
|
|
|
|
|> json(%{"error" => "Token not found"})
|
|
|
|
end
|
2023-06-06 16:38:52 +00:00
|
|
|
end
|
2023-06-14 17:02:10 +00:00
|
|
|
|
|
|
|
defp fetch_token(id, conn) do
|
|
|
|
if id == conn.assigns[:token].id do
|
|
|
|
{:ok, conn.assigns[:token]}
|
|
|
|
else
|
|
|
|
case Repo.one(from t in Token, where: t.id == ^id) do
|
|
|
|
nil -> {:error, :token_not_found}
|
2023-06-14 17:12:28 +00:00
|
|
|
token ->
|
|
|
|
# If the denizen doesn't own it, we lie about its existence
|
|
|
|
if token.denizen_id == conn.assigns[:denizen].id do
|
|
|
|
{:ok, token}
|
|
|
|
else
|
|
|
|
{:error, :token_not_found}
|
|
|
|
end
|
2023-06-14 17:02:10 +00:00
|
|
|
end
|
|
|
|
end
|
|
|
|
end
|
2023-06-06 16:38:52 +00:00
|
|
|
end
|