2021-05-04 02:03:19 +08:00
|
|
|
defmodule LivebookWeb.UserPlug do
|
|
|
|
# Initializes the session and cookies with user-related info.
|
|
|
|
#
|
|
|
|
# The first time someone visits Livebook
|
2023-06-30 05:10:00 +08:00
|
|
|
# this plug stores a new random user id or the ZTA user
|
|
|
|
# in the session under `:identity_data`.
|
2021-05-04 02:03:19 +08:00
|
|
|
#
|
|
|
|
# Additionally the cookies are checked for the presence
|
|
|
|
# of `"user_data"` and if there is none, a new user
|
|
|
|
# attributes are stored there. This makes sure
|
|
|
|
# the client-side can always access some `"user_data"`
|
|
|
|
# for `connect_params` of the socket connection.
|
|
|
|
|
|
|
|
@behaviour Plug
|
|
|
|
|
|
|
|
import Plug.Conn
|
2023-06-21 06:25:25 +08:00
|
|
|
import Phoenix.Controller
|
2021-05-04 02:03:19 +08:00
|
|
|
|
|
|
|
@impl true
|
|
|
|
def init(opts), do: opts
|
|
|
|
|
|
|
|
@impl true
|
|
|
|
def call(conn, _opts) do
|
|
|
|
conn
|
2023-06-21 06:25:25 +08:00
|
|
|
|> ensure_user_identity()
|
2021-05-04 02:03:19 +08:00
|
|
|
|> ensure_user_data()
|
2021-06-16 01:33:16 +08:00
|
|
|
|> mirror_user_data_in_session()
|
2021-05-04 02:03:19 +08:00
|
|
|
end
|
|
|
|
|
2023-06-21 06:25:25 +08:00
|
|
|
defp ensure_user_identity(conn) do
|
2023-10-25 15:44:09 +08:00
|
|
|
{_type, module, _key} = Livebook.Config.identity_provider()
|
|
|
|
{conn, identity_data} = module.authenticate(LivebookWeb.ZTA, conn, [])
|
2023-06-21 06:25:25 +08:00
|
|
|
|
2024-04-13 16:29:22 +08:00
|
|
|
cond do
|
|
|
|
identity_data ->
|
|
|
|
# Ensure we have a unique ID to identify this user/session.
|
|
|
|
id =
|
|
|
|
identity_data[:id] || get_session(conn, :identity_data)[:id] ||
|
|
|
|
Livebook.Utils.random_long_id()
|
|
|
|
|
|
|
|
put_session(conn, :identity_data, Map.put(identity_data, :id, id))
|
|
|
|
|
|
|
|
conn.halted ->
|
|
|
|
conn
|
|
|
|
|
|
|
|
true ->
|
|
|
|
conn
|
|
|
|
|> put_status(:forbidden)
|
|
|
|
|> put_view(LivebookWeb.ErrorHTML)
|
|
|
|
|> render("403.html", %{status: 403})
|
|
|
|
|> halt()
|
2023-06-21 06:25:25 +08:00
|
|
|
end
|
|
|
|
end
|
|
|
|
|
2023-07-27 04:39:33 +08:00
|
|
|
defp ensure_user_data(conn) when conn.halted, do: conn
|
|
|
|
|
2021-05-04 02:03:19 +08:00
|
|
|
defp ensure_user_data(conn) do
|
2024-04-02 21:25:08 +08:00
|
|
|
if Map.has_key?(conn.req_cookies, "lb_user_data") do
|
2021-05-04 02:03:19 +08:00
|
|
|
conn
|
|
|
|
else
|
2024-04-13 16:29:22 +08:00
|
|
|
encoded =
|
|
|
|
%{"name" => nil, "hex_color" => Livebook.EctoTypes.HexColor.random()}
|
|
|
|
|> Jason.encode!()
|
|
|
|
|> Base.encode64()
|
2023-04-28 20:50:40 +08:00
|
|
|
|
2023-04-28 22:38:17 +08:00
|
|
|
# We disable HttpOnly, so that it can be accessed on the client
|
|
|
|
# and set expiration to 5 years
|
|
|
|
opts = [http_only: false, max_age: 157_680_000] ++ LivebookWeb.Endpoint.cookie_options()
|
2024-04-02 21:25:08 +08:00
|
|
|
put_resp_cookie(conn, "lb_user_data", encoded, opts)
|
2021-05-04 02:03:19 +08:00
|
|
|
end
|
|
|
|
end
|
|
|
|
|
2021-06-16 01:33:16 +08:00
|
|
|
# Copies user_data from cookie to session, so that it's
|
|
|
|
# accessible to LiveViews
|
2023-07-27 04:39:33 +08:00
|
|
|
defp mirror_user_data_in_session(conn) when conn.halted, do: conn
|
|
|
|
|
2021-06-16 01:33:16 +08:00
|
|
|
defp mirror_user_data_in_session(conn) do
|
2024-04-02 21:25:08 +08:00
|
|
|
user_data = conn.cookies["lb_user_data"] |> Base.decode64!() |> Jason.decode!()
|
2021-06-16 01:33:16 +08:00
|
|
|
put_session(conn, :user_data, user_data)
|
|
|
|
end
|
2021-05-04 02:03:19 +08:00
|
|
|
end
|