Skip to content

Commit

Permalink
improvement: Adds show_sensitive_fields option to Resource to allow u…
Browse files Browse the repository at this point in the history
…nredacting se… (#86)

---------

Co-authored-by: Rohan Relan <[email protected]>
  • Loading branch information
hanrelan and Rohan Relan authored Jan 5, 2024
1 parent 4b944cb commit 3a285e8
Show file tree
Hide file tree
Showing 9 changed files with 84 additions and 18 deletions.
1 change: 1 addition & 0 deletions .formatter.exs
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ spark_locals_without_parens = [
resource_group_labels: 1,
show?: 1,
show_action: 1,
show_sensitive_fields: 1,
table_columns: 1,
type: 1,
update_actions: 1
Expand Down
7 changes: 4 additions & 3 deletions dev/repo/seeds.exs
Original file line number Diff line number Diff line change
Expand Up @@ -13,15 +13,16 @@ defmodule Demo.Seeder do
})
end

@spec insert_user!(String.t(), String.t(), String.t(), String.t()) :: User.t()
def insert_user!(first_name, last_name, bio, history) do
@spec insert_user!(String.t(), String.t(), String.t(), String.t(), String.t()) :: User.t()
def insert_user!(first_name, last_name, bio, history, api_key) do
Seed.seed!(User, %{
first_name: first_name,
last_name: last_name,
profile: %{
bio: bio,
history: history,
},
api_key: api_key,
alternate_profiles: []
})
end
Expand Down Expand Up @@ -84,7 +85,7 @@ System.argv()

Demo.Seeder.insert_admin!("Super", "Admin");
org = Demo.Seeder.insert_organization!("Ash Project");
Demo.Seeder.insert_user!("Alice", "Courtney", "Lorem ipsum dolor sit amet", "Duis aute irure dolor in reprehenderit in voluptate velit esse");
Demo.Seeder.insert_user!("Alice", "Courtney", "Lorem ipsum dolor sit amet", "Duis aute irure dolor in reprehenderit in voluptate velit esse", "123456");
bob = Demo.Seeder.insert_customer!("Bob", "Maclean");
carol = Demo.Seeder.insert_representative!("Carol", "White");

Expand Down
20 changes: 20 additions & 0 deletions documentation/dsls/DSL:-AshAdmin.Resource.cheatmd
Original file line number Diff line number Diff line change
Expand Up @@ -293,6 +293,26 @@ If this is not set, then *all* actions will require tables.
</td>
</tr>

<tr>
<td style="text-align: left">
<a id="admin-show_sensitive_fields" href="#admin-show_sensitive_fields">
<span style="font-family: Inconsolata, Menlo, Courier, monospace;">
show_sensitive_fields
</span>
</a>

</td>
<td style="text-align: left">
<code class="inline">list(atom)</code>
</td>
<td style="text-align: left">

</td>
<td style="text-align: left" colspan=2>
The list of fields that should not be redacted in the admin UI even if they are marked as sensitive.
</td>
</tr>

</tbody>
</table>

Expand Down
1 change: 1 addition & 0 deletions lib/ash_admin/components/resource/data_table.ex
Original file line number Diff line number Diff line change
Expand Up @@ -103,6 +103,7 @@ defmodule AshAdmin.Components.Resource.DataTable do
api={@api}
attributes={AshAdmin.Resource.table_columns(@resource)}
format_fields={AshAdmin.Resource.format_fields(@resource)}
show_sensitive_fields={AshAdmin.Resource.show_sensitive_fields(@resource)}
prefix={@prefix}
actor={@actor}
/>
Expand Down
21 changes: 15 additions & 6 deletions lib/ash_admin/components/resource/form.ex
Original file line number Diff line number Diff line change
Expand Up @@ -751,7 +751,7 @@ defmodule AshAdmin.Components.Resource.Form do
><%= value(@value, @form, @attribute) %></textarea>
<% short_text?(@form.source.resource, @attribute) -> %>
<.input
type={text_input_type(@attribute)}
type={text_input_type(@form.source.resource, @attribute)}
id={@form.id <> "_#{@attribute.name}"}
value={value(@value, @form, @attribute)}
class="mt-1 focus:ring-indigo-500 focus:border-indigo-500 block w-full shadow-sm sm:text-sm border-gray-300 rounded-md"
Expand All @@ -760,7 +760,7 @@ defmodule AshAdmin.Components.Resource.Form do
/>
<% true -> %>
<.input
type={text_input_type(@attribute)}
type={text_input_type(@form.source.resource, @attribute)}
placeholder={placeholder(@default)}
id={@form.id <> "_#{@attribute.name}"}
value={value(@value, @form, @attribute)}
Expand Down Expand Up @@ -954,7 +954,7 @@ defmodule AshAdmin.Components.Resource.Form do

~H"""
<.input
type={text_input_type(@attribute)}
type={text_input_type(@form.source.resource, @attribute)}
placeholder={placeholder(@attribute.default)}
value={@casted_value}
name={@name || @form.name <> "[#{@attribute.name}]"}
Expand All @@ -970,7 +970,7 @@ defmodule AshAdmin.Components.Resource.Form do

~H"""
<.input
type={text_input_type(@attribute)}
type={text_input_type(@form.source.resource, @attribute)}
placeholder={placeholder(@attribute.default)}
value={@value}
name={@name || @form.name <> "[#{@attribute.name}]"}
Expand Down Expand Up @@ -1076,8 +1076,17 @@ defmodule AshAdmin.Components.Resource.Form do

defp placeholder(_), do: nil

defp text_input_type(%{sensitive?: true}), do: "password"
defp text_input_type(_), do: "text"
defp text_input_type(resource, %{name: name, sensitive?: true}) do
show_sensitive_fields = AshAdmin.Resource.show_sensitive_fields(resource)

