saverio-kantox

saverio-kantox

Ecto/Postgres/Phoenix set session variable on checkout connection

I need to set up a “audit” table in the database, where changes to records trigger an insert on some other table. Writing the trigger is not an issue.

Ideally, I would try to store also some sort of “current user” on the audit, which is known for instance in a specific plug. Here is the issue: I see no way to tell Ecto that all connections that are checked out from the pool should have an “initial” command sent like set session 'myapp.current_user' = 'abcdef12-1234-1234-12345678abcdcdef';.

The problem is threefold:

  1. How to hook into connection checkout
  2. How to ensure that the connection is not held for too much time
  3. How to pass it down for instance to a liveview (maybe it’s just a restatement of 1+2, because the liveview is long-lived, and the connection should not be held)

Does anyone have any suggestion on how to approach the problem?

Than you beforehand!!

Marked As Solved

idi527

idi527

Or add audited_insert to the default repo:

defmodule MyApp.Repo do
  # ... use Ecto.Repo, blah blah
  
  defp audited(op, %User{} = user, args) do
    transaction(fn ->
      query("set session 'myapp.current_user' = '#{user.id}';")
      apply(__MODULE__, op, args)
    end)
  end

  def audited_insert(changeset, user, opts \\ []) do
    audited(:insert, user, [changeset, opts])
  end
  
  # ... no need for defedelegates
end

Also Liked

halostatue

halostatue

The hex.pm source has a really good audit logging implementation that is worth looking at. I’ve been running a (heavily modified) variant of it in production for the last couple of years and it hasn’t failed me yet. It has a Task-based audit function that can be used for writing to the audit log on reads. I’m sure it wouldn’t be hard to write your error handling to use the audit function if, say, a multi operation failed.

wolfiton

wolfiton

If you need videos my search was successful and found a lot but that specific one eludes me as well.

In any case if you want eleixir conf videos this is what I found https://www.youtube.com/results?search_query=elixir+conf
Maybe this might help:

danschultzer

danschultzer

Pow Core Team

I’m not sure if this is a good way of doing it, but you could create a custom repo module, and require the user id to be passed along:

defmodule MyApp.AuditedRepo do
  alias MyApp.Repo

  @spec insert(Ecto.Changeset.t(), binary() | nil, keyword()) :: {:ok, map()} | {:error, Ecto.Changeset.t()}
  def insert(changeset, user_id, opts \\ []) do
    Repo.transaction(fn ->
      # set session here
      Repo.insert(changeset, opts)
    end)
  end

  # all other methods, e.g. using defdelegate for the actions that doesn't need user id
end

So in the context methods you’ll have to pass along the user id:

defmodule MyApp.Posts do
  alias MyApp.{AuditedRepo, Posts.Post}

  def create_post(user_id, attrs) do
    %Post{}
    |> Post.changeset(attrs)
    |> AuditedRepo.insert(user_id)
  end

  # ...
end

Where Next?

Popular in Questions Top

chrisalley
ExUnit now has describe blocks which is a welcome addition coming from RSpec. In the docs, it states that nested hierarchies of describe ...
New
gshaw
What is the idiomatic way of matching for not nil in Elixir? E.g., First way: defp halt_if_not_signed_in(conn, signed_in_account) when...
New
fireproofsocks
Forgive me if this is obvious, but how does one delete a database record WITHOUT selecting it first? Ecto.Repo — Ecto v3.14.0 has exampl...
New
jay1
Why is it that the mnesia database isn’t the most preferred database for use in Elixir/Phoenix?
New
freewebwithme
Using vs code and installed ElixirLS: support and debugger. And I got an error popped up on start up says Failed to run ‘elixir’ comma...
New
JDanielMartinez
Hi! May someone helps me, please! I have two apps into an umbrella project: the first one is Database, which manages queries, and the se...
New
jason.o
In the code below, if the create action is not set to accept “extra_key” as an input, it errors out with a message shown above. Is there ...
New
romenigld
I am trying to run a deploy with docker and I successfully runned with this command: docker build -t romenigld/blog-prod . but when I t...
New
hariharasudhan94
I would like to know what is the best IDE for elixir development?
New
vonH
In asking this question I am more interested about the expressiveness of the language itself and less concerned about the availability of...
New

Other popular topics Top

9mm
I am constructing a JSON object (map) and I need to conditionally set a field. I’m trying to write proper elixir-way code… and I’m at a l...
New
AstonJ
Posting this to see if we can make things easier for people to get into Neovim. If you use Neovim and have a favourite distro please let ...
New
Nvim
Anybody knows a comprehensive comparison of Django and Phoenix, thanks for the help. Where are they similar? Where do they differ the m...
New
jay1
Why is it that the mnesia database isn’t the most preferred database for use in Elixir/Phoenix?
New
vrod
I am using the Starship cross-shell prompt – it seems pretty nice, but I get some errors: [WARN] - (starship::utils): Executing command ...
New
romenigld
I am trying to run a deploy with docker and I successfully runned with this command: docker build -t romenigld/blog-prod . but when I t...
New
nsuchy
Hi. I’ve noticed that Windows Powershell has it’s own IEX command and you cannot access Elixir’s IEX due to the conflict. This isn’t a cr...
New
hariharasudhan94
Lets say I have map like this fetching from my database %{"_id" => #BSON.ObjectId<58eb1a7a9ad169198c3dXXXX>, "email" => ...
New
svb
Hi! Currently I want to submit a form by pressing the Enter key. However, since my input field is of type “textarea” this is just adds a...
New
vonH
In asking this question I am more interested about the expressiveness of the language itself and less concerned about the availability of...
New

Latest on Elixir Forum

Elixir Forum

We're in Beta

About us Mission Statement