defmodule Rivet.Auth do
@moduledoc """
Public interface to authentication and authorization functions.
"""
import Rivet.Graphql
alias Rivet.Auth.Assertion
alias Rivet.Auth.Signin
alias Rivet.Ident
require Logger
def check_authz(%{context: %{user: %Ident.User{} = u}}, assertion),
do: check_authz(u, assertion)
def check_authz(%Ident.User{} = user, %Assertion{domain: :global} = assertion) do
Ident.User.Lib.check_authz(user, assertion)
end
def check_authz(%Ident.User{} = user, %Assertion{domain: domain} = assertion) do
with {:error, user} <- Ident.User.Lib.check_authz(user, assertion),
{:ok, %{parent_id: parent_id}} when is_binary(parent_id) <-
domain.one(id: assertion.ref_id) do
# Walk up the tree, but disable fallback. If it didn't work before, it
# won't work again.
check_authz(user, %{assertion | fallback: false})
else
{:ok, %Ident.User{}} = pass -> pass
_ -> {:error, user}
end
end
def check_authz(_, _), do: {:error, nil}
@spec change_password(Ident.UserCode.t(), String.t()) :: boolean()
def change_password(%Ident.UserCode{} = code, new), do: set_password(code.user, new)
@spec change_password(Ident.User.t(), String.t(), String.t()) :: boolean()
def change_password(%Ident.User{} = user, current, new) do
if Signin.Local.check_password(user, current) do
set_password(user, new)
else
Logger.warning("password change failure: current password mis-match", user_id: user.id)
false
end
end
@spec set_password(Ident.User.t(), String.t()) :: boolean()
defp set_password(%Ident.User{} = user, new) do
case Ident.Factor.Lib.set_password(user, new) do
{:ok, %Ident.Factor{}} ->
Rivet.Ident.cfg(:notify_password_changed).sendto(user)
true
error ->
IO.inspect(error, label: "password change error")
Logger.error("password change failure: cannot set factor", user_id: user.id)
false
end
end
def authz_action(user, assertion, method \\ nil, kwlog \\ []) do
with {:ok, user} <- check_authz(user, assertion) do
graphql_log(method, kwlog)
{:ok, user}
else
_ ->
graphql_error(method, :authz)
end
end
end