Current section
Files
Jump to
Current section
Files
lib/charon_oauth2/internal/gen_mod/authorizations.ex
defmodule CharonOauth2.Internal.GenMod.Authorizations do
@moduledoc false
def generate(schemas_and_contexts, repo) do
quote location: :keep,
generated: true,
bind_quoted: [schemas_and_contexts: schemas_and_contexts, repo: repo] do
@moduledoc """
Context to manage authorizations
"""
require Logger
import Ecto.Query, only: [where: 3, limit: 2, offset: 2, order_by: 2]
alias Ecto.Changeset
alias CharonOauth2.Internal
import Internal
@authorization_schema schemas_and_contexts.authorization
@repo repo
@doc """
Get a single authorization by one or more clauses, optionally with preloads.
Returns nil if Authorization cannot be found.
## Doctests
iex> authorization = insert_test_authorization!(resource_owner_id: insert_test_user().id)
iex> %Authorization{} = Authorizations.get_by(id: authorization.id)
iex> nil = Authorizations.get_by(id: authorization.id + 1)
# preloads things
iex> authorization = insert_test_authorization!(resource_owner_id: insert_test_user().id)
iex> %{resource_owner: %{id: _}, client: %{id: _}} = Authorizations.get_by([id: authorization.id], Authorization.supported_preloads)
"""
@spec get_by(keyword | map, [@authorization_schema.resolvable]) ::
@authorization_schema.t() | nil
def get_by(clauses, preloads \\ []) do
preloads |> @authorization_schema.preload() |> @repo.get_by(clauses)
end
@doc """
Get a list of all oauth2 authorizations.
## Doctests
iex> insert_test_authorization!(resource_owner_id: insert_test_user().id)
iex> [%Authorization{}] = Authorizations.all()
# can be filtered
iex> authorization = insert_test_authorization!(resource_owner_id: insert_test_user().id)
iex> [%Authorization{}] = Authorizations.all(%{resource_owner_id: authorization.resource_owner_id})
iex> [%Authorization{}] = Authorizations.all(%{scope: authorization.scope |> List.first()})
iex> [] = Authorizations.all(%{resource_owner_id: authorization.resource_owner_id + 1})
"""
@spec all(%{required(atom) => any}, [@authorization_schema.resolvable]) ::
[@authorization_schema.t()]
def all(filters \\ %{}, preloads \\ []) do
base_query = @authorization_schema.preload(preloads)
filters
|> Enum.reduce(base_query, fn
{:id, v}, q -> where(q, [a], a.id == ^v)
{:client_id, v}, q -> where(q, [a], a.client_id == ^v)
{:resource_owner_id, v}, q -> where(q, [a], a.resource_owner_id == ^v)
{:scope, v}, q -> where(q, [a], set_contains_any(a.scope, [v]))
{:limit, v}, q -> limit(q, ^v)
{:offset, v}, q -> offset(q, ^v)
{:order_by, v}, q -> order_by(q, ^v)
{k, _v}, _ -> raise "can't filter authorization query by #{k}"
end)
|> @repo.all()
end
@doc """
Insert a new authorization
## Examples / doctests
# succesfully creates an authorization
iex> {:ok, _} = insert_test_authorization(resource_owner_id: insert_test_user().id)
# a user can authorize a client only once
iex> {:ok, authorization} = insert_test_authorization(resource_owner_id: insert_test_user().id)
iex> insert_test_authorization(resource_owner_id: authorization.resource_owner_id, client_id: authorization.client_id) |> errors_on()
%{client_id: ["user already authorized this client"]}
# owner and client must exist
iex> insert_test_authorization(resource_owner_id: -1, client_id: insert_test_client!(owner_id: insert_test_user().id).id) |> errors_on()
%{resource_owner: ["does not exist"]}
iex> insert_test_authorization(resource_owner_id: insert_test_user().id, client_id: Ecto.UUID.generate()) |> errors_on()
%{client: ["does not exist"]}
iex> Authorizations.insert(%{}) |> errors_on()
%{scope: ["can't be blank"], client_id: ["can't be blank"], resource_owner_id: ["can't be blank"]}
"""
@spec insert(map) :: {:ok, @authorization_schema.t()} | {:error, Changeset.t()}
def insert(params) do
params
|> @authorization_schema.insert_only_changeset()
|> @authorization_schema.changeset(params)
|> @repo.insert()
end
@doc """
Update an authorization.
# TODO: fix ~(write write) test by implementing overrides
## Examples / doctests
# scopes must be subset of configured scopes and of client scopes
iex> insert_test_authorization!(resource_owner_id: insert_test_user().id) |> Authorizations.update(%{scope: ~w(cry)}) |> errors_on()
%{scope: ["must be subset of party, read, write"]}
iex> insert_test_authorization!(resource_owner_id: insert_test_user().id) |> Authorizations.update(%{scope: ~w(write write)}) |> errors_on()
%{scope: ["client not allowed to access scope(s): write"]}
# client and resource owner can't be updated
iex> %{id: id, client_id: client_id, resource_owner_id: owner_id} = insert_test_authorization!(resource_owner_id: insert_test_user().id)
iex> {:ok, %{client_id: ^client_id, resource_owner_id: ^owner_id}} = Authorizations.update([id: id], %{client_id: -1, owner_id: -1})
"""
@spec update(@authorization_schema.t() | keyword() | map(), map) ::
{:ok, @authorization_schema.t()} | {:error, Changeset.t()} | {:error, :not_found}
def update(authorization = %@authorization_schema{}, params) do
authorization
|> @authorization_schema.changeset(params)
|> @repo.update()
end
def update(clauses, params) do
get_and_do(fn -> get_by(clauses) end, &update(&1, params), @repo)
end
@doc """
Delete an authorization.
## Examples / doctests
# authorization must exist
iex> {:error, :not_found} = Authorizations.delete(id: -1)
# succesfully deletes an authorization
iex> authorization = insert_test_authorization!(resource_owner_id: insert_test_user().id)
iex> {:ok, _} = Authorizations.delete([id: authorization.id])
iex> {:error, :not_found} = Authorizations.delete([id: authorization.id])
"""
@spec delete(@authorization_schema.t() | keyword | map) ::
{:ok, @authorization_schema.t()} | {:error, :not_found}
def delete(authorization = %@authorization_schema{}), do: @repo.delete(authorization)
def delete(clauses) do
get_and_do(fn -> get_by(clauses) end, &delete/1, @repo)
end
end
end
end