mirror of
https://github.com/livebook-dev/livebook.git
synced 2025-09-10 23:14:35 +08:00
Apply review comments
This commit is contained in:
parent
bd405e4b3c
commit
50ac0769c6
4 changed files with 73 additions and 67 deletions
|
@ -153,7 +153,7 @@ defmodule Livebook.Hubs.TeamClient do
|
||||||
@doc """
|
@doc """
|
||||||
Returns if the given user groups has access to given app.
|
Returns if the given user groups has access to given app.
|
||||||
"""
|
"""
|
||||||
@spec user_app_access?(String.t(), Livebook.Users.User.t(), String.t()) :: boolean()
|
@spec user_app_access?(String.t(), list(map()), String.t()) :: boolean()
|
||||||
def user_app_access?(id, groups, slug) do
|
def user_app_access?(id, groups, slug) do
|
||||||
GenServer.call(registry_name(id), {:check_app_access, groups, slug})
|
GenServer.call(registry_name(id), {:check_app_access, groups, slug})
|
||||||
end
|
end
|
||||||
|
|
|
@ -8,11 +8,28 @@ defmodule LivebookWeb.AppSessionLive do
|
||||||
alias Livebook.Notebook.Cell
|
alias Livebook.Notebook.Cell
|
||||||
|
|
||||||
@impl true
|
@impl true
|
||||||
|
def mount(%{"slug" => slug} = params, _session, socket)
|
||||||
|
when not socket.assigns.app_authenticated? do
|
||||||
|
if connected?(socket) do
|
||||||
|
to =
|
||||||
|
if id = params["id"] do
|
||||||
|
~p"/apps/#{slug}/authenticate?id=#{id}"
|
||||||
|
else
|
||||||
|
~p"/apps/#{slug}/authenticate"
|
||||||
|
end
|
||||||
|
|
||||||
def mount(%{"slug" => slug, "id" => session_id}, _session, socket)
|
{:ok, push_navigate(socket, to: to)}
|
||||||
when socket.assigns.app_authenticated? and socket.assigns.app_authorized? do
|
else
|
||||||
|
{:ok, socket}
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
def mount(_params, _session, socket) when not socket.assigns.app_authorized? do
|
||||||
|
{:ok, socket, layout: false}
|
||||||
|
end
|
||||||
|
|
||||||
|
def mount(%{"slug" => slug, "id" => session_id}, _session, socket) do
|
||||||
{:ok, app} = Livebook.Apps.fetch_app(slug)
|
{:ok, app} = Livebook.Apps.fetch_app(slug)
|
||||||
|
|
||||||
app_session = Enum.find(app.sessions, &(&1.id == session_id))
|
app_session = Enum.find(app.sessions, &(&1.id == session_id))
|
||||||
|
|
||||||
if app_session && app_session.app_status.lifecycle == :active do
|
if app_session && app_session.app_status.lifecycle == :active do
|
||||||
|
@ -54,26 +71,6 @@ defmodule LivebookWeb.AppSessionLive do
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
def mount(_params, _session, socket)
|
|
||||||
when socket.assigns.app_authenticated? and not socket.assigns.app_authorized? do
|
|
||||||
{:ok, socket, layout: false}
|
|
||||||
end
|
|
||||||
|
|
||||||
def mount(%{"slug" => slug} = params, _session, socket) do
|
|
||||||
if connected?(socket) do
|
|
||||||
to =
|
|
||||||
if id = params["id"] do
|
|
||||||
~p"/apps/#{slug}/authenticate?id=#{id}"
|
|
||||||
else
|
|
||||||
~p"/apps/#{slug}/authenticate"
|
|
||||||
end
|
|
||||||
|
|
||||||
{:ok, push_navigate(socket, to: to)}
|
|
||||||
else
|
|
||||||
{:ok, socket}
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
# Puts the given assigns in `socket.private`,
|
# Puts the given assigns in `socket.private`,
|
||||||
# to ensure they are not used for rendering.
|
# to ensure they are not used for rendering.
|
||||||
defp assign_private(socket, assigns) do
|
defp assign_private(socket, assigns) do
|
||||||
|
@ -116,7 +113,7 @@ defmodule LivebookWeb.AppSessionLive do
|
||||||
<.remix_icon icon="arrow-down-s-line" />
|
<.remix_icon icon="arrow-down-s-line" />
|
||||||
</button>
|
</button>
|
||||||
</:toggle>
|
</:toggle>
|
||||||
<.menu_item :if={@livebook_authenticated?}>
|
<.menu_item :if={@livebook_authorized?}>
|
||||||
<.link navigate={~p"/"} role="menuitem">
|
<.link navigate={~p"/"} role="menuitem">
|
||||||
<.remix_icon icon="home-6-line" />
|
<.remix_icon icon="home-6-line" />
|
||||||
<span>Home</span>
|
<span>Home</span>
|
||||||
|
@ -143,7 +140,7 @@ defmodule LivebookWeb.AppSessionLive do
|
||||||
<span>View source</span>
|
<span>View source</span>
|
||||||
</.link>
|
</.link>
|
||||||
</.menu_item>
|
</.menu_item>
|
||||||
<.menu_item :if={@livebook_authenticated?}>
|
<.menu_item :if={@livebook_authorized?}>
|
||||||
<.link patch={~p"/sessions/#{@session.id}"} role="menuitem">
|
<.link patch={~p"/sessions/#{@session.id}"} role="menuitem">
|
||||||
<.remix_icon icon="terminal-line" />
|
<.remix_icon icon="terminal-line" />
|
||||||
<span>Debug</span>
|
<span>Debug</span>
|
||||||
|
@ -183,7 +180,7 @@ defmodule LivebookWeb.AppSessionLive do
|
||||||
<div class="flex items-center gap-6">
|
<div class="flex items-center gap-6">
|
||||||
<span class="tooltip top" data-tooltip="Debug">
|
<span class="tooltip top" data-tooltip="Debug">
|
||||||
<.link
|
<.link
|
||||||
:if={@livebook_authenticated?}
|
:if={@livebook_authorized?}
|
||||||
navigate={~p"/sessions/#{@session.id}" <> "#cell-#{@data_view.errored_cell_id}"}
|
navigate={~p"/sessions/#{@session.id}" <> "#cell-#{@data_view.errored_cell_id}"}
|
||||||
>
|
>
|
||||||
<.remix_icon icon="terminal-line" />
|
<.remix_icon icon="terminal-line" />
|
||||||
|
@ -383,19 +380,16 @@ defmodule LivebookWeb.AppSessionLive do
|
||||||
{:noreply, redirect_on_closed(socket)}
|
{:noreply, redirect_on_closed(socket)}
|
||||||
end
|
end
|
||||||
|
|
||||||
def handle_info(
|
def handle_info({:app_deployment_updated, %{slug: slug}}, %{assigns: %{slug: slug}} = socket) do
|
||||||
{:app_deployment_updated, %{slug: slug, hub_id: hub_id}},
|
|
||||||
%{assigns: %{slug: slug}} = socket
|
|
||||||
) do
|
|
||||||
# We force the redirection in case of
|
# We force the redirection in case of
|
||||||
# the current user loses access to this app.
|
# the current user loses access to this app.
|
||||||
|
|
||||||
# With this strategy, we guarantee that unauthorized users
|
# With this strategy, we guarantee that unauthorized users
|
||||||
# won't be able to keep reading the app which they
|
# won't be able to keep reading the app which they
|
||||||
# should't have access.
|
# should't have access.
|
||||||
groups = socket.assigns.current_user.restricted_apps_groups
|
{:ok, app} = Livebook.Apps.fetch_app(slug)
|
||||||
|
|
||||||
if Livebook.Hubs.TeamClient.user_app_access?(hub_id, groups, slug) do
|
if Livebook.Apps.authorized?(app, socket.assigns.current_user) do
|
||||||
{:noreply, socket}
|
{:noreply, socket}
|
||||||
else
|
else
|
||||||
{:noreply, redirect(socket, to: ~p"/apps/#{slug}/sessions/#{socket.assigns.session.id}")}
|
{:noreply, redirect(socket, to: ~p"/apps/#{slug}/sessions/#{socket.assigns.session.id}")}
|
||||||
|
|
|
@ -38,7 +38,7 @@ defmodule LivebookWeb.AppAuthHook do
|
||||||
# For public apps (or in case the user has full access) it is
|
# For public apps (or in case the user has full access) it is
|
||||||
# set to `true` on both dead and live render
|
# set to `true` on both dead and live render
|
||||||
#
|
#
|
||||||
# * `:livebook_authenticated?` - if the user has full Livebook
|
# * `:livebook_authorized?` - if the user has full Livebook
|
||||||
# access
|
# access
|
||||||
#
|
#
|
||||||
# * `:app_settings` - the current app settings
|
# * `:app_settings` - the current app settings
|
||||||
|
@ -49,40 +49,26 @@ defmodule LivebookWeb.AppAuthHook do
|
||||||
LivebookWeb.SessionHelpers.subscribe_to_logout()
|
LivebookWeb.SessionHelpers.subscribe_to_logout()
|
||||||
end
|
end
|
||||||
|
|
||||||
user =
|
livebook_authorized? = livebook_authorized?(session, socket)
|
||||||
LivebookWeb.UserPlug.build_current_user(
|
|
||||||
session,
|
|
||||||
session["identity_data"],
|
|
||||||
session["user_data"]
|
|
||||||
)
|
|
||||||
|
|
||||||
livebook_authenticated? = livebook_authenticated?(session, user, socket)
|
|
||||||
|
|
||||||
socket =
|
socket =
|
||||||
socket
|
socket
|
||||||
|> assign(livebook_authenticated?: livebook_authenticated?)
|
|> assign(livebook_authorized?: livebook_authorized?)
|
||||||
|> attach_hook(:logout, :handle_info, &handle_info/2)
|
|> attach_hook(:logout, :handle_info, &handle_info/2)
|
||||||
|> attach_hook(:logout, :handle_event, &handle_event/3)
|
|> attach_hook(:logout, :handle_event, &handle_event/3)
|
||||||
|
|
||||||
with {:ok, app} <- Livebook.Apps.fetch_app(slug),
|
with {:ok, app} <- Livebook.Apps.fetch_app(slug),
|
||||||
{:ok, app_settings} <- Livebook.Apps.fetch_settings(slug) do
|
{:ok, app_settings} <- Livebook.Apps.fetch_settings(slug) do
|
||||||
app_authorized? =
|
|
||||||
case app.app_spec do
|
|
||||||
%Livebook.Apps.TeamsAppSpec{hub_id: hub_id} ->
|
|
||||||
Livebook.Hubs.TeamClient.user_app_access?(hub_id, user.restricted_apps_groups, slug)
|
|
||||||
|
|
||||||
_ ->
|
|
||||||
true
|
|
||||||
end
|
|
||||||
|
|
||||||
app_authenticated? =
|
app_authenticated? =
|
||||||
app_settings.access_type == :public or
|
case app_settings.access_type do
|
||||||
(livebook_authenticated? or has_valid_token?(socket, app_settings))
|
:public -> true
|
||||||
|
:protected -> livebook_authorized? or has_valid_token?(socket, app_settings)
|
||||||
|
end
|
||||||
|
|
||||||
{:cont,
|
{:cont,
|
||||||
assign(socket,
|
assign(socket,
|
||||||
app_authenticated?: app_authenticated?,
|
app_authenticated?: app_authenticated?,
|
||||||
app_authorized?: app_authorized?,
|
app_authorized?: app_authorized?(session, app),
|
||||||
app_settings: app_settings
|
app_settings: app_settings
|
||||||
)}
|
)}
|
||||||
else
|
else
|
||||||
|
@ -95,11 +81,22 @@ defmodule LivebookWeb.AppAuthHook do
|
||||||
{:cont, socket}
|
{:cont, socket}
|
||||||
end
|
end
|
||||||
|
|
||||||
defp livebook_authenticated?(session, user, socket) do
|
defp livebook_authorized?(session, socket) do
|
||||||
uri = get_connect_info(socket, :uri)
|
uri = get_connect_info(socket, :uri)
|
||||||
|
|
||||||
LivebookWeb.AuthPlug.authenticated?(session, uri.port) and
|
LivebookWeb.AuthPlug.authenticated?(session, uri.port) and
|
||||||
user.restricted_apps_groups == nil
|
LivebookWeb.AuthPlug.authorized?(session)
|
||||||
|
end
|
||||||
|
|
||||||
|
defp app_authorized?(session, app) do
|
||||||
|
user =
|
||||||
|
LivebookWeb.UserPlug.build_current_user(
|
||||||
|
session,
|
||||||
|
session["identity_data"],
|
||||||
|
session["user_data"]
|
||||||
|
)
|
||||||
|
|
||||||
|
Livebook.Apps.authorized?(app, user)
|
||||||
end
|
end
|
||||||
|
|
||||||
defp handle_info(:logout, socket) do
|
defp handle_info(:logout, socket) do
|
||||||
|
|
|
@ -12,17 +12,12 @@ defmodule LivebookWeb.AuthPlug do
|
||||||
@impl true
|
@impl true
|
||||||
def call(conn, _opts) do
|
def call(conn, _opts) do
|
||||||
if authenticated?(conn) do
|
if authenticated?(conn) do
|
||||||
user =
|
if not authorized?(conn) do
|
||||||
LivebookWeb.UserPlug.build_current_user(
|
# User has access only to specific app pages, so they do not have
|
||||||
get_session(conn),
|
# access to any pages guarded by this plug.
|
||||||
conn.assigns.identity_data,
|
|
||||||
conn.assigns.user_data
|
|
||||||
)
|
|
||||||
|
|
||||||
if user.restricted_apps_groups == nil do
|
|
||||||
conn
|
|
||||||
else
|
|
||||||
render_unauthorized(conn)
|
render_unauthorized(conn)
|
||||||
|
else
|
||||||
|
conn
|
||||||
end
|
end
|
||||||
else
|
else
|
||||||
authenticate(conn)
|
authenticate(conn)
|
||||||
|
@ -62,6 +57,26 @@ defmodule LivebookWeb.AuthPlug do
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
@doc """
|
||||||
|
Checks if given connection or session is authorized.
|
||||||
|
"""
|
||||||
|
@spec authorized?(Plug.Conn.t() | map()) :: boolean()
|
||||||
|
def authorized?(%Plug.Conn{} = conn) do
|
||||||
|
LivebookWeb.UserPlug.build_current_user(
|
||||||
|
get_session(conn),
|
||||||
|
conn.assigns.identity_data,
|
||||||
|
conn.assigns.user_data
|
||||||
|
).restricted_apps_groups == nil
|
||||||
|
end
|
||||||
|
|
||||||
|
def authorized?(%{} = session) do
|
||||||
|
LivebookWeb.UserPlug.build_current_user(
|
||||||
|
session,
|
||||||
|
session["identity_data"],
|
||||||
|
session["user_data"]
|
||||||
|
).restricted_apps_groups == nil
|
||||||
|
end
|
||||||
|
|
||||||
defp authenticate(conn) do
|
defp authenticate(conn) do
|
||||||
case authentication(conn) do
|
case authentication(conn) do
|
||||||
%{mode: :password} ->
|
%{mode: :password} ->
|
||||||
|
|
Loading…
Add table
Reference in a new issue