Skip to content

Commit

Permalink
Add TransportWeb.Session
Browse files Browse the repository at this point in the history
  • Loading branch information
AntoineAugusti committed Jan 8, 2024
1 parent 4d8995f commit 75bf77c
Show file tree
Hide file tree
Showing 7 changed files with 124 additions and 49 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -188,16 +188,10 @@ defmodule TransportWeb.PageController do

conn
|> assign(:datasets, datasets)
|> refresh_is_producer(datasets)
|> TransportWeb.Session.set_is_producer(datasets)
|> render("espace_producteur.html")
end

defp refresh_is_producer(%Plug.Conn{} = conn, datasets) do
is_producer = not Enum.empty?(datasets)
current_user = get_session(conn, :current_user, %{})
conn |> put_session(:current_user, Map.put(current_user, "is_producer", is_producer))
end

defp aoms_with_dataset do
aoms_legal_owners =
Dataset.base_query()
Expand Down
42 changes: 6 additions & 36 deletions apps/transport/lib/transport_web/controllers/session_controller.ex
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,6 @@ defmodule TransportWeb.SessionController do
"""
use TransportWeb, :controller
alias Datagouvfr.Authentication
import Ecto.Query
require Logger

def new(conn, _) do
Expand Down Expand Up @@ -128,46 +127,17 @@ defmodule TransportWeb.SessionController do
end

def save_current_user(%Plug.Conn{} = conn, %{} = user_params) do
conn |> put_session(:current_user, user_params_for_session(user_params))
conn
|> put_session(:current_user, user_params_for_session(user_params))
|> TransportWeb.Session.set_is_producer(user_params)
|> TransportWeb.Session.set_is_admin(user_params)
end

def user_params_for_session(%{} = params) do
params
defp user_params_for_session(%{} = params) do
# Remove the list of `organizations` from the final map: it's already stored in the database
# and maintained up-to-date by `Transport.Jobs.UpdateContactsJob`
# and it can be too big to be stored in a cookie
|> Map.delete("organizations")
# - `is_admin` is needed to check permissions
# - `is_producer` is used to get access to the "Espace producteur"
# `is_producer` is also refreshed when they visit their "Espace producteur"
|> Map.merge(%{"is_producer" => is_producer?(params), "is_admin" => is_admin?(params)})
end

@doc """
Are you a data producer?
You're a data producer if you're a member of an organization with an active dataset
on transport.data.gouv.fr.
This is set when you log in and refreshed when you visit your "Espace producteur".
"""
def is_producer?(%{"organizations" => orgs}) do
org_ids = Enum.map(orgs, & &1["id"])

DB.Dataset.base_query() |> where([dataset: d], d.organization_id in ^org_ids) |> DB.Repo.exists?()
end

@doc """
Are you a transport.data.gouv.fr admin?
You're an admin if you're a member of the PAN organization on data.gouv.fr.
iex> is_admin?(%{"organizations" => [%{"slug" => "equipe-transport-data-gouv-fr"}, %{"slug" => "foo"}]})
true
iex> is_admin?(%{"organizations" => [%{"slug" => "foo"}]})
false
iex> is_admin?(%{"organizations" => []})
false
"""
def is_admin?(%{"organizations" => orgs}) do
Enum.any?(orgs, &(&1["slug"] == "equipe-transport-data-gouv-fr"))
Map.delete(params, "organizations")
end

defp get_redirect_path(%Plug.Conn{} = conn) do
Expand Down
4 changes: 2 additions & 2 deletions apps/transport/lib/transport_web/router.ex
Original file line number Diff line number Diff line change
Expand Up @@ -354,8 +354,8 @@ defmodule TransportWeb.Router do
end

# NOTE: method visibility set to public because we need to call the same logic from LiveView
# `current_user` is set by TransportWeb.SessionController.user_params_for_session/1
def is_transport_data_gouv_member?(%{"is_admin" => true} = _current_user), do: true
# `current_user` is set by TransportWeb.SessionController
def is_transport_data_gouv_member?(%{} = current_user), do: TransportWeb.Session.is_admin?(current_user)
def is_transport_data_gouv_member?(_), do: false

# Check that a secret key is passed in the URL in the `export_key` query parameter
Expand Down
69 changes: 69 additions & 0 deletions apps/transport/lib/transport_web/session.ex
Original file line number Diff line number Diff line change
@@ -0,0 +1,69 @@
defmodule TransportWeb.Session do
@moduledoc """
Web session getters and setters.
"""
import Ecto.Query
import Plug.Conn

@is_admin_key_name "is_admin"
@is_producer_key_name "is_producer"

@doc """
Are you a data producer?
You're a data producer if you're a member of an organization with an active dataset
on transport.data.gouv.fr.
This is set when you log in and refreshed when you visit your "Espace producteur".
"""
@spec set_is_producer(Plug.Conn.t(), map() | [DB.Dataset.t()]) :: Plug.Conn.t()
def set_is_producer(%Plug.Conn{} = conn, %{"organizations" => _} = params) do
set_session_attribute_attribute(conn, @is_producer_key_name, is_producer?(params))
end

def set_is_producer(%Plug.Conn{} = conn, [%DB.Dataset{}] = _datasets_for_user) do
set_session_attribute_attribute(conn, @is_producer_key_name, true)
end

def set_is_producer(%Plug.Conn{} = conn, [] = _datasets_for_user) do
set_session_attribute_attribute(conn, @is_producer_key_name, false)
end

@doc """
Are you a transport.data.gouv.fr admin?
You're an admin if you're a member of the PAN organization on data.gouv.fr.
"""
def set_is_admin(%Plug.Conn{} = conn, %{"organizations" => _} = params) do
set_session_attribute_attribute(conn, @is_admin_key_name, is_admin?(params))
end

def is_admin?(%{"organizations" => orgs}) do
Enum.any?(orgs, &(&1["slug"] == "equipe-transport-data-gouv-fr"))
end

def is_admin?(%Plug.Conn{} = conn) do
conn |> current_user() |> Map.get(@is_admin_key_name, false)
end

def is_admin?(%{"current_user" => current_user}) do
Map.get(current_user, @is_admin_key_name, false)
end

def is_admin?(%{}), do: false

def is_producer?(%Plug.Conn{} = conn) do
conn |> current_user() |> Map.get(@is_producer_key_name, false)
end

def is_producer?(%{"organizations" => orgs}) do
org_ids = Enum.map(orgs, & &1["id"])
DB.Dataset.base_query() |> where([dataset: d], d.organization_id in ^org_ids) |> DB.Repo.exists?()
end

@spec set_session_attribute_attribute(Plug.Conn.t(), binary(), boolean()) :: Plug.Conn.t()
defp set_session_attribute_attribute(%Plug.Conn{} = conn, key, value) do
current_user = current_user(conn)
conn |> put_session(:current_user, Map.put(current_user, key, value))
end

defp current_user(%Plug.Conn{} = conn), do: get_session(conn, :current_user, %{})
end
Original file line number Diff line number Diff line change
Expand Up @@ -84,7 +84,7 @@
<%= if is_transport_data_gouv_member?(assigns[:current_user]) do %>
<%= link("Administration", to: "/backoffice") %>
<% end %>
<%= if Map.get(assigns[:current_user], "is_producer", false) do %>
<%= if TransportWeb.Session.is_producer?(@conn) do %>
<%= link(gettext("Producer space"),
to: page_path(@conn, :espace_producteur, utm_source: "menu_dropdown")
) %>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -90,11 +90,11 @@ defmodule TransportWeb.SessionControllerTest do
test "save_current_user", %{conn: conn} do
pan_org = %{"slug" => "equipe-transport-data-gouv-fr", "name" => "PAN", "id" => org_id = Ecto.UUID.generate()}

assert is_admin?(%{"organizations" => [pan_org]})
refute is_producer?(%{"organizations" => [pan_org]})
assert TransportWeb.Session.is_admin?(%{"organizations" => [pan_org]})
refute TransportWeb.Session.is_producer?(%{"organizations" => [pan_org]})
insert(:dataset, organization_id: org_id)
# You're a producer if you're a member of an org with an active dataset
assert is_producer?(%{"organizations" => [pan_org]})
assert TransportWeb.Session.is_producer?(%{"organizations" => [pan_org]})

user_params = %{"foo" => "bar", "organizations" => [pan_org]}

Expand Down
42 changes: 42 additions & 0 deletions apps/transport/test/transport_web/session_text.exs
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
defmodule TransportWeb.SessionTest do
use ExUnit.Case, async: true
import DB.Factory
import TransportWeb.Session
doctest TransportWeb.Session, import: true

@pan_org_id Ecto.UUID.generate()

setup do
:ok = Ecto.Adapters.SQL.Sandbox.checkout(DB.Repo)
end

test "is_producer?" do
refute is_producer?(%{"organizations" => [pan_org()]})
insert(:dataset, organization_id: @pan_org_id)
# You're a producer if you're a member of an org with an active dataset
assert is_producer?(%{"organizations" => [pan_org()]})
end

test "is_admin?" do
refute is_admin?(%{"organizations" => []})
refute is_admin?(%{"organizations" => [%{"slug" => "foo"}]})
assert is_admin?(%{"organizations" => [pan_org()]})
end

describe "reader" do
test "is_admin?" do
refute is_admin?(Plug.Test.init_test_session(%Plug.Conn{}, %{}))
assert is_admin?(Plug.Test.init_test_session(%Plug.Conn{}, %{"current_user" => %{"is_admin" => true}}))
assert is_admin?(%{"current_user" => %{"is_admin" => true}})
end

test "is_producer?" do
assert is_producer?(Plug.Test.init_test_session(%Plug.Conn{}, %{"current_user" => %{"is_producer" => true}}))
refute is_producer?(Plug.Test.init_test_session(%Plug.Conn{}, %{}))
end
end

def pan_org do
%{"slug" => "equipe-transport-data-gouv-fr", "name" => "PAN", "id" => @pan_org_id}
end
end

0 comments on commit 75bf77c

Please sign in to comment.