defmodule Guardian.DB.ETSAdapter do @moduledoc """ Implement the Guardian.DB.Adapter for ETS """ @behaviour Guardian.DB.Adapter @impl true def one(claims, opts) do jti = Map.get(claims, "jti") aud = Map.get(claims, "aud") match = opts |> Keyword.fetch!(:table) |> :ets.match({jti, aud, :_, :_, :"$1"}) case match do [[token]] -> token _ -> nil end end @impl true def insert(%{valid?: true} = changeset, opts) do table = Keyword.fetch!(opts, :table) token = Map.merge(changeset.data, changeset.changes) unless :ets.insert(table, {token.jti, token.aud, token.sub, token.exp, token}) do raise """ An error occurred trying to insert a new record into the ETS table #{table}. Please ensure you've created the table before attempting to insert records. """ end {:ok, token} end def insert(changeset, _opts) do {:error, changeset} end @impl true def delete(%{jti: jti} = token, opts) do table = Keyword.fetch!(opts, :table) unless :ets.delete(table, jti) do raise """ An error occurred trying to delete a record from the ETS table #{table}. Please ensure you've created the table before attempting to delete records. """ end {:ok, token} end @impl true def delete_by_sub(sub, opts) do table = Keyword.fetch!(opts, :table) table |> :ets.match({:"$1", :_, sub, :_, :"$2"}) |> delete_many(table) end @impl true def purge_expired_tokens(exp, opts) do table = Keyword.fetch!(opts, :table) matcher = [{{:"$1", :"$2", :"$3", :"$4", :"$5"}, [{:<, :"$4", exp}], [[:"$1", :"$5"]]}] table |> :ets.select(matcher) |> delete_many(table) end defp delete_many(tokens, table) do deleted_tokens = Enum.reduce(tokens, [], fn [jti, token], acc -> if :ets.delete(table, jti) do [token | acc] else acc end end) {length(deleted_tokens), deleted_tokens} end end