Packages
ash
2.19.13
3.29.3
3.29.2
3.29.1
3.29.0
3.28.0
3.27.8
3.27.7
3.27.6
3.27.5
3.27.4
3.27.3
3.27.2
3.27.1
3.27.0
3.26.0
3.25.2
3.25.1
3.25.0
retired
3.24.7
3.24.6
3.24.5
3.24.4
3.24.3
3.24.2
3.24.1
3.24.0
3.23.1
3.23.0
3.22.2
3.22.1
3.22.0
3.21.3
3.21.2
3.21.1
3.21.0
3.20.0
3.19.3
3.19.2
3.19.1
3.19.0
3.18.0
3.17.1
3.17.0
3.16.0
3.15.0
3.14.1
3.14.0
retired
3.13.2
3.13.1
3.13.0
3.12.0
3.11.3
3.11.2
3.11.1
3.11.0
3.10.1
3.10.0
3.9.0
3.8.0
3.7.6
3.7.5
3.7.4
3.7.3
3.7.2
3.7.1
3.7.0
retired
3.6.3
retired
3.6.2
3.6.1
3.6.0
3.5.43
3.5.42
3.5.41
3.5.40
3.5.39
3.5.38
3.5.37
3.5.36
3.5.35
3.5.34
3.5.33
3.5.32
3.5.31
3.5.30
3.5.29
3.5.28
3.5.27
3.5.26
3.5.25
3.5.24
3.5.23
3.5.22
3.5.21
3.5.20
3.5.19
3.5.18
3.5.17
3.5.16
3.5.15
3.5.14
3.5.13
3.5.12
3.5.11
3.5.10
3.5.9
3.5.8
3.5.7
3.5.6
3.5.5
3.5.4
3.5.3
3.5.2
3.5.1
3.5.0
3.4.74
retired
3.4.73
3.4.72
3.4.71
3.4.70
3.4.69
3.4.68
3.4.67
3.4.66
3.4.65
3.4.64
3.4.63
3.4.62
3.4.61
3.4.60
3.4.59
3.4.58
3.4.57
3.4.56
3.4.55
3.4.54
3.4.53
3.4.52
3.4.51
3.4.50
3.4.49
3.4.48
3.4.47
3.4.46
3.4.45
3.4.44
3.4.43
3.4.42
3.4.41
3.4.40
3.4.39
3.4.38
3.4.37
3.4.36
3.4.35
3.4.34
3.4.33
3.4.32
3.4.31
3.4.30
3.4.29
3.4.28
3.4.27
3.4.26
3.4.25
3.4.24
3.4.23
3.4.22
3.4.21
3.4.20
3.4.19
3.4.18
3.4.17
3.4.16
3.4.15
3.4.14
3.4.13
3.4.12
3.4.11
3.4.10
3.4.9
3.4.8
3.4.7
3.4.6
3.4.5
3.4.4
3.4.3
3.4.2
3.4.1
3.4.0
3.3.3
3.3.2
3.3.1
3.3.0
3.2.6
3.2.5
3.2.4
3.2.3
3.2.2
3.2.1
3.2.0
3.1.8
3.1.7
3.1.6
3.1.5
3.1.4
3.1.3
3.1.2
3.1.1
3.1.0
3.0.16
3.0.15
3.0.14
3.0.13
3.0.12
3.0.11
3.0.10
3.0.9
3.0.8
3.0.7
3.0.6
3.0.5
3.0.4
3.0.3
3.0.2
3.0.1
3.0.0
3.0.0-rc.46
3.0.0-rc.45
3.0.0-rc.44
3.0.0-rc.43
3.0.0-rc.42
3.0.0-rc.41
3.0.0-rc.40
3.0.0-rc.39
3.0.0-rc.38
3.0.0-rc.37
3.0.0-rc.36
3.0.0-rc.35
3.0.0-rc.34
3.0.0-rc.33
3.0.0-rc.32
3.0.0-rc.31
3.0.0-rc.29
3.0.0-rc.28
3.0.0-rc.27
3.0.0-rc.26
3.0.0-rc.25
3.0.0-rc.24
3.0.0-rc.23
3.0.0-rc.22
3.0.0-rc.21
3.0.0-rc.20
3.0.0-rc.19
3.0.0-rc.18
3.0.0-rc.17
3.0.0-rc.16
3.0.0-rc.15
3.0.0-rc.14
3.0.0-rc.13
3.0.0-rc.12
3.0.0-rc.11
3.0.0-rc.10
3.0.0-rc.9
3.0.0-rc.8
3.0.0-rc.7
3.0.0-rc.6
3.0.0-rc.5
3.0.0-rc.4
3.0.0-rc.3
3.0.0-rc.1
3.0.0-rc.0
2.21.15
2.21.14
2.21.13
2.21.12
2.21.11
2.21.10
2.21.9
2.21.8
2.21.7
2.21.6
2.21.5
2.21.4
2.21.3
2.21.2
2.21.1
2.21.0
2.20.3
2.20.2
2.20.1
2.20.0
2.19.14
2.19.13
2.19.12
2.19.11
2.19.10
2.19.9
2.19.8
2.19.7
2.19.6
2.19.5
2.19.4
2.19.3
retired
2.19.2
retired
2.19.1
retired
2.19.0
retired
2.18.2
2.18.1
2.18.0
2.17.24
2.17.23
2.17.22
2.17.21
2.17.20
2.17.19
2.17.18
2.17.17
2.17.16
2.17.15
2.17.14
2.17.13
2.17.12
2.17.11
2.17.10
2.17.9
2.17.8
2.17.7
2.17.6
2.17.5
2.17.4
2.17.3
2.17.2
2.17.1
2.17.0
2.16.1
2.16.0
2.15.20
2.15.19
2.15.18
2.15.17
2.15.16
2.15.15
2.15.14
2.15.13
2.15.12
2.15.11
2.15.10
2.15.9
2.15.8
2.15.7
2.15.6
2.15.5
2.15.4
2.15.2
2.15.1
2.15.0
2.14.21
2.14.20
2.14.19
2.14.18
2.14.17
2.14.16
2.14.15
2.14.14
2.14.13
2.14.12
2.14.11
2.14.10
2.14.9
2.14.8
2.14.7
2.14.6
2.14.5
2.14.4
2.14.3
2.14.2
2.14.1
2.14.0
2.13.4
retired
2.13.3
2.13.2
2.13.1
2.13.0
2.12.1
2.12.0
2.11.11
2.11.10
2.11.9
2.11.8
2.11.7
2.11.6
2.11.5
2.11.4
2.11.3
2.11.2
2.11.1
2.11.0
2.11.0-rc.3
2.11.0-rc.2
2.11.0-rc.1
2.11.0-rc.0
2.10.2
2.10.1
2.10.0
2.9.29
2.9.28
2.9.27
2.9.26
2.9.25
2.9.24
2.9.23
2.9.22
2.9.21
2.9.20
2.9.19
2.9.18
2.9.17
2.9.16
2.9.15
2.9.14
2.9.13
2.9.12
2.9.11
2.9.10
2.9.9
2.9.8
2.9.7
2.9.6
2.9.5
2.9.4
2.9.3
2.9.2
2.9.1
2.9.0
2.8.1
2.8.0
2.7.1
2.7.0
2.6.31
2.6.30
2.6.29
2.6.28
2.6.27
2.6.26
2.6.25
2.6.24
2.6.23
2.6.22
2.6.21
2.6.20
2.6.19
2.6.18
2.6.17
2.6.16
2.6.15
2.6.14
2.6.13
2.6.11
2.6.10
2.6.9
2.6.8
2.6.7
2.6.6
2.6.5
2.6.4
2.6.3
2.6.2
2.6.1
2.6.0
2.5.16
2.5.15
2.5.14
2.5.13
2.5.12
2.5.11
2.5.10
2.5.9
2.5.8
2.5.7
2.5.6
2.5.5
2.5.4
2.5.3
2.5.2
2.5.1
2.5.0
2.5.0-rc.6
2.5.0-rc.5
2.5.0-rc.4
2.5.0-rc.3
2.5.0-rc.2
2.5.0-rc.1
2.5.0-rc.0
2.4.30
2.4.29
2.4.28
2.4.27
2.4.26
2.4.25
2.4.24
2.4.23
2.4.22
2.4.21
2.4.20
2.4.19
2.4.18
2.4.17
2.4.16
2.4.15
2.4.14
2.4.13
2.4.12
2.4.11
2.4.10
2.4.9
2.4.8
2.4.7
2.4.6
2.4.5
2.4.4
2.4.3
2.4.2
2.4.1
2.4.0
2.3.0
2.2.0
2.1.0
2.0.0
2.0.0-rc.15
2.0.0-rc.14
2.0.0-rc.13
2.0.0-rc.12
2.0.0-rc.11
2.0.0-rc.10
2.0.0-rc.9
2.0.0-rc.8
2.0.0-rc.7
2.0.0-rc.6
2.0.0-rc.5
2.0.0-rc.4
2.0.0-rc.3
2.0.0-rc.2
2.0.0-rc.1
2.0.0-rc.0
2.0.0-pre.8
2.0.0-pre.7
2.0.0-pre.6
2.0.0-pre.5
2.0.0-pre.4
2.0.0-pre.3
2.0.0-pre.2
2.0.0-pre.1
2.0.0-pre.0
1.53.3
1.53.2
1.53.0
1.52.0-rc.22
1.52.0-rc.21
1.52.0-rc.20
1.52.0-rc.19
1.52.0-rc.18
1.52.0-rc.17
1.52.0-rc.16
1.52.0-rc.15
1.52.0-rc.14
1.52.0-rc.13
1.52.0-rc.12
1.52.0-rc.11
1.52.0-rc.10
1.52.0-rc.9
1.52.0-rc.8
1.52.0-rc.7
1.52.0-rc.6
1.52.0-rc.5
1.52.0-rc.4
1.52.0-rc.3
1.52.0-rc.2
1.52.0-rc.1
1.52.0-rc.0
1.51.2
1.51.1
retired
1.51.0
1.50.21
1.50.20
1.50.19
1.50.18
1.50.17
1.50.16
1.50.15
1.50.14
1.50.13
1.50.12
1.50.11
1.50.10
1.50.9
1.50.8
1.50.7
1.50.6
1.50.5
1.50.4
1.50.3
1.50.2
1.50.1
1.50.0
1.49.0
1.48.0-rc.30
1.48.0-rc.29
1.48.0-rc.28
1.48.0-rc.27
1.48.0-rc.26
1.48.0-rc.25
1.48.0-rc.24
1.48.0-rc.23
1.48.0-rc.22
1.48.0-rc.21
1.48.0-rc.20
1.48.0-rc.19
1.48.0-rc.18
1.48.0-rc.17
1.48.0-rc.16
1.48.0-rc.15
1.48.0-rc.14
1.48.0-rc.13
1.48.0-rc.12
1.48.0-rc.11
1.48.0-rc.10
1.48.0-rc.9
1.48.0-rc.8
1.48.0-rc.7
1.48.0-rc.6
1.48.0-rc.5
1.48.0-rc.4
1.48.0-rc.3
1.48.0-rc.2
1.48.0-rc.1
1.48.0-rc.0
1.47.12
1.47.11
1.47.10
1.47.9
1.47.8
1.47.7
1.47.6
1.47.5
1.47.4
1.47.3
1.47.2
1.47.1
1.47.0
1.46.13
1.46.12
1.46.11
1.46.10
1.46.9
1.46.8
1.46.7
1.46.6
1.46.5
1.46.4
1.46.3
1.46.2
1.46.1
1.46.0
1.45.0-rc9
1.45.0-rc8
1.45.0-rc7
1.45.0-rc6
1.45.0-rc5
1.45.0-rc4
1.45.0-rc3
1.45.0-rc20
1.45.0-rc2
1.45.0-rc19
1.45.0-rc18
1.45.0-rc17
1.45.0-rc16
1.45.0-rc15
1.45.0-rc14
1.45.0-rc13
1.45.0-rc12
1.45.0-rc11
1.45.0-rc10
1.45.0-rc1
1.45.0-rc0
1.44.13
1.44.12
1.44.11
1.44.10
1.44.9
1.44.8
1.44.7
1.44.6
1.44.5
1.44.4
1.44.3
1.44.2
1.44.1
1.44.0
1.43.12
1.43.11
1.43.10
1.43.9
1.43.8
1.43.7
1.43.6
1.43.5
1.43.4
1.43.3
1.43.2
1.43.1
1.43.0
1.42.0
1.41.12
1.41.11
1.41.10
1.41.9
1.41.8
1.41.7
1.41.6
1.41.5
1.41.4
1.41.3
1.41.2
1.41.1
1.41.0
1.40.0
1.39.7
1.39.6
1.39.5
1.39.4
1.39.3
1.39.2
1.39.1
1.39.0
1.38.0
1.37.2
1.37.1
1.37.0
1.36.22
1.36.21
1.36.19
1.36.18
1.36.17
1.36.16
1.36.15
1.36.14
1.36.13
1.36.12
1.36.11
1.36.10
1.36.9
1.36.8
1.36.7
1.36.6
1.36.5
1.36.4
1.36.3
1.36.2
1.36.0
1.35.1
1.35.0
1.34.9
1.34.8
1.34.7
1.34.6
1.34.5
1.34.4
1.34.3
1.34.2
1.34.1
1.34.0
1.33.0
1.32.2
1.32.1
1.32.0
1.31.1
1.31.0
1.30.2
1.30.1
1.29.0-rc1
1.29.0-rc0
1.28.1
1.28.0
1.27.1
1.27.0
1.26.13
1.26.12
1.26.11
1.26.10
1.26.9
1.26.8
1.26.7
1.26.6
1.26.5
1.26.4
1.26.2
1.26.1
1.26.0
1.25.8
1.25.7
1.25.6
1.25.5
1.25.4
1.25.3
1.25.2
1.25.1
1.25.0
1.24.2
1.24.1
1.24.0
1.23.3
1.23.2
1.23.1
1.23.0
1.22.1
1.22.0
1.20.1
1.20.0
1.19.1
1.19.0
1.18.1
1.18.0
1.17.1
1.17.0
1.16.2
1.15.1
1.15.0
1.14.0
1.13.4
1.13.3
1.13.2
1.13.1
1.13.0
1.12.0
1.11.1
1.11.0
1.10.0
1.9.0
1.8.0
1.7.0
1.6.8
1.6.7
1.6.6
1.6.5
1.6.4
1.6.3
1.6.2
1.6.1
1.6.0
1.5.1
1.5.0
1.4.1
1.4.0
1.3.1
1.3.0
1.2.1
1.2.0
1.1.3
1.1.2
1.1.0
1.0.3
1.0.2
1.0.1
1.0.0
0.13.1
0.13.0
0.12.0
0.10.0
0.9.1
0.9.0
0.8.0
0.7.0
0.6.5
0.6.4
0.6.3
0.6.2
0.6.1
0.6.0
0.5.2
0.5.1
0.5.0
0.4.0
0.3.0
0.2.0
0.1.9
0.1.8
0.1.3
0.1.1
0.1.0
A declarative, extensible framework for building Elixir applications.
Security advisory:
This version has known vulnerabilities.
View advisories
Current section
Files
Jump to
Current section
Files
lib/ash/policy/chart/mermaid.ex
defmodule Ash.Policy.Chart.Mermaid do
@moduledoc "Generates policy mermaid charts"
def chart(resource) do
policies = Ash.Policy.Info.policies(resource)
policy_count = Enum.count(policies)
at_least_one_policy =
Enum.flat_map(policies, fn policy ->
case Enum.reject(List.wrap(policy.condition), fn
%{check_module: Ash.Policy.Check.Static, check_opts: opts} ->
opts[:result] == true
{Ash.Policy.Check.Static, opts} ->
opts[:result] == true
_ ->
false
end) do
[] ->
[]
conditions ->
conditions
|> Enum.map(fn condition ->
{mod, opts} =
case condition do
%{module: module, opts: opts} ->
{module, opts}
{module, opts} ->
{module, opts}
end
mod.describe(opts)
end)
|> Enum.intersperse(" and ")
|> Enum.join()
|> List.wrap()
end
end)
|> case do
[one] ->
one
[] ->
nil
multiple ->
Enum.map_join(multiple, "\nor ", fn one ->
if String.contains?(one, " and ") do
"(#{one})"
else
one
end
end)
end
at_least_one_policy =
if at_least_one_policy do
"""
subgraph at least one policy applies
direction TB
at_least_one_policy[#{quote_and_escape(at_least_one_policy)}]
end
at_least_one_policy--False-->Forbidden
at_least_one_policy--True-->0_conditions
"""
end
policy_text =
policies
|> Enum.with_index()
|> Enum.map_join("\n", fn {%{condition: conditions, policies: checks} = policy,
policy_index} ->
conditions = List.wrap(conditions)
# remove `always()` here
condition_text =
conditions
|> Enum.map_join(" and ", fn condition ->
{mod, opts} =
case condition do
%{module: module, opts: opts} ->
{module, opts}
{module, opts} ->
{module, opts}
end
mod.describe(opts)
end)
last_policy? = policy_index == policy_count - 1
next_policy = "#{policy_index + 1}_conditions"
false_destination =
if last_policy? do
"Authorized"
else
next_policy
end
conditions =
"""
#{policy_index}_conditions{#{quote_and_escape(condition_text)}}
#{policy_index}_conditions--True-->#{policy_index}_checks_0
"""
conditions =
conditions <> "\n" <> "#{policy_index}_conditions--False-->#{false_destination}"
checks = List.wrap(checks)
check_count = Enum.count(checks)
checks_text =
checks
|> Enum.with_index()
|> Enum.map_join("\n", fn {check, check_index} ->
next_policy_or_authorized =
if last_policy? do
"Authorized"
else
next_policy
end
next_check = "#{policy_index}_checks_#{check_index + 1}"
next_check_or_forbidden =
if check_index == check_count - 1 do
"Forbidden"
else
next_check
end
next_check_or_next_policy_or_end =
cond do
check_index != check_count - 1 ->
next_check
last_policy? ->
nil
true ->
next_policy
end
{true_dest, false_dest} =
case check.type do
:authorize_if ->
if policy.bypass? do
{"Authorized", next_policy_or_authorized}
else
{next_policy_or_authorized, next_check_or_forbidden}
end
:forbid_if ->
forbidden =
if policy.bypass? do
next_policy_or_authorized
else
"Forbidden"
end
{forbidden, next_check_or_next_policy_or_end}
:authorize_unless ->
if policy.bypass? do
{"Authorized", next_policy_or_authorized}
else
{next_policy_or_authorized, next_check_or_forbidden}
end
:forbid_unless ->
forbidden =
if policy.bypass? do
next_policy_or_authorized
else
"Forbidden"
end
{next_check_or_next_policy_or_end, forbidden}
end
true_path = "#{policy_index}_checks_#{check_index}--True-->#{true_dest}"
false_path =
if false_dest do
"#{policy_index}_checks_#{check_index}--False-->#{false_dest}"
end
description = check.check_module.describe(check.check_opts)
description =
if description && description != "" do
"{#{quote_and_escape(description)}}"
else
"{}"
end
"""
#{policy_index}_checks_#{check_index}#{description}
#{true_path}
#{false_path}
"""
|> String.trim()
end)
description =
if policy.description && policy.description != "" do
"Policy #{policy_index + 1}[#{policy.description}]"
else
"Policy #{policy_index + 1}"
end
{nodes, rules} =
"""
#{conditions}
#{checks_text}
"""
|> String.split("\n")
|> Enum.split_with(fn thing ->
is_node?(thing)
end)
"""
subgraph #{description}
direction TB
#{Enum.join(nodes, "\n")}
end
#{Enum.join(rules, "\n")}
"""
end)
"""
flowchart TB
#{at_least_one_policy}
#{policy_text}
subgraph results[Results]
Authorized([Authorized])
Forbidden([Forbidden])
end
"""
|> remove_always_links()
|> collapse_constant_nodes()
|> remove_empty_subgraphs()
end
defp remove_empty_subgraphs(lines) do
lines
|> String.split("\n")
|> Enum.reject(fn line ->
String.trim(line) == ""
end)
|> do_remove_empty_subgraphs([])
|> Enum.join("\n")
end
defp do_remove_empty_subgraphs([], acc), do: Enum.reverse(acc)
defp do_remove_empty_subgraphs(["subgraph " <> _, "direction TB", "end" | rest], acc) do
do_remove_empty_subgraphs(rest, acc)
end
defp do_remove_empty_subgraphs([item | rest], acc) do
do_remove_empty_subgraphs(rest, [item | acc])
end
defp collapse_constant_nodes(text) do
lines = String.split(text, "\n")
Enum.find_value(lines, fn line ->
if is_node?(line) do
case String.split(line, "{\"") do
[node_id | _] ->
true_to = find_branch_to(lines, node_id, "True")
false_to = find_branch_to(lines, node_id, "False")
if true_to && false_to && true_to == false_to do
phrase =
if false_to in ["Authorized", "Forbidden"] do
""
else
"Or"
end
{node_id, true_to, phrase}
end
_ ->
nil
end
end
end)
|> case do
nil ->
text
{from, to, phrase} ->
lines
|> delete_branches(from, to)
|> Enum.concat(["#{from}--#{phrase}-->#{to}"])
|> Enum.join("\n")
|> collapse_constant_nodes()
end
end
defp delete_branches(lines, from, to) do
Enum.reject(lines, fn line ->
line in ["#{from}--True-->#{to}", "#{from}--False-->#{to}"]
end)
end
defp is_node?(line) do
not (String.contains?(line, "--True-->") or String.contains?(line, "--False-->")) &&
String.contains?(line, "{\"")
end
defp quote_and_escape(text) do
"\"#{escape(text)}\""
end
defp escape(string) do
String.replace(string, "\"", "'")
end
defp remove_always_links(text) do
lines = String.split(text, "\n")
lines
|> Enum.find(fn string ->
String.contains?(string, "always true") || String.contains?(string, "always false")
end)
|> case do
nil ->
text
text ->
target_branch =
if String.contains?(text, "always true") do
"True"
else
"False"
end
node_id = text |> String.split("{") |> Enum.at(0)
lines
|> Enum.reduce([], fn line, acc ->
case from_and_to(line) do
{from, target, to} ->
if to == node_id do
true_to = find_branch_to(lines, node_id, target_branch)
[build_line(target, from, true_to) | acc]
else
[line | acc]
end
nil ->
[line | acc]
end
end)
|> Enum.reject(fn line ->
line == text || String.starts_with?(line, "#{node_id}--") ||
String.ends_with?(line, "-->#{node_id}")
end)
|> Enum.reverse()
|> Enum.join("\n")
|> remove_always_links()
end
end
defp find_branch_to(lines, id, target) do
Enum.find_value(lines, fn line ->
if String.starts_with?(line, "#{id}--#{target}-->") do
String.trim_leading(line, "#{id}--#{target}-->")
end
end)
end
defp build_line(target, from, to), do: "#{from}--#{target}-->#{to}"
defp from_and_to(line) do
cond do
String.contains?(line, "--True-->") ->
[from, to] = String.split(line, "--True-->")
{from, "True", to}
String.contains?(line, "--False-->") ->
[from, to] = String.split(line, "--False-->")
{from, "False", to}
true ->
nil
end
end
end