mirror of
https://github.com/outbackdingo/firezone.git
synced 2026-01-27 18:18:55 +00:00
Refactor api tokens (#1370)
Related to #1364 Co-authored-by: Jamil Bou Kheir <jamilbk@users.noreply.github.com>
This commit is contained in:
@@ -1,47 +1,49 @@
|
||||
defmodule FzHttp.ApiTokens do
|
||||
@moduledoc """
|
||||
The ApiTokens context.
|
||||
"""
|
||||
|
||||
import Ecto.Query, warn: false
|
||||
alias FzHttp.Repo
|
||||
|
||||
alias FzHttp.{Repo, Validator}
|
||||
alias FzHttp.Users
|
||||
alias FzHttp.ApiTokens.ApiToken
|
||||
|
||||
def count_by_user_id(user_id) do
|
||||
Repo.aggregate(from(a in ApiToken, where: a.user_id == ^user_id), :count)
|
||||
ApiToken.Query.by_user_id(user_id)
|
||||
|> Repo.aggregate(:count)
|
||||
end
|
||||
|
||||
def list_api_tokens do
|
||||
Repo.all(ApiToken)
|
||||
ApiToken.Query.all()
|
||||
|> Repo.list()
|
||||
end
|
||||
|
||||
def list_api_tokens(user_id) do
|
||||
Repo.all(from a in ApiToken, where: a.user_id == ^user_id)
|
||||
def list_api_tokens_by_user_id(user_id) do
|
||||
ApiToken.Query.by_user_id(user_id)
|
||||
|> Repo.list()
|
||||
end
|
||||
|
||||
def get_api_token(id), do: Repo.get(ApiToken, id)
|
||||
def fetch_api_token_by_id(id) do
|
||||
if Validator.valid_uuid?(id) do
|
||||
ApiToken.Query.by_id(id)
|
||||
|> Repo.fetch()
|
||||
else
|
||||
{:error, :not_found}
|
||||
end
|
||||
end
|
||||
|
||||
def get_api_token!(id), do: Repo.get!(ApiToken, id)
|
||||
|
||||
def get_unexpired_api_token(api_token_id) do
|
||||
now = DateTime.utc_now()
|
||||
|
||||
Repo.one(
|
||||
from a in ApiToken,
|
||||
where: a.id == ^api_token_id and a.expires_at >= ^now
|
||||
)
|
||||
def fetch_unexpired_api_token_by_id(id) do
|
||||
if Validator.valid_uuid?(id) do
|
||||
ApiToken.Query.by_id(id)
|
||||
|> ApiToken.Query.not_expired()
|
||||
|> Repo.fetch()
|
||||
else
|
||||
{:error, :not_found}
|
||||
end
|
||||
end
|
||||
|
||||
def new_api_token(attrs \\ %{}) do
|
||||
ApiToken.create_changeset(attrs)
|
||||
ApiToken.Changeset.changeset(attrs)
|
||||
end
|
||||
|
||||
def create_user_api_token(%FzHttp.Users.User{} = user, params) do
|
||||
changeset =
|
||||
params
|
||||
|> Enum.into(%{"user_id" => user.id})
|
||||
|> ApiToken.create_changeset(count_per_user: count_by_user_id(user.id))
|
||||
count_by_user_id = count_by_user_id(user.id)
|
||||
changeset = ApiToken.Changeset.create_changeset(user, params, max: count_by_user_id)
|
||||
|
||||
with {:ok, api_token} <- Repo.insert(changeset) do
|
||||
FzHttp.Telemetry.create_api_token()
|
||||
@@ -53,10 +55,14 @@ defmodule FzHttp.ApiTokens do
|
||||
DateTime.diff(api_token.expires_at, DateTime.utc_now()) < 0
|
||||
end
|
||||
|
||||
def delete_api_token(%ApiToken{} = api_token) do
|
||||
with {:ok, api_token} <- Repo.delete(api_token) do
|
||||
FzHttp.Telemetry.delete_api_token(api_token)
|
||||
{:ok, api_token}
|
||||
def delete_api_token_by_id(api_token_id, %Users.User{} = user) do
|
||||
with {:ok, api_token} <- fetch_api_token_by_id(api_token_id),
|
||||
# A user can only delete his/her own MFA method!
|
||||
true <- api_token.user_id == user.id do
|
||||
{:ok, Repo.delete!(api_token)}
|
||||
else
|
||||
{:error, :not_found} -> {:error, :not_found}
|
||||
false -> {:error, :not_found}
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
@@ -1,8 +1,5 @@
|
||||
defmodule FzHttp.ApiTokens.ApiToken do
|
||||
use FzHttp, :schema
|
||||
import Ecto.Changeset
|
||||
|
||||
@max_per_user 25
|
||||
|
||||
schema "api_tokens" do
|
||||
field :expires_at, :utc_datetime_usec
|
||||
@@ -14,38 +11,4 @@ defmodule FzHttp.ApiTokens.ApiToken do
|
||||
|
||||
timestamps(updated_at: false)
|
||||
end
|
||||
|
||||
def create_changeset(attrs, opts \\ []) do
|
||||
%__MODULE__{}
|
||||
|> cast(attrs, ~w[
|
||||
user_id
|
||||
expires_in
|
||||
expires_at
|
||||
]a)
|
||||
|> validate_required([:user_id, :expires_in])
|
||||
|> validate_number(:expires_in, greater_than_or_equal_to: 1, less_than_or_equal_to: 90)
|
||||
|> resolve_expires_at()
|
||||
|> validate_required(:expires_at)
|
||||
|> assoc_constraint(:user)
|
||||
|> maybe_validate_count_per_user(@max_per_user, opts[:count_per_user])
|
||||
end
|
||||
|
||||
def max_per_user, do: @max_per_user
|
||||
|
||||
defp resolve_expires_at(changeset) do
|
||||
expires_at =
|
||||
DateTime.utc_now()
|
||||
|> DateTime.add(get_field(changeset, :expires_in), :day)
|
||||
|
||||
put_change(changeset, :expires_at, expires_at)
|
||||
end
|
||||
|
||||
defp maybe_validate_count_per_user(changeset, max, num) when is_integer(num) and num >= max do
|
||||
# XXX: This suffers from a race condition because the count happens in a separate transaction.
|
||||
# At the moment it's not a big concern. Fixing it would require locking against INSERTs or DELETEs
|
||||
# while counts are happening.
|
||||
add_error(changeset, :base, "token limit of #{@max_per_user} reached")
|
||||
end
|
||||
|
||||
defp maybe_validate_count_per_user(changeset, _, _), do: changeset
|
||||
end
|
||||
|
||||
44
apps/fz_http/lib/fz_http/api_tokens/api_token/changeset.ex
Normal file
44
apps/fz_http/lib/fz_http/api_tokens/api_token/changeset.ex
Normal file
@@ -0,0 +1,44 @@
|
||||
defmodule FzHttp.ApiTokens.ApiToken.Changeset do
|
||||
use FzHttp, :changeset
|
||||
alias FzHttp.ApiTokens.ApiToken
|
||||
|
||||
@max_per_user 25
|
||||
|
||||
def create_changeset(user, attrs, opts \\ []) do
|
||||
changeset(attrs)
|
||||
|> put_change(:user_id, user.id)
|
||||
|> assoc_constraint(:user)
|
||||
|> maybe_validate_count_per_user(@max_per_user, opts[:max])
|
||||
end
|
||||
|
||||
def changeset(api_token \\ %ApiToken{}, attrs) do
|
||||
api_token
|
||||
|> cast(attrs, ~w[
|
||||
expires_in
|
||||
expires_at
|
||||
]a)
|
||||
|> validate_required([:expires_in])
|
||||
|> validate_number(:expires_in, greater_than_or_equal_to: 1, less_than_or_equal_to: 90)
|
||||
|> resolve_expires_at()
|
||||
|> validate_required(:expires_at)
|
||||
end
|
||||
|
||||
def max_per_user, do: @max_per_user
|
||||
|
||||
defp resolve_expires_at(changeset) do
|
||||
expires_at =
|
||||
DateTime.utc_now()
|
||||
|> DateTime.add(get_field(changeset, :expires_in), :day)
|
||||
|
||||
put_change(changeset, :expires_at, expires_at)
|
||||
end
|
||||
|
||||
defp maybe_validate_count_per_user(changeset, max, num) when is_integer(num) and num >= max do
|
||||
# XXX: This suffers from a race condition because the count happens in a separate transaction.
|
||||
# At the moment it's not a big concern. Fixing it would require locking against INSERTs or DELETEs
|
||||
# while counts are happening.
|
||||
add_error(changeset, :base, "token limit of #{@max_per_user} reached")
|
||||
end
|
||||
|
||||
defp maybe_validate_count_per_user(changeset, _, _), do: changeset
|
||||
end
|
||||
19
apps/fz_http/lib/fz_http/api_tokens/api_token/query.ex
Normal file
19
apps/fz_http/lib/fz_http/api_tokens/api_token/query.ex
Normal file
@@ -0,0 +1,19 @@
|
||||
defmodule FzHttp.ApiTokens.ApiToken.Query do
|
||||
use FzHttp, :query
|
||||
|
||||
def all do
|
||||
from(api_tokens in FzHttp.ApiTokens.ApiToken, as: :api_tokens)
|
||||
end
|
||||
|
||||
def by_id(queryable \\ all(), id) do
|
||||
where(queryable, [api_tokens: api_tokens], api_tokens.id == ^id)
|
||||
end
|
||||
|
||||
def by_user_id(queryable \\ all(), user_id) do
|
||||
where(queryable, [api_tokens: api_tokens], api_tokens.user_id == ^user_id)
|
||||
end
|
||||
|
||||
def not_expired(queryable \\ all()) do
|
||||
where(queryable, [api_tokens: api_tokens], api_tokens.expires_at >= fragment("NOW()"))
|
||||
end
|
||||
end
|
||||
@@ -18,12 +18,10 @@ defmodule FzHttpWeb.Auth.JSON.Authentication do
|
||||
|
||||
@impl Guardian
|
||||
def resource_from_claims(%{"api" => api_token_id}) do
|
||||
with %ApiTokens.ApiToken{} = api_token <- ApiTokens.get_unexpired_api_token(api_token_id),
|
||||
with {:ok, %ApiTokens.ApiToken{} = api_token} <-
|
||||
ApiTokens.fetch_unexpired_api_token_by_id(api_token_id),
|
||||
{:ok, %Users.User{} = user} <- Users.fetch_user_by_id(api_token.user_id) do
|
||||
{:ok, user}
|
||||
else
|
||||
_ ->
|
||||
{:error, :resource_not_found}
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
@@ -200,7 +200,7 @@
|
||||
<% end %>
|
||||
</div>
|
||||
|
||||
<%= if length(@api_tokens) < FzHttp.ApiTokens.ApiToken.max_per_user() do %>
|
||||
<%= if length(@api_tokens) < FzHttp.ApiTokens.ApiToken.Changeset.max_per_user() do %>
|
||||
<.link patch={~p"/settings/account/api_token"} class="button">
|
||||
<span class="icon is-small">
|
||||
<i class="mdi mdi-plus"></i>
|
||||
|
||||
@@ -23,13 +23,14 @@ defmodule FzHttpWeb.SettingLive.Account do
|
||||
def mount(params, _session, socket) do
|
||||
Endpoint.subscribe(@live_sessions_topic)
|
||||
{:ok, methods} = MFA.list_methods_for_user(socket.assigns.current_user)
|
||||
{:ok, api_tokens} = ApiTokens.list_api_tokens_by_user_id(socket.assigns.current_user.id)
|
||||
|
||||
socket =
|
||||
socket
|
||||
|> assign(:api_token_id, params["api_token_id"])
|
||||
|> assign(:subscribe_link, subscribe_link())
|
||||
|> assign(:allow_delete, Users.count_by_role(:admin) > 1)
|
||||
|> assign(:api_tokens, ApiTokens.list_api_tokens(socket.assigns.current_user.id))
|
||||
|> assign(:api_tokens, api_tokens)
|
||||
|> assign(:changeset, Users.change_user(socket.assigns.current_user))
|
||||
|> assign(:methods, methods)
|
||||
|> assign(:page_title, @page_title)
|
||||
@@ -45,30 +46,32 @@ defmodule FzHttpWeb.SettingLive.Account do
|
||||
|
||||
@impl Phoenix.LiveView
|
||||
def handle_params(%{"api_token_id" => api_token_id}, _url, socket) do
|
||||
{:noreply,
|
||||
socket
|
||||
|> assign(:api_token, ApiTokens.get_api_token!(api_token_id))}
|
||||
{:ok, api_token} = ApiTokens.fetch_unexpired_api_token_by_id(api_token_id)
|
||||
{:noreply, assign(socket, :api_token, api_token)}
|
||||
end
|
||||
|
||||
@impl Phoenix.LiveView
|
||||
def handle_params(_params, _url, socket) do
|
||||
{:noreply,
|
||||
socket
|
||||
|> assign(:allow_delete, Users.count_by_role(:admin) > 1)
|
||||
|> assign(:api_tokens, ApiTokens.list_api_tokens(socket.assigns.current_user.id))}
|
||||
{:ok, api_tokens} = ApiTokens.list_api_tokens_by_user_id(socket.assigns.current_user.id)
|
||||
|
||||
socket =
|
||||
socket
|
||||
|> assign(:allow_delete, Users.count_by_role(:admin) > 1)
|
||||
|> assign(:api_tokens, api_tokens)
|
||||
|
||||
{:noreply, socket}
|
||||
end
|
||||
|
||||
@impl Phoenix.LiveView
|
||||
def handle_event("delete_api_token", %{"id" => id}, socket) do
|
||||
api_token = ApiTokens.get_api_token!(id)
|
||||
case ApiTokens.delete_api_token_by_id(id, socket.assigns.current_user) do
|
||||
{:ok, _api_token} ->
|
||||
{:ok, api_tokens} = ApiTokens.list_api_tokens_by_user_id(socket.assigns.current_user.id)
|
||||
{:noreply, assign(socket, :api_tokens, api_tokens)}
|
||||
|
||||
if api_token.user_id == socket.assigns.current_user.id do
|
||||
{:ok, _deleted} = ApiTokens.delete_api_token(api_token)
|
||||
{:error, :not_found} ->
|
||||
{:noreply, socket}
|
||||
end
|
||||
|
||||
{:noreply,
|
||||
socket
|
||||
|> assign(:api_tokens, ApiTokens.list_api_tokens(socket.assigns.current_user.id))}
|
||||
end
|
||||
|
||||
@impl Phoenix.LiveView
|
||||
|
||||
@@ -149,7 +149,9 @@ defmodule FzHttpWeb.SettingLive.OIDCFormComponent do
|
||||
|
||||
<div class="level">
|
||||
<div class="level-left">
|
||||
<p class="help">Automatically create users when signing in for the first time.</p>
|
||||
<p class="help">
|
||||
Automatically provision users when signing in for the first time.
|
||||
</p>
|
||||
<p class="help is-danger">
|
||||
<%= error_tag(f, :auto_create_users) %>
|
||||
</p>
|
||||
|
||||
@@ -171,7 +171,9 @@ defmodule FzHttpWeb.SettingLive.SAMLFormComponent do
|
||||
|
||||
<div class="level">
|
||||
<div class="level-left">
|
||||
<p class="help">Automatically create users when signing in for the first time.</p>
|
||||
<p class="help">
|
||||
Automatically provision users when signing in for the first time.
|
||||
</p>
|
||||
<p class="help is-danger">
|
||||
<%= error_tag(f, :auto_create_users) %>
|
||||
</p>
|
||||
|
||||
@@ -6,6 +6,7 @@ defmodule FzHttpWeb.UserFromAuth do
|
||||
alias FzHttp.Users
|
||||
alias FzHttpWeb.Auth.HTML.Authentication
|
||||
|
||||
# Local auth
|
||||
def find_or_create(
|
||||
%Ueberauth.Auth{
|
||||
provider: :identity,
|
||||
|
||||
@@ -1,50 +1,142 @@
|
||||
defmodule FzHttp.ApiTokensTest do
|
||||
use FzHttp.DataCase
|
||||
|
||||
alias FzHttp.ApiTokensFixtures
|
||||
alias FzHttp.UsersFixtures
|
||||
alias FzHttp.ApiTokens
|
||||
alias FzHttp.ApiTokens.ApiToken
|
||||
|
||||
describe "api_tokens" do
|
||||
alias FzHttp.ApiTokens.ApiToken
|
||||
|
||||
import FzHttp.ApiTokensFixtures
|
||||
import FzHttp.UsersFixtures
|
||||
|
||||
@invalid_params %{"expires_in" => 0}
|
||||
|
||||
test "list_api_tokens/0 returns all api_tokens" do
|
||||
api_token = api_token()
|
||||
assert ApiTokens.list_api_tokens() == [api_token]
|
||||
describe "count_by_user_id/1" do
|
||||
test "returns 0 when no user exist" do
|
||||
assert ApiTokens.count_by_user_id(Ecto.UUID.generate()) == 0
|
||||
end
|
||||
|
||||
test "list_api_tokens/1 returns api_tokens scoped to a user" do
|
||||
api_token1 = api_token()
|
||||
api_token2 = api_token()
|
||||
assert [api_token1] == ApiTokens.list_api_tokens(api_token1.user_id)
|
||||
assert [api_token2] == ApiTokens.list_api_tokens(api_token2.user_id)
|
||||
test "returns the number of api_tokens for a user" do
|
||||
user = UsersFixtures.create_user()
|
||||
assert ApiTokens.count_by_user_id(user.id) == 0
|
||||
|
||||
ApiTokensFixtures.create_api_token(user: user)
|
||||
assert ApiTokens.count_by_user_id(user.id) == 1
|
||||
|
||||
ApiTokensFixtures.create_api_token(user: user)
|
||||
assert ApiTokens.count_by_user_id(user.id) == 2
|
||||
end
|
||||
end
|
||||
|
||||
describe "list_api_tokens/0" do
|
||||
test "returns empty list when no api tokens" do
|
||||
assert ApiTokens.list_api_tokens() == {:ok, []}
|
||||
end
|
||||
|
||||
test "get_api_token!/1 returns the api_token with given id" do
|
||||
api_token = api_token()
|
||||
assert ApiTokens.get_api_token!(api_token.id) == api_token
|
||||
test "returns all api_tokens" do
|
||||
assert ApiTokens.list_api_tokens() == {:ok, []}
|
||||
|
||||
api_token = ApiTokensFixtures.create_api_token()
|
||||
assert ApiTokens.list_api_tokens() == {:ok, [api_token]}
|
||||
end
|
||||
end
|
||||
|
||||
describe "list_api_tokens_by_user_id/1" do
|
||||
test "returns api tokens scoped to a user" do
|
||||
api_token1 = ApiTokensFixtures.create_api_token()
|
||||
api_token2 = ApiTokensFixtures.create_api_token()
|
||||
|
||||
assert ApiTokens.list_api_tokens_by_user_id(api_token1.user_id) == {:ok, [api_token1]}
|
||||
assert ApiTokens.list_api_tokens_by_user_id(api_token2.user_id) == {:ok, [api_token2]}
|
||||
end
|
||||
end
|
||||
|
||||
describe "fetch_api_token_by_id/1" do
|
||||
test "returns error when UUID is invalid" do
|
||||
assert ApiTokens.fetch_api_token_by_id("foo") == {:error, :not_found}
|
||||
end
|
||||
|
||||
test "create_user_api_token/2 with valid data creates a api_token" do
|
||||
test "returns api token by id" do
|
||||
api_token = ApiTokensFixtures.create_api_token()
|
||||
assert ApiTokens.fetch_api_token_by_id(api_token.id) == {:ok, api_token}
|
||||
end
|
||||
end
|
||||
|
||||
describe "fetch_unexpired_api_token_by_id/1" do
|
||||
test "fetches the unexpired token" do
|
||||
api_token = ApiTokensFixtures.create_api_token()
|
||||
assert ApiTokens.fetch_unexpired_api_token_by_id(api_token.id) == {:ok, api_token}
|
||||
end
|
||||
|
||||
test "returns error for expired token" do
|
||||
api_token =
|
||||
ApiTokensFixtures.create_api_token(%{"expires_in" => 1})
|
||||
|> ApiTokensFixtures.expire_api_token()
|
||||
|
||||
assert ApiTokens.fetch_unexpired_api_token_by_id(api_token.id) == {:error, :not_found}
|
||||
end
|
||||
end
|
||||
|
||||
describe "create_user_api_token/2" do
|
||||
test "creates an api_token" do
|
||||
user = UsersFixtures.create_user()
|
||||
|
||||
valid_params = %{
|
||||
"expires_in" => 1
|
||||
}
|
||||
|
||||
assert {:ok, %ApiToken{} = api_token} =
|
||||
ApiTokens.create_user_api_token(user(), valid_params)
|
||||
assert {:ok, %ApiToken{} = api_token} = ApiTokens.create_user_api_token(user, valid_params)
|
||||
|
||||
# Within 10 seconds
|
||||
assert_in_delta DateTime.to_unix(api_token.expires_at),
|
||||
DateTime.to_unix(DateTime.add(DateTime.utc_now(), 1, :day)),
|
||||
10
|
||||
|
||||
assert api_token.user_id == user.id
|
||||
assert api_token.expires_in == 1
|
||||
end
|
||||
|
||||
test "create_user_api_token/2 with invalid data returns error changeset" do
|
||||
assert {:error, %Ecto.Changeset{}} =
|
||||
ApiTokens.create_user_api_token(user(), @invalid_params)
|
||||
test "returns changeset error on invalid data" do
|
||||
user = UsersFixtures.create_user()
|
||||
|
||||
assert {:error, %Ecto.Changeset{} = changeset} =
|
||||
ApiTokens.create_user_api_token(user, %{"expires_in" => 0})
|
||||
|
||||
assert changeset.valid? == false
|
||||
assert errors_on(changeset) == %{expires_in: ["must be greater than or equal to 1"]}
|
||||
end
|
||||
end
|
||||
|
||||
describe "api_token_expired?/1" do
|
||||
test "returns true when expired" do
|
||||
api_token =
|
||||
ApiTokensFixtures.create_api_token(%{"expires_in" => 1})
|
||||
|> ApiTokensFixtures.expire_api_token()
|
||||
|
||||
assert ApiTokens.api_token_expired?(api_token) == true
|
||||
end
|
||||
|
||||
test "returns false when not expired" do
|
||||
api_token = ApiTokensFixtures.create_api_token(%{"expires_in" => 1})
|
||||
assert ApiTokens.api_token_expired?(api_token) == false
|
||||
end
|
||||
end
|
||||
|
||||
describe "delete_api_token_by_id/1" do
|
||||
test "deletes the api token" do
|
||||
user = UsersFixtures.create_user()
|
||||
api_token = ApiTokensFixtures.create_api_token(user: user)
|
||||
|
||||
assert {:ok, deleted_api_token} = ApiTokens.delete_api_token_by_id(api_token.id, user)
|
||||
|
||||
assert deleted_api_token.id == api_token.id
|
||||
refute Repo.one(ApiTokens.ApiToken)
|
||||
end
|
||||
|
||||
test "returns error when api token did not belong to user" do
|
||||
user = UsersFixtures.create_user()
|
||||
api_token = ApiTokensFixtures.create_api_token()
|
||||
|
||||
assert ApiTokens.delete_api_token_by_id(api_token.id, user) == {:error, :not_found}
|
||||
end
|
||||
|
||||
test "returns error when api token does not exist" do
|
||||
user = UsersFixtures.create_user()
|
||||
assert ApiTokens.delete_api_token_by_id(Ecto.UUID.generate(), user) == {:error, :not_found}
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
@@ -0,0 +1,33 @@
|
||||
defmodule FzHttpWeb.Auth.JSON.AuthenticationTest do
|
||||
use FzHttpWeb.ApiCase, async: true
|
||||
alias FzHttp.UsersFixtures
|
||||
import FzHttpWeb.ApiCase
|
||||
|
||||
test "renders error when api token is invalid" do
|
||||
conn =
|
||||
api_conn()
|
||||
|> Plug.Conn.put_req_header("authorization", "bearer invalid")
|
||||
|> FzHttpWeb.Auth.JSON.Pipeline.call([])
|
||||
|
||||
assert json_response(conn, 401) == %{"errors" => %{"auth" => "invalid_token"}}
|
||||
end
|
||||
|
||||
test "renders error when api token resource is invalid" do
|
||||
user = UsersFixtures.user(%{role: :admin})
|
||||
|
||||
claims = %{
|
||||
"api" => Ecto.UUID.generate(),
|
||||
"exp" => DateTime.to_unix(DateTime.utc_now() |> DateTime.add(1, :hour))
|
||||
}
|
||||
|
||||
{:ok, token, _claims} =
|
||||
Guardian.encode_and_sign(FzHttpWeb.Auth.JSON.Authentication, user, claims)
|
||||
|
||||
conn =
|
||||
api_conn()
|
||||
|> Plug.Conn.put_req_header("authorization", "bearer #{token}")
|
||||
|> FzHttpWeb.Auth.JSON.Pipeline.call([])
|
||||
|
||||
assert json_response(conn, 401) == %{"errors" => %{"auth" => "no_resource_found"}}
|
||||
end
|
||||
end
|
||||
@@ -51,7 +51,7 @@ defmodule FzHttpWeb.ApiCase do
|
||||
|
||||
def authed_conn do
|
||||
user = UsersFixtures.user(%{role: :admin})
|
||||
api_token = ApiTokensFixtures.api_token(%{"user_id" => user.id})
|
||||
api_token = ApiTokensFixtures.create_api_token(user: user)
|
||||
|
||||
{:ok, token, _claims} = FzHttpWeb.Auth.JSON.Authentication.fz_encode_and_sign(api_token, user)
|
||||
|
||||
|
||||
@@ -1,25 +1,21 @@
|
||||
defmodule FzHttp.ApiTokensFixtures do
|
||||
@moduledoc """
|
||||
This module defines test helpers for creating
|
||||
entities via the `FzHttp.ApiTokens` context.
|
||||
"""
|
||||
alias FzHttp.UsersFixtures
|
||||
|
||||
@doc """
|
||||
Generate a api_token.
|
||||
"""
|
||||
def api_token(params \\ %{}) do
|
||||
user_id =
|
||||
Map.get_lazy(
|
||||
params,
|
||||
"user_id",
|
||||
fn ->
|
||||
FzHttp.UsersFixtures.user().id
|
||||
end
|
||||
)
|
||||
|
||||
{:ok, api_token} =
|
||||
FzHttp.ApiTokens.create_user_api_token(%FzHttp.Users.User{id: user_id}, params)
|
||||
def api_token_attrs(attrs \\ %{}) do
|
||||
Enum.into(attrs, %{})
|
||||
end
|
||||
|
||||
def create_api_token(attrs \\ %{}) do
|
||||
attrs = api_token_attrs(attrs)
|
||||
{user, attrs} = Map.pop_lazy(attrs, :user, fn -> UsersFixtures.user() end)
|
||||
{:ok, api_token} = FzHttp.ApiTokens.create_user_api_token(user, attrs)
|
||||
api_token
|
||||
end
|
||||
|
||||
def expire_api_token(api_token) do
|
||||
one_second_ago = DateTime.utc_now() |> DateTime.add(-1, :second)
|
||||
|
||||
Ecto.Changeset.change(api_token, expires_at: one_second_ago)
|
||||
|> FzHttp.Repo.update!()
|
||||
end
|
||||
end
|
||||
|
||||
Reference in New Issue
Block a user