if Enum.member?(show_sensitive_fields, name) do
"text"
else
"password"
end
end

defp text_input_type(_, _), do: "text"

defp redirect_to(socket, record) do
if AshAdmin.Resource.show_action(socket.assigns.resource) do
Expand Down
3 changes: 2 additions & 1 deletion lib/ash_admin/components/resource/show.ex
Original file line number Diff line number Diff line change
Expand Up @@ -276,8 +276,9 @@ defmodule AshAdmin.Components.Resource.Show do

defp render_maybe_sensitive_attribute(assigns, resource, record, attribute) do
assigns = assign(assigns, attribute: attribute)
show_sensitive_fields = AshAdmin.Resource.show_sensitive_fields(resource)

if attribute.sensitive? do
if attribute.sensitive? && not Enum.member?(show_sensitive_fields, attribute.name) do
~H"""
<.live_component
id={"#{@record.id}-#{@attribute.name}"}
Expand Down
32 changes: 24 additions & 8 deletions lib/ash_admin/components/resource/table.ex
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ defmodule AshAdmin.Components.Resource.Table do
attr :prefix, :any, required: true
attr :skip, :list, default: []
attr :format_fields, :any, default: []
attr :show_sensitive_fields, :list, default: []
attr :actor, :any, default: nil

def table(assigns) do
Expand All @@ -30,7 +31,14 @@ defmodule AshAdmin.Components.Resource.Table do
<tbody>
<tr :for={record <- @data} class="border-b-2">
<td :for={attribute <- attributes(@resource, @attributes, @skip)} class="py-3">
<%= render_attribute(@api, record, attribute, @format_fields, @actor) %>
<%= render_attribute(
@api,
record,
attribute,
@format_fields,
@show_sensitive_fields,
@actor
) %>
</td>
<td :if={@actions && actions?(@resource)}>
<div class="flex h-max justify-items-center">
Expand Down Expand Up @@ -91,14 +99,21 @@ defmodule AshAdmin.Components.Resource.Table do
|> Enum.reject(&(&1.name in skip))
end

defp render_attribute(api, record, attribute, formats, actor) do
process_attribute(api, record, attribute, formats, actor)
defp render_attribute(api, record, attribute, formats, show_sensitive_fields, actor) do
process_attribute(api, record, attribute, formats, show_sensitive_fields, actor)
rescue
_ ->
"..."
end

defp process_attribute(api, record, %module{} = attribute, formats, actor)
defp process_attribute(
api,
record,
%module{} = attribute,
formats,
show_sensitive_fields,
actor
)
when module in [HasOne, BelongsTo] do
display_attributes = AshAdmin.Resource.relationship_display_fields(attribute.destination)

Expand All @@ -120,13 +135,13 @@ defmodule AshAdmin.Components.Resource.Table do
attributes = attributes(attribute.destination, display_attributes, [])

Enum.map_join(attributes, " - ", fn x ->
render_attribute(api, relationship, x, formats, actor)
render_attribute(api, relationship, x, formats, show_sensitive_fields, actor)
end)
end
end
end

defp process_attribute(_, record, %struct{} = attribute, formats, _actor)
defp process_attribute(_, record, %struct{} = attribute, formats, show_sensitive_fields, _actor)
when struct in [Ash.Resource.Attribute, Ash.Resource.Aggregate, Ash.Resource.Calculation] do
{mod, func, args} =
Keyword.get(formats || [], attribute.name, {Phoenix.HTML.Safe, :to_iodata, []})
Expand All @@ -136,14 +151,15 @@ defmodule AshAdmin.Components.Resource.Table do
|> Map.get(attribute.name)
|> (&apply(mod, func, [&1] ++ args)).()

if struct == Ash.Resource.Attribute && attribute.sensitive? do
if struct == Ash.Resource.Attribute && attribute.sensitive? &&
not Enum.member?(show_sensitive_fields, attribute.name) do
format_sensitive_value(data, attribute, record)
else
format_attribute_value(data, attribute)
end
end

defp process_attribute(_api, _record, _attr, _formats, _actor) do
defp process_attribute(_api, _record, _attr, _formats, _show_sensitive_fields, _actor) do
"..."
end

Expand Down
8 changes: 8 additions & 0 deletions lib/ash_admin/helpers.ex
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,14 @@ defmodule AshAdmin.Helpers do
|> Enum.map_join(" ", &String.capitalize/1)
end

def sensitive?(%Ash.Resource.Attribute{sensitive?: true}) do
true
end

def sensitive?(_) do
false
end

def short_description(nil), do: {:not_split, nil}

def short_description(description) do
Expand Down
9 changes: 9 additions & 0 deletions lib/ash_admin/resource/resource.ex
Original file line number Diff line number Diff line change
Expand Up @@ -84,6 +84,11 @@ defmodule AshAdmin.Resource do
resource_group: [
type: :atom,
doc: "The group in the top resource dropdown that the resource appears in."
],
show_sensitive_fields: [
type: {:list, :atom},
doc:
"The list of fields that should not be redacted in the admin UI even if they are marked as sensitive."
]
]
}
Expand Down Expand Up @@ -135,6 +140,10 @@ defmodule AshAdmin.Resource do
Spark.Dsl.Extension.get_opt(resource, [:admin], :resource_group, nil, true)
end

def show_sensitive_fields(resource) do
Spark.Dsl.Extension.get_opt(resource, [:admin], :show_sensitive_fields, [], true)
end

def actor?(resource) do
Spark.Dsl.Extension.get_opt(resource, [:admin], :actor?, false, true)
end
Expand Down

0 comments on commit 3a285e8

Please sign in to comment.