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

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
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
jononomo
I am trying to figure out how Mix knows whether the environment is test, dev, or prod – where is this set? Thanks.
New
minhajuddin
I have seen a lot of code which picks the first element from a list using Enum.at(0) instead of List.first. Is there a reason why people ...
New
Fl4m3Ph03n1x
About me? ( if you have nothing better to do than reading about some random guy in the internet :stuck_out_tongue: ) Hello all, this is ...
New
itssasanka
Hi all, Trying to get some more clarity over utc_datetime and naive_datetime for Ecto: The documentation above suggests that while ...
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
fayddelight
I tried installing elixir 1.11.2 erlang 23.3.4 via asdf in my zsh shell. Enabled the versions locally and globally. When I list them ...
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
komlanvi
Hi everyone, I was playing with phoenix liveView but I run into an issue. I have a form and want to validate each input text when the te...
New

Other popular topics Top

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
minhajuddin
I have seen a lot of code which picks the first element from a list using Enum.at(0) instead of List.first. Is there a reason why people ...
New
chrismccord
This release brings a number of exciting features, including integration with the new Phoenix LiveDashboard and Phoenix LiveView. There h...
New
AngeloChecked
What learn first? Rust or Elixir Hi Elixir community! I’m here because i want learn a new language. I’m a junior developer and mainly i ...
New
gausby
I asked this very same question on twitter and got some interesting feedback, but I thought it would be a good question to ask here as we...
1207 39523 209
New
Qqwy
Original source of discussion: This topic on the Pragmatic Programmers’ Functional Web Development with Elixir, OTP, and Phoenix forum. ...
New
RisingFromAshes
I’ve read in another post that it may be possible with a router helper - but I couldn’t find an appropriate one, and tbh, I’m still just ...
New
bsollish-terakeet
Credo is smart enough to check for (something like) this: assert length(the_list) == 0 with this response: Checking if an enum is empt...
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
sergio
Kind of like when jquery came out, it was super necessary. Existing drag and drop libraries have a bunch of baggage to support old browse...
New

We're in Beta

About us Mission Statement