From e1d6ca14a6432c3883591832d65c48977b03317e Mon Sep 17 00:00:00 2001 From: Finn Behrens Date: Sat, 22 Jul 2023 14:04:30 +0200 Subject: [PATCH] feat(auth): add webauthn key list edit button --- .../repo/account/user/u2f_token.ex | 30 +++++++++ .../components/core_components.ex | 40 +++++++++-- .../auth/webauthn_html/edit_token.html.heex | 34 ++++++++++ .../auth/webauthn_html/index.html.heex | 13 ++-- .../auth/webauthn_html/token.html.heex | 13 ++-- .../controllers/auth/webauthn_live.ex | 66 ++++++++++++++++--- 6 files changed, 168 insertions(+), 28 deletions(-) create mode 100644 apps/ex_fleet_yards_auth/lib/ex_fleet_yards_auth/controllers/auth/webauthn_html/edit_token.html.heex diff --git a/apps/ex_fleet_yards/lib/ex_fleet_yards/repo/account/user/u2f_token.ex b/apps/ex_fleet_yards/lib/ex_fleet_yards/repo/account/user/u2f_token.ex index 91d98fb6..ab827260 100644 --- a/apps/ex_fleet_yards/lib/ex_fleet_yards/repo/account/user/u2f_token.ex +++ b/apps/ex_fleet_yards/lib/ex_fleet_yards/repo/account/user/u2f_token.ex @@ -4,6 +4,7 @@ defmodule ExFleetYards.Repo.Account.User.U2fToken do """ use TypedEctoSchema + require Logger alias ExFleetYards.Repo alias ExFleetYards.Repo.Account.User @@ -39,6 +40,17 @@ defmodule ExFleetYards.Repo.Account.User.U2fToken do import Ecto.Query + def get_key(user, key) do + key_query(user, key) + |> Repo.one() + end + + def edit(key, params) do + edit_changeset(key, params) + |> Repo.update() + |> broadcast_change([:edit]) + end + def user_allow_credentials(%User{id: user_id}), do: user_allow_credentials(user_id) def user_allow_credentials(user_id) when is_binary(user_id) do @@ -86,9 +98,17 @@ defmodule ExFleetYards.Repo.Account.User.U2fToken do token |> cast(attrs, [:user_id, :credential_id, :cose_key, :name]) |> validate_required([:user_id, :credential_id, :cose_key]) + |> validate_length(:name, min: 2, max: 30) |> unique_constraint([:credential_id]) end + def edit_changeset(token, attrs) do + token + |> cast(attrs, [:name]) + |> validate_length(:name, min: 2, max: 30) + |> validate_required([:name]) + end + @doc """ Subscribe to updates to the user webauthn key list. """ @@ -111,5 +131,15 @@ defmodule ExFleetYards.Repo.Account.User.U2fToken do v end + defp broadcast_change(v, _event) do + Logger.debug("Could not send broadcast for #{inspect(v)}") + v + end + + defp broadcast_change(v, _event, _user) do + Logger.debug("Could not send broadcast for #{inspect(v)}") + v + end + defp topic(user_id) when is_binary(user_id), do: Atom.to_string(__MODULE__) <> ":" <> user_id end diff --git a/apps/ex_fleet_yards_auth/lib/ex_fleet_yards_auth/components/core_components.ex b/apps/ex_fleet_yards_auth/lib/ex_fleet_yards_auth/components/core_components.ex index ed29fb0f..3432ab83 100644 --- a/apps/ex_fleet_yards_auth/lib/ex_fleet_yards_auth/components/core_components.ex +++ b/apps/ex_fleet_yards_auth/lib/ex_fleet_yards_auth/components/core_components.ex @@ -15,18 +15,43 @@ defmodule ExFleetYardsAuth.CoreComponents do """ attr :type, :string, default: nil attr :class, :string, default: nil + attr :role, :string, default: "primary" attr :rest, :global, include: ~w(disabled form name value) slot :inner_block, required: true - def button(assigns) do + def button(%{role: "cancel"} = assigns) do + classList = [ + "bg-red-500 hover:bg-red-600 active:bg-red-700 focus:border-red-600 ring-red-300 text-white", + assigns["class"] + ] + + assigns + |> assign(role: nil) + |> assign(class: Enum.join(classList, ",")) + |> button + end + + def button(%{role: "primary"} = assigns) do + classList = [ + "bg-indigo-400 hover:bg-indigo-500 active:bg-indigo-600", + "focus:border-indigo-500 ring-indigo-300 text-white", + assigns["class"] + ] + + assigns + |> assign(role: nil) + |> assign(class: Enum.join(classList, ",")) + |> button + end + + def button(%{role: nil} = assigns) do ~H""" - + diff --git a/apps/ex_fleet_yards_auth/lib/ex_fleet_yards_auth/controllers/auth/webauthn_live.ex b/apps/ex_fleet_yards_auth/lib/ex_fleet_yards_auth/controllers/auth/webauthn_live.ex index 39aeab01..2006980c 100644 --- a/apps/ex_fleet_yards_auth/lib/ex_fleet_yards_auth/controllers/auth/webauthn_live.ex +++ b/apps/ex_fleet_yards_auth/lib/ex_fleet_yards_auth/controllers/auth/webauthn_live.ex @@ -10,14 +10,6 @@ defmodule ExFleetYardsAuth.Auth.WebAuthNLive do ExFleetYardsAuth.Auth.WebAuthNHTML.index(assigns) end - def token(assigns) do - IO.inspect(assigns) - - ~H""" - - """ - end - def mount(params, %{"user_token" => user_token}, socket) do {:ok, token, user} = ExFleetYardsAuth.Auth.get_user_from_token(user_token) User.U2fToken.subscribe(user) @@ -32,6 +24,8 @@ defmodule ExFleetYardsAuth.Auth.WebAuthNLive do |> assign(:current_user, user) |> assign(:totp_tokens, totp_tokens) |> assign(:webauthn_keys, webauthn_keys) + |> assign(:edit_key, nil) + |> assign(:edit_form, nil) {:ok, socket} end @@ -44,6 +38,56 @@ defmodule ExFleetYardsAuth.Auth.WebAuthNLive do {:noreply, socket} end + def handle_event("edit_key", %{"id" => key_id}, socket) do + user = socket.assigns[:current_user] + key = User.U2fToken.get_key(user, key_id) + + changeset = + User.U2fToken.edit_changeset(key, %{}) + |> to_form + + {:noreply, + socket + |> assign(:edit_key, key) + |> assign(:edit_form, changeset)} + end + + def handle_event( + "validate_edit", + %{"u2f_token" => params}, + %{assigns: %{edit_key: key}} = socket + ) do + changeset = + User.U2fToken.edit_changeset(key, params) + |> to_form() + + {:noreply, + socket + |> assign(:edit_form, changeset)} + end + + def handle_event("save_edit", %{"u2f_token" => params}, %{assigns: %{edit_key: key}} = socket) do + User.U2fToken.edit(key, params) + |> case do + {:error, e} -> + form = to_form(e) + + {:noreply, + socket + |> assign(:edit_form, form)} + + {:ok, key} -> + {:noreply, + socket + |> assign(:edit_form, nil) + |> assign(:edit_key, nil)} + end + end + + def handle_event("cancel_edit", %{"id" => key_id}, socket) do + {:noreply, socket |> assign(:edit_key, nil) |> assign(:edit_form, nil)} + end + def handle_info({User.U2fToken, [:delete], _}, socket) do webauthn_keys = User.U2fToken.key_list(socket.assigns[:current_user]) {:noreply, assign(socket, webauthn_keys: webauthn_keys)} @@ -53,4 +97,10 @@ defmodule ExFleetYardsAuth.Auth.WebAuthNLive do webauthn_keys = User.U2fToken.key_list(socket.assigns[:current_user]) {:noreply, assign(socket, webauthn_keys: webauthn_keys)} end + + def handle_info({User.U2fToken, [:edit], _}, socket) do + # dont refresh full list? + webauthn_keys = User.U2fToken.key_list(socket.assigns[:current_user]) + {:noreply, assign(socket, webauthn_keys: webauthn_keys)} + end end