Skip to content

Commit

Permalink
add authorization to the Product Settings LiveView
Browse files Browse the repository at this point in the history
  • Loading branch information
joshk committed Dec 18, 2023
1 parent a401369 commit 9ee83c8
Show file tree
Hide file tree
Showing 7 changed files with 76 additions and 16 deletions.
9 changes: 3 additions & 6 deletions lib/nerves_hub/accounts.ex
Original file line number Diff line number Diff line change
Expand Up @@ -130,12 +130,9 @@ defmodule NervesHub.Accounts do
end

def get_org_user(org, user) do
from(
ou in OrgUser,
where:
ou.org_id == ^org.id and
ou.user_id == ^user.id
)
OrgUser
|> where([ou], ou.org_id == ^org.id)
|> where([ou], ou.user_id == ^user.id)
|> OrgUser.with_user()
|> Repo.exclude_deleted()
|> Repo.one()
Expand Down
2 changes: 2 additions & 0 deletions lib/nerves_hub_web.ex
Original file line number Diff line number Diff line change
Expand Up @@ -116,6 +116,8 @@ defmodule NervesHubWeb do
# Translation
import NervesHubWeb.Gettext

import NervesHub.Helpers.Authorization

# Shortcut for generating JS commands
alias Phoenix.LiveView.JS

Expand Down
17 changes: 17 additions & 0 deletions lib/nerves_hub_web/helpers/authorization.ex
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
defmodule NervesHub.Helpers.Authorization do
alias NervesHub.Accounts.OrgUser
alias NervesHub.Accounts.User

def authorized!(org_user, permission) do
true = authorized?(org_user, permission)
end

def authorized?(:update_product, %OrgUser{role: user_role}), do: role_check(:write, user_role)
def authorized?(:delete_product, %OrgUser{role: user_role}), do: role_check(:admin, user_role)

defp role_check(required_role, user_role) do
required_role
|> User.role_or_higher()
|> Enum.any?(&(&1 == user_role))
end
end
19 changes: 19 additions & 0 deletions lib/nerves_hub_web/live/product/settings.ex
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,8 @@ defmodule NervesHubWeb.Live.Product.Settings do
end

def handle_event("delta-updated", %{"delta_updatable" => delta}, socket) do
authorized!(:update_product, socket.assigns.org_user)

attrs = %{delta_updatable: delta == "true"}

{:ok, product} = Products.update_product(socket.assigns.product, attrs)
Expand All @@ -23,6 +25,8 @@ defmodule NervesHubWeb.Live.Product.Settings do
end

def handle_event("add-shared-secret", _params, socket) do
authorized!(:update_product, socket.assigns.org_user)

{:ok, _} = Products.create_shared_secret_auth(socket.assigns.product)

refreshed = Products.load_shared_secret_auth(socket.assigns.product)
Expand All @@ -40,6 +44,8 @@ defmodule NervesHubWeb.Live.Product.Settings do
end

def handle_event("deactivate-shared-secret", %{"shared_secret_id" => shared_secret_id}, socket) do
authorized!(:update_product, socket.assigns.org_user)

product = socket.assigns.product

{:ok, _} = Products.deactivate_shared_secret_auth(product, shared_secret_id)
Expand All @@ -48,4 +54,17 @@ defmodule NervesHubWeb.Live.Product.Settings do

{:reply, assign(socket, :shared_secrets, refreshed.shared_secret_auth)}
end

def handle_event("delete-product", _parmas, socket) do
authorized!(:delete_product, socket.assigns.org_user)

with {:ok, _product} <- Products.delete_product(socket.assigns.product) do
socket =
socket
|> put_flash(:info, "Product deleted successfully.")
|> redirect(to: "/org/#{socket.assigns.org.name}")

{:noreply, socket}
end
end
end
33 changes: 23 additions & 10 deletions lib/nerves_hub_web/live/product/settings.html.heex
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,16 @@
<form>
<div class="flex-row align-items-center">
<input type="hidden" name="delta_updatable" value={@product.delta_updatable} />
<input type="checkbox" id="delta_updatable_input" name="delta_updatable" value="true" checked={@product.delta_updatable} class="form-control checkbox" phx-change="delta-updated" />
<input
type="checkbox"
id="delta_updatable_input"
name="delta_updatable"
value="true"
checked={@product.delta_updatable}
class="form-control checkbox"
phx-change="delta-updated"
disabled={!authorized?(:update_product, @org_user)}
/>
<label for="delta_updatable_input" class="color-white pl-1 m-0">
Enable delta firmware updates
</label>
Expand Down Expand Up @@ -91,7 +100,14 @@
</button>
</td>
<td>
<button :if={is_nil(auth.deactivated_at)} class="btn btn-secondary" phx-click="deactivate-shared-secret" phx-value-shared_secret_id={auth.id} data-confirm="Are you sure?">
<button
:if={is_nil(auth.deactivated_at)}
class="btn btn-secondary"
phx-click="deactivate-shared-secret"
phx-value-shared_secret_id={auth.id}
disabled={!authorized?(:update_product, @org_user)}
data-confirm="Are you sure?"
>
Deactivate
</button>

Expand All @@ -103,10 +119,10 @@
</table>

<div>
<button :if={Enum.empty?(@shared_secrets)} class="btn btn-secondary" phx-click="add-shared-secret" data-confirm="Are you sure?">
<button :if={Enum.empty?(@shared_secrets)} class="btn btn-secondary" phx-click="add-shared-secret" disabled={!authorized?(:update_product, @org_user)} data-confirm="Are you sure?">
Add your first Shared Secret.
</button>
<button :if={Enum.any?(@shared_secrets)} class="btn btn-secondary" phx-click="add-shared-secret" data-confirm="Are you sure?">
<button :if={Enum.any?(@shared_secrets)} class="btn btn-secondary" phx-click="add-shared-secret" disabled={!authorized?(:update_product, @org_user)} data-confirm="Are you sure?">
Add a Shared Secret
</button>
</div>
Expand All @@ -118,10 +134,7 @@
<div class="border-bottom border-dark mt-5 mb-2"></div>

<div class="mt-4">
<%= link("Remove Product",
class: "btn btn-primary",
to: ~p"/org/#{@org.name}/#{@product.name}",
method: :delete,
data: [confirm: "Are you sure you want to delete this product? This can not be undone."]
) %>
<button class="btn btn-primary" phx-click="delete-product" data-confirm="Are you sure you want to delete this product? This can not be undone." disabled={!authorized?(:delete_product, @org_user)}>
Remove Product
</button>
</div>
11 changes: 11 additions & 0 deletions lib/nerves_hub_web/mounts/fetch_org_user.ex
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
defmodule NervesHubWeb.Mounts.FetchOrgUser do
import Phoenix.Component

alias NervesHub.Accounts

def on_mount(_, _, _, socket) do
{:ok, org_user} = Accounts.get_org_user(socket.assigns.org, socket.assigns.user)

{:cont, assign(socket, :org_user, org_user)}
end
end
1 change: 1 addition & 0 deletions lib/nerves_hub_web/router.ex
Original file line number Diff line number Diff line change
Expand Up @@ -256,6 +256,7 @@ defmodule NervesHubWeb.Router do
NervesHubWeb.Mounts.AccountAuth,
NervesHubWeb.Mounts.CurrentPath,
NervesHubWeb.Mounts.FetchOrg,
NervesHubWeb.Mounts.FetchOrgUser,
NervesHubWeb.Mounts.FetchProduct
] do
live("/settings", Live.Product.Settings)
Expand Down

0 comments on commit 9ee83c8

Please sign in to comment.