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

aalberti333
As the title describes, I’m trying to run Enum.map() over a list of key/value pairs, where the value is a map. My data looks like this: ...
New
sergio_101
I am VERY much an elixir newbie. I have taken one elixir course and one phoenix course on Udemy. During that course, I saw the instructor...
New
Werner
Hi, I’m using Ubuntu 18.04 and after updating to OTP-24.0 yesterday i have this warning when I run “mix local.hex”: 14:57:30.512 [warn] ...
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
gonzofish
I’m currently trying to understand how to join three tables using Ecto. All the examples I’ve seen use 2, so maybe I’m just missing somet...
New
electic
Hi, I am new to Elixir. I am trying to use the DateTime component to insert a date into MySQL however the there seems to be no way to fo...
New
Exadra37
Sometimes I want to check if the input into a function is not a blank string. My first approach: defmodule Example do def do_stuff(s...
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
wernerlaude
In AR this is so simple @articles = current_user.articles How to do in Ecto? def index(conn, _params) do current_user = conn.assig...
New
idi527
I’ve been re-reading swift book again and noticed that multiline strings there don’t have a trailing line break, unlike in elixir iex(2)...
New

Other popular topics Top

senggen
Erlang/OTP 25 [erts-13.2.2] [source] [64-bit] [smp:8:8] [ds:8:8:10] [async-threads:1] 15:22:35.803 [error] gen_event {lager_file_backend...
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
ycv005
I have followed this StackOverflow post to install the specific version of Erlang. And When I am running mix ecto.setup then getting fol...
New
KronicDeth
Elixir plugin for JetBrain’s IntelliJ Platform (including Rubymine) This is a plugin that adds support for Elixir to JetBrains IntelliJ...
289 35421 110
New
quazar
How to set Jason to encode all fields in ecto schema, I don’t care about security and implementing only is taking long list of attributes...
New
SoCreat
i’m a new one to elixir which editor can i use vs code? or atom? Thanks! :smiley:
New
stefanchrobot
What’s the safe way to decode a JSON string into a struct? I want to avoid calling String.to_atom. Jason.decode can give me a map with st...
New
Qqwy
Original source of discussion: This topic on the Pragmatic Programmers' Functional Web Development with Elixir, OTP, and Phoenix forum. ...
New
AstonJ
by Lance Halvorsen Elixir and Phoenix are generating tremendous excitement as an unbeatable platform for building modern web application...
460 27162 124
New
siddhant3030
Hi, I have to write a raw query for one of my project. But till now I have used ecto queries and don’t have much experience writing raw ...
New

We're in Beta

About us Mission Statement