From 77f2948337af47beebde2870f5ce57a1576ed3b4 Mon Sep 17 00:00:00 2001 From: Wojtek Mach Date: Wed, 1 Jan 2025 19:30:22 +0100 Subject: [PATCH] Restrict teams features based on org status (#2897) --- lib/livebook/hubs/team.ex | 1 + lib/livebook/hubs/team_client.ex | 25 ++++++++++++++++--- .../live/hub/edit/personal_component.ex | 3 +++ .../live/hub/edit/team_component.ex | 25 +++++++++++++++++-- lib/livebook_web/live/hub/edit_live.ex | 5 ++++ .../live/hub/file_system_form_component.ex | 2 +- .../live/hub/file_system_list_component.ex | 6 ++++- .../live/hub/secret_form_component.ex | 2 +- .../lib/livebook_proto/agent_connected.pb.ex | 1 + proto/lib/livebook_proto/event.pb.ex | 2 ++ proto/lib/livebook_proto/org_updated.pb.ex | 6 +++++ proto/lib/livebook_proto/user_connected.pb.ex | 1 + proto/messages.proto | 8 ++++++ 13 files changed, 78 insertions(+), 9 deletions(-) create mode 100644 proto/lib/livebook_proto/org_updated.pb.ex diff --git a/lib/livebook/hubs/team.ex b/lib/livebook/hubs/team.ex index 83e5be63e..60df74655 100644 --- a/lib/livebook/hubs/team.ex +++ b/lib/livebook/hubs/team.ex @@ -41,6 +41,7 @@ defmodule Livebook.Hubs.Team do field :session_token, :string, redact: true field :hub_name, :string field :hub_emoji, :string + field :disabled, :boolean, default: false embeds_one :offline, Offline end diff --git a/lib/livebook/hubs/team_client.ex b/lib/livebook/hubs/team_client.ex index 14a3e34a7..c7cc44eb2 100644 --- a/lib/livebook/hubs/team_client.ex +++ b/lib/livebook/hubs/team_client.ex @@ -641,6 +641,7 @@ defmodule Livebook.Hubs.TeamClient do defp handle_event(:user_connected, user_connected, state) do state + |> update_hub(user_connected) |> dispatch_secrets(user_connected) |> dispatch_file_systems(user_connected) |> dispatch_deployment_groups(user_connected) @@ -728,6 +729,10 @@ defmodule Livebook.Hubs.TeamClient do state end + defp handle_event(:org_updated, org_updated, state) do + update_hub(state, org_updated) + end + defp dispatch_secrets(state, %{secrets: secrets}) do decrypted_secrets = Enum.map(secrets, &build_secret(state, &1)) @@ -796,14 +801,26 @@ defmodule Livebook.Hubs.TeamClient do state end - defp update_hub(state, %{public_key: org_public_key}) do - hub = %{state.hub | org_public_key: org_public_key} + defp update_hub(state, %LivebookProto.UserConnected{org_disabled: disabled}) do + update_hub(state, &put_in(&1.disabled, disabled)) + end - if Livebook.Hubs.hub_exists?(hub.id) do + defp update_hub(state, %LivebookProto.OrgUpdated{disabled: disabled}) do + update_hub(state, &put_in(&1.disabled, disabled)) + end + + defp update_hub(state, %LivebookProto.AgentConnected{public_key: org_public_key}) do + update_hub(state, &put_in(&1.org_public_key, org_public_key)) + end + + defp update_hub(state, fun) when is_function(fun, 1) do + hub = fun.(state.hub) + + if Hubs.hub_exists?(hub.id) do Hubs.save_hub(hub) end - %{state | hub: hub} + put_in(state.hub, hub) end defp diff(old_list, new_list, fun, deleted_fun \\ nil, updated_fun \\ nil) do diff --git a/lib/livebook_web/live/hub/edit/personal_component.ex b/lib/livebook_web/live/hub/edit/personal_component.ex index dde1164e9..5c1fabcee 100644 --- a/lib/livebook_web/live/hub/edit/personal_component.ex +++ b/lib/livebook_web/live/hub/edit/personal_component.ex @@ -123,6 +123,7 @@ defmodule LivebookWeb.Hub.Edit.PersonalComponent do id="hub-file-systems-list" hub_id={@hub.id} file_systems={@file_systems} + disabled={false} /> @@ -195,6 +196,7 @@ defmodule LivebookWeb.Hub.Edit.PersonalComponent do hub={@hub} secret_name={@secret_name} secret_value={@secret_value} + disabled={false} return_to={~p"/hub/#{@hub.id}"} /> @@ -210,6 +212,7 @@ defmodule LivebookWeb.Hub.Edit.PersonalComponent do module={LivebookWeb.Hub.FileSystemFormComponent} id="file-systems" hub={@hub} + disabled={false} file_system={@file_system} file_system_id={@file_system_id} return_to={~p"/hub/#{@hub.id}"} diff --git a/lib/livebook_web/live/hub/edit/team_component.ex b/lib/livebook_web/live/hub/edit/team_component.ex index 2e4fc3863..fac6260dc 100644 --- a/lib/livebook_web/live/hub/edit/team_component.ex +++ b/lib/livebook_web/live/hub/edit/team_component.ex @@ -63,6 +63,15 @@ defmodule LivebookWeb.Hub.Edit.TeamComponent do {Provider.connection_status(@hub)} + +

+ Workspace disabled: your organization doesn't have an active subscription. Please contact your <.link + href={org_url(@hub, "/users")} + class="underline" + >org's admin. +

+
+
@@ -176,10 +185,15 @@ defmodule LivebookWeb.Hub.Edit.TeamComponent do secrets={@secrets} edit_path={"hub/#{@hub.id}/secrets/edit"} return_to={~p"/hub/#{@hub.id}"} + disabled={@hub.disabled} />
- <.button patch={~p"/hub/#{@hub.id}/secrets/new"} id="add-secret"> + <.button + patch={~p"/hub/#{@hub.id}/secrets/new"} + id="add-secret" + disabled={@hub.disabled} + > Add secret
@@ -200,6 +214,7 @@ defmodule LivebookWeb.Hub.Edit.TeamComponent do hub_id={@hub.id} file_systems={@file_systems} target={@myself} + disabled={@hub.disabled} />
@@ -233,7 +248,11 @@ defmodule LivebookWeb.Hub.Edit.TeamComponent do
- <.button patch={~p"/hub/#{@hub.id}/groups/new"} id="add-deployment-group"> + <.button + patch={~p"/hub/#{@hub.id}/groups/new"} + id="add-deployment-group" + disabled={@hub.disabled} + > Add deployment group
@@ -289,6 +308,7 @@ defmodule LivebookWeb.Hub.Edit.TeamComponent do secret_name={@secret_name} secret_value={@secret_value} return_to={~p"/hub/#{@hub.id}"} + disabled={@hub.disabled} /> @@ -303,6 +323,7 @@ defmodule LivebookWeb.Hub.Edit.TeamComponent do module={LivebookWeb.Hub.FileSystemFormComponent} id="file-systems" hub={@hub} + disabled={@hub.disabled} file_system={@file_system} file_system_id={@file_system_id} return_to={~p"/hub/#{@hub.id}"} diff --git a/lib/livebook_web/live/hub/edit_live.ex b/lib/livebook_web/live/hub/edit_live.ex index fc99e48ce..6768f2161 100644 --- a/lib/livebook_web/live/hub/edit_live.ex +++ b/lib/livebook_web/live/hub/edit_live.ex @@ -11,6 +11,7 @@ defmodule LivebookWeb.Hub.EditLive do def mount(_params, _session, socket) do if connected?(socket) do Hubs.Broadcasts.subscribe([:connection]) + Livebook.Teams.Broadcasts.subscribe([:deployment_groups, :app_deployments, :agents]) end @@ -111,6 +112,10 @@ defmodule LivebookWeb.Hub.EditLive do {:noreply, load_hub(socket, id)} end + def handle_info({:hub_changed, id}, %{assigns: %{hub: %{id: id}}} = socket) do + {:noreply, load_hub(socket, id)} + end + def handle_info(_message, socket) do {:noreply, socket} end diff --git a/lib/livebook_web/live/hub/file_system_form_component.ex b/lib/livebook_web/live/hub/file_system_form_component.ex index 47693f5e1..f5c6105e4 100644 --- a/lib/livebook_web/live/hub/file_system_form_component.ex +++ b/lib/livebook_web/live/hub/file_system_form_component.ex @@ -88,7 +88,7 @@ defmodule LivebookWeb.Hub.FileSystemFormComponent do

<% end %>
- <.button type="submit" disabled={not @changeset.valid?}> + <.button type="submit" disabled={@disabled or not @changeset.valid?}> <.remix_icon icon={@button.icon} /> {@button.label} diff --git a/lib/livebook_web/live/hub/file_system_list_component.ex b/lib/livebook_web/live/hub/file_system_list_component.ex index e732e81be..5f278878f 100644 --- a/lib/livebook_web/live/hub/file_system_list_component.ex +++ b/lib/livebook_web/live/hub/file_system_list_component.ex @@ -60,7 +60,11 @@ defmodule LivebookWeb.Hub.FileSystemListComponent do
- <.button patch={~p"/hub/#{@hub_id}/file-systems/new"} id="add-file-system"> + <.button + patch={~p"/hub/#{@hub_id}/file-systems/new"} + id="add-file-system" + disabled={@disabled} + > Add file storage
diff --git a/lib/livebook_web/live/hub/secret_form_component.ex b/lib/livebook_web/live/hub/secret_form_component.ex index dd99f34f6..ff1280017 100644 --- a/lib/livebook_web/live/hub/secret_form_component.ex +++ b/lib/livebook_web/live/hub/secret_form_component.ex @@ -75,7 +75,7 @@ defmodule LivebookWeb.Hub.SecretFormComponent do <.hidden_field field={f[:hub_id]} value={@hub.id} /> <.hidden_field field={f[:deployment_group_id]} value={@deployment_group_id} />
- <.button type="submit" disabled={not @changeset.valid?}> + <.button type="submit" disabled={@disabled or not @changeset.valid?}> <.remix_icon icon={@button.icon} /> {@button.label} diff --git a/proto/lib/livebook_proto/agent_connected.pb.ex b/proto/lib/livebook_proto/agent_connected.pb.ex index 75234eb03..60b249bfd 100644 --- a/proto/lib/livebook_proto/agent_connected.pb.ex +++ b/proto/lib/livebook_proto/agent_connected.pb.ex @@ -18,4 +18,5 @@ defmodule LivebookProto.AgentConnected do json_name: "appDeployments" field :agents, 9, repeated: true, type: LivebookProto.Agent + field :org_disabled, 10, type: :bool, json_name: "orgDisabled" end diff --git a/proto/lib/livebook_proto/event.pb.ex b/proto/lib/livebook_proto/event.pb.ex index fe26e32e9..5eb37e100 100644 --- a/proto/lib/livebook_proto/event.pb.ex +++ b/proto/lib/livebook_proto/event.pb.ex @@ -71,4 +71,6 @@ defmodule LivebookProto.Event do type: LivebookProto.AppDeploymentStopped, json_name: "appDeploymentStopped", oneof: 0 + + field :org_updated, 17, type: LivebookProto.OrgUpdated, json_name: "orgUpdated", oneof: 0 end diff --git a/proto/lib/livebook_proto/org_updated.pb.ex b/proto/lib/livebook_proto/org_updated.pb.ex new file mode 100644 index 000000000..ecf173425 --- /dev/null +++ b/proto/lib/livebook_proto/org_updated.pb.ex @@ -0,0 +1,6 @@ +defmodule LivebookProto.OrgUpdated do + use Protobuf, protoc_gen_elixir_version: "0.13.0", syntax: :proto3 + + field :id, 1, type: :string + field :disabled, 2, type: :bool +end diff --git a/proto/lib/livebook_proto/user_connected.pb.ex b/proto/lib/livebook_proto/user_connected.pb.ex index a91b75669..81b8eec26 100644 --- a/proto/lib/livebook_proto/user_connected.pb.ex +++ b/proto/lib/livebook_proto/user_connected.pb.ex @@ -16,4 +16,5 @@ defmodule LivebookProto.UserConnected do json_name: "appDeployments" field :agents, 6, repeated: true, type: LivebookProto.Agent + field :org_disabled, 7, type: :bool, json_name: "orgDisabled" end diff --git a/proto/messages.proto b/proto/messages.proto index 239c56fcc..67e53e5cd 100644 --- a/proto/messages.proto +++ b/proto/messages.proto @@ -110,6 +110,7 @@ message UserConnected { repeated DeploymentGroup deployment_groups = 4; repeated AppDeployment app_deployments = 5; repeated Agent agents = 6; + bool org_disabled = 7; } message AgentConnected { @@ -121,6 +122,7 @@ message AgentConnected { repeated DeploymentGroup deployment_groups = 7; repeated AppDeployment app_deployments = 8; repeated Agent agents = 9; + bool org_disabled = 10; } message AppDeployment { @@ -157,6 +159,11 @@ message AgentLeft { string id = 1; } +message OrgUpdated { + string id = 1; + bool disabled = 2; +} + message Agent { string id = 1; string name = 2; @@ -210,5 +217,6 @@ message Event { AgentJoined agent_joined = 14; AgentLeft agent_left = 15; AppDeploymentStopped app_deployment_stopped = 16; + OrgUpdated org_updated = 17; } }