Current section

Files

Jump to
ash_authentication lib ash_authentication add_ons log_out_everywhere action.ex
Raw

lib/ash_authentication/add_ons/log_out_everywhere/action.ex

# SPDX-FileCopyrightText: 2022 Alembic Pty Ltd
#
# SPDX-License-Identifier: MIT
defmodule AshAuthentication.AddOn.LogOutEverywhere.Action do
@moduledoc """
Revokes all tokens for the specified user.
"""
use Ash.Resource.Actions.Implementation
alias Ash.Error.Framework.AssumptionFailed
alias AshAuthentication.Info
alias AshAuthentication.TokenResource.Info, as: TokenResourceInfo
require Ash.Query
@doc false
@impl true
def run(input, _opts, context) do
case Info.strategy_for_action(input.resource, input.action.name) do
{:ok, strategy} ->
really_run_action(input, strategy, context)
:error ->
raise AssumptionFailed,
message: "Action does not correlate with an authentication strategy"
end
end
defp really_run_action(input, strategy, context) do
user = Map.fetch!(input.arguments, strategy.argument_name)
subject = AshAuthentication.user_to_subject(user)
revoke_all_tokens_for_subject(subject, strategy, context)
end
defp revoke_all_tokens_for_subject(subject, strategy, context) do
with {:ok, token_resource} <- Info.authentication_tokens_token_resource(strategy.resource),
{:ok, revoke_all_tokens_action_name} <-
TokenResourceInfo.token_revocation_revoke_all_stored_for_subject_action_name(
token_resource
) do
token_resource
|> include_purposes(strategy)
|> exclude_purposes(strategy)
|> Ash.bulk_update(
revoke_all_tokens_action_name,
%{subject: subject},
Ash.Context.to_opts(context,
strategy: [:atomic, :atomic_batches, :stream],
context: %{private: %{ash_authentication?: true}},
return_errors?: true,
stop_on_error?: true
)
)
|> case do
%{status: :success} ->
:ok
%{errors: errors} ->
{:error, errors}
end
end
end
defp include_purposes(query, strategy) do
if strategy.include_purposes do
Ash.Query.filter(query, purpose in ^strategy.include_purposes)
else
query
end
end
defp exclude_purposes(query, strategy) do
if strategy.exclude_purposes do
Ash.Query.filter(query, purpose not in ^strategy.exclude_purposes)
else
query
end
end
end