-
-
Notifications
You must be signed in to change notification settings - Fork 181
/
add_missing_field_policies.ex
101 lines (79 loc) · 2.72 KB
/
add_missing_field_policies.ex
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
defmodule Ash.Policy.Authorizer.Transformers.AddMissingFieldPolicies do
@moduledoc "Adds field policies for any missing fields"
use Spark.Dsl.Transformer
import Spark.Dsl.Builder
alias Spark.Dsl.Transformer
def after?(Ash.Policy.Authorizer.Transformers.CacheFieldPolicyExpressions), do: false
def after?(_), do: true
def transform(dsl) do
non_pkey_fields =
dsl
|> Ash.Resource.Info.fields([:aggregates, :calculations, :attributes])
|> Enum.reject(fn
%{public?: false} ->
true
%{primary_key?: true} ->
true
_ ->
false
end)
|> Enum.map(& &1.name)
if Enum.empty?(Ash.Policy.Info.field_policies(dsl)) do
{:ok, dsl}
else
dsl
|> replace_asterisk(non_pkey_fields)
|> ensure_field_coverage(non_pkey_fields)
end
end
defbuilderp replace_asterisk(dsl, non_pkey_fields) do
field_policies = Ash.Policy.Info.field_policies(dsl)
Enum.reduce(field_policies, dsl, fn policy, dsl ->
if :* in policy.fields do
fields =
policy.fields
|> Enum.reject(&(&1 == :*))
|> Enum.concat(non_pkey_fields)
Transformer.replace_entity(dsl, [:field_policies], %{policy | fields: fields})
else
dsl
end
end)
end
defbuilderp ensure_field_coverage(dsl, non_pkey_fields) do
field_policies = Ash.Policy.Info.field_policies(dsl)
fields =
field_policies
|> Enum.flat_map(& &1.fields)
|> Enum.uniq()
invalid_fields = Enum.filter(fields, &(&1 not in non_pkey_fields))
missing_fields = Enum.filter(non_pkey_fields, &(&1 not in fields))
module = Transformer.get_persisted(dsl, :module)
unless Enum.empty?(invalid_fields) do
raise Spark.Error.DslError,
module: module,
path: [:field_policies],
message: """
Invalid field reference(s) in field policy: #{inspect(invalid_fields)}
Only non primary-key, public attributes, calculations and aggregates are supported.
"""
end
unless Enum.empty?(missing_fields) do
raise Spark.Error.DslError,
module: module,
path: [:field_policies],
message: """
Missing field reference(s) in field policies: #{inspect(missing_fields)}
If any field policies are present, *all* public, non-primary key fields must be accounted for.
To create a catch-all field policy that allows any fields that aren't covered
by other policies, use:
field_policy :* do
authorize_if always()
end
Keep in mind that all policies relevant to a given field must pass, so this will
not override other field policies.
"""
end
{:ok, dsl}
end
end