mirror of
https://github.com/livebook-dev/livebook.git
synced 2025-10-12 06:25:56 +08:00
Add secret key to personal hub edit form (#1776)
Co-authored-by: José Valim <jose.valim@dashbit.co>
This commit is contained in:
parent
52f94a09e4
commit
9712554c14
3 changed files with 112 additions and 40 deletions
|
@ -6,6 +6,8 @@ defmodule Livebook.Hubs.Personal do
|
||||||
|
|
||||||
alias Livebook.Hubs
|
alias Livebook.Hubs
|
||||||
|
|
||||||
|
@secret_key_size 64
|
||||||
|
|
||||||
@type t :: %__MODULE__{
|
@type t :: %__MODULE__{
|
||||||
id: String.t() | nil,
|
id: String.t() | nil,
|
||||||
hub_name: String.t() | nil,
|
hub_name: String.t() | nil,
|
||||||
|
@ -65,6 +67,12 @@ defmodule Livebook.Hubs.Personal do
|
||||||
personal
|
personal
|
||||||
|> cast(attrs, @fields)
|
|> cast(attrs, @fields)
|
||||||
|> validate_required(@fields)
|
|> validate_required(@fields)
|
||||||
|
|> validate_change(:secret_key, fn :secret_key, secret_key ->
|
||||||
|
case Base.url_decode64(secret_key, padding: false) do
|
||||||
|
{:ok, binary} when byte_size(binary) == @secret_key_size -> []
|
||||||
|
_ -> [secret_key: "must be #{@secret_key_size} bytes in Base 64 URL alphabet"]
|
||||||
|
end
|
||||||
|
end)
|
||||||
|> put_change(:id, id())
|
|> put_change(:id, id())
|
||||||
end
|
end
|
||||||
|
|
||||||
|
@ -73,7 +81,7 @@ defmodule Livebook.Hubs.Personal do
|
||||||
"""
|
"""
|
||||||
@spec generate_secret_key() :: String.t()
|
@spec generate_secret_key() :: String.t()
|
||||||
def generate_secret_key() do
|
def generate_secret_key() do
|
||||||
:crypto.strong_rand_bytes(64) |> Base.url_encode64(padding: false)
|
:crypto.strong_rand_bytes(@secret_key_size) |> Base.url_encode64(padding: false)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
|
@ -105,7 +105,7 @@ defmodule LivebookWeb.FormComponents do
|
||||||
name={@name}
|
name={@name}
|
||||||
id={@id || @name}
|
id={@id || @name}
|
||||||
value={Phoenix.HTML.Form.normalize_value("text", @value)}
|
value={Phoenix.HTML.Form.normalize_value("text", @value)}
|
||||||
class="input"
|
class="input pr-8"
|
||||||
{@rest}
|
{@rest}
|
||||||
/>
|
/>
|
||||||
</.with_password_toggle>
|
</.with_password_toggle>
|
||||||
|
@ -310,21 +310,11 @@ defmodule LivebookWeb.FormComponents do
|
||||||
|
|
||||||
~H"""
|
~H"""
|
||||||
<.field_wrapper id={@id} name={@name} label={@label} errors={@errors}>
|
<.field_wrapper id={@id} name={@name} label={@label} errors={@errors}>
|
||||||
<div class="flex border-[1px] bg-gray-50 rounded-lg space-x-4 items-center">
|
<div class="flex border bg-gray-50 rounded-lg space-x-4 items-center">
|
||||||
<div
|
<div id={"#{@id}-picker"} class="flex w-full" phx-hook="EmojiPicker">
|
||||||
id={"#{@id}-picker"}
|
<div class="grow p-1 pl-3">
|
||||||
class="grid grid-cols-1 md:grid-cols-3 w-full"
|
|
||||||
phx-hook="EmojiPicker"
|
|
||||||
>
|
|
||||||
<div class="place-content-start">
|
|
||||||
<div class="p-1 pl-3">
|
|
||||||
<span id={"#{@id}-preview"} data-emoji-preview><%= @value %></span>
|
<span id={"#{@id}-preview"} data-emoji-preview><%= @value %></span>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
|
||||||
|
|
||||||
<div />
|
|
||||||
|
|
||||||
<div class="flex items-center place-content-end">
|
|
||||||
<button
|
<button
|
||||||
id={"#{@id}-button"}
|
id={"#{@id}-button"}
|
||||||
type="button"
|
type="button"
|
||||||
|
@ -333,7 +323,6 @@ defmodule LivebookWeb.FormComponents do
|
||||||
>
|
>
|
||||||
<.remix_icon icon="emotion-line" class="text-xl" />
|
<.remix_icon icon="emotion-line" class="text-xl" />
|
||||||
</button>
|
</button>
|
||||||
</div>
|
|
||||||
<input
|
<input
|
||||||
type="hidden"
|
type="hidden"
|
||||||
name={@name}
|
name={@name}
|
||||||
|
@ -434,7 +423,7 @@ defmodule LivebookWeb.FormComponents do
|
||||||
|
|
||||||
def error(assigns) do
|
def error(assigns) do
|
||||||
~H"""
|
~H"""
|
||||||
<p class="text-red-600 text-sm hidden phx-form-error:block">
|
<p class="mt-0.5 text-red-600 text-sm hidden phx-form-error:block">
|
||||||
<%= render_slot(@inner_block) %>
|
<%= render_slot(@inner_block) %>
|
||||||
</p>
|
</p>
|
||||||
"""
|
"""
|
||||||
|
|
|
@ -17,7 +17,7 @@ defmodule LivebookWeb.Hub.Edit.PersonalComponent do
|
||||||
{:ok,
|
{:ok,
|
||||||
socket
|
socket
|
||||||
|> assign(assigns)
|
|> assign(assigns)
|
||||||
|> assign(changeset: changeset, secret_value: secret_value)}
|
|> assign(changeset: changeset, stamp_changeset: changeset, secret_value: secret_value)}
|
||||||
end
|
end
|
||||||
|
|
||||||
@impl true
|
@impl true
|
||||||
|
@ -58,7 +58,7 @@ defmodule LivebookWeb.Hub.Edit.PersonalComponent do
|
||||||
phx-disable-with="Updating..."
|
phx-disable-with="Updating..."
|
||||||
disable={not @changeset.valid?}
|
disable={not @changeset.valid?}
|
||||||
>
|
>
|
||||||
Update Hub
|
Save
|
||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
</.form>
|
</.form>
|
||||||
|
@ -76,6 +76,61 @@ defmodule LivebookWeb.Hub.Edit.PersonalComponent do
|
||||||
target={@myself}
|
target={@myself}
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
<div class="flex flex-col space-y-4">
|
||||||
|
<h2 class="text-xl text-gray-800 font-medium pb-2 border-b border-gray-200">
|
||||||
|
Stamping
|
||||||
|
</h2>
|
||||||
|
|
||||||
|
<p class="text-gray-700">
|
||||||
|
Notebooks may be stamped using your <span class="font-medium text-gray-800">secret key</span>.
|
||||||
|
A stamp allows to securely store information such as the names of the secrets that you granted access to.
|
||||||
|
You must not share your secret key with others. But you may copy the secret key between
|
||||||
|
different machines you own.
|
||||||
|
</p>
|
||||||
|
<p class="text-gray-700">
|
||||||
|
If you change the <span class="font-medium text-gray-800">secret key</span>, you will need
|
||||||
|
to grant access to secrets once again in previously stamped notebooks.
|
||||||
|
</p>
|
||||||
|
|
||||||
|
<.form
|
||||||
|
:let={f}
|
||||||
|
id={"#{@id}-stamp"}
|
||||||
|
class="flex flex-col mt-4 space-y-4"
|
||||||
|
for={@stamp_changeset}
|
||||||
|
phx-submit="stamp_save"
|
||||||
|
phx-change="stamp_validate"
|
||||||
|
phx-target={@myself}
|
||||||
|
>
|
||||||
|
<div class="flex space-x-2">
|
||||||
|
<div class="grow">
|
||||||
|
<.password_field field={f[:secret_key]} label="Secret key" />
|
||||||
|
</div>
|
||||||
|
<div class="mt-6">
|
||||||
|
<span class="tooltip top" data-tooltip="Generate">
|
||||||
|
<button
|
||||||
|
class="button-base button-outlined-gray button-square-icon"
|
||||||
|
type="button"
|
||||||
|
phx-click="generate_secret_key"
|
||||||
|
phx-target={@myself}
|
||||||
|
>
|
||||||
|
<.remix_icon icon="refresh-line" class="text-xl" />
|
||||||
|
</button>
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div>
|
||||||
|
<button
|
||||||
|
class="button-base button-blue"
|
||||||
|
type="submit"
|
||||||
|
phx-disable-with="Updating..."
|
||||||
|
disable={not @stamp_changeset.valid?}
|
||||||
|
>
|
||||||
|
Save
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
</.form>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<.modal
|
<.modal
|
||||||
|
@ -183,20 +238,24 @@ defmodule LivebookWeb.Hub.Edit.PersonalComponent do
|
||||||
|
|
||||||
@impl true
|
@impl true
|
||||||
def handle_event("save", %{"personal" => params}, socket) do
|
def handle_event("save", %{"personal" => params}, socket) do
|
||||||
case Personal.update_hub(socket.assigns.hub, params) do
|
{:noreply, save(params, :changeset, socket)}
|
||||||
{:ok, hub} ->
|
|
||||||
{:noreply,
|
|
||||||
socket
|
|
||||||
|> put_flash(:success, "Hub updated successfully")
|
|
||||||
|> push_navigate(to: ~p"/hub/#{hub.id}")}
|
|
||||||
|
|
||||||
{:error, changeset} ->
|
|
||||||
{:noreply, assign(socket, changeset: changeset)}
|
|
||||||
end
|
|
||||||
end
|
end
|
||||||
|
|
||||||
def handle_event("validate", %{"personal" => attrs}, socket) do
|
def handle_event("validate", %{"personal" => params}, socket) do
|
||||||
{:noreply, assign(socket, changeset: Personal.validate_hub(socket.assigns.hub, attrs))}
|
{:noreply, validate(params, :changeset, socket)}
|
||||||
|
end
|
||||||
|
|
||||||
|
def handle_event("stamp_save", %{"personal" => params}, socket) do
|
||||||
|
{:noreply, save(params, :stamp_changeset, socket)}
|
||||||
|
end
|
||||||
|
|
||||||
|
def handle_event("stamp_validate", %{"personal" => params}, socket) do
|
||||||
|
{:noreply, validate(params, :stamp_changeset, socket)}
|
||||||
|
end
|
||||||
|
|
||||||
|
def handle_event("generate_secret_key", %{}, socket) do
|
||||||
|
params = %{"secret_key" => Personal.generate_secret_key()}
|
||||||
|
{:noreply, validate(params, :stamp_changeset, socket)}
|
||||||
end
|
end
|
||||||
|
|
||||||
def handle_event("delete_hub_secret", attrs, socket) do
|
def handle_event("delete_hub_secret", attrs, socket) do
|
||||||
|
@ -205,4 +264,20 @@ defmodule LivebookWeb.Hub.Edit.PersonalComponent do
|
||||||
|
|
||||||
{:noreply, socket}
|
{:noreply, socket}
|
||||||
end
|
end
|
||||||
|
|
||||||
|
defp save(params, changeset_name, socket) do
|
||||||
|
case Personal.update_hub(socket.assigns.hub, params) do
|
||||||
|
{:ok, hub} ->
|
||||||
|
socket
|
||||||
|
|> put_flash(:success, "Hub updated successfully")
|
||||||
|
|> push_navigate(to: ~p"/hub/#{hub.id}")
|
||||||
|
|
||||||
|
{:error, changeset} ->
|
||||||
|
assign(socket, changeset_name, changeset)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
defp validate(params, changeset_name, socket) do
|
||||||
|
assign(socket, changeset_name, Personal.validate_hub(socket.assigns.hub, params))
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|
Loading…
Add table
Reference in a new issue