-
Notifications
You must be signed in to change notification settings - Fork 0
/
rule.ex
75 lines (61 loc) · 1.73 KB
/
rule.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
defmodule Janus.Policy.Rule do
@moduledoc """
Defines a rule for an individual schema and action.
"""
@valid_clauses [:where, :where_not, :or_where]
defstruct [
:schema,
:action,
allow: [],
deny: []
]
@type t :: %__MODULE__{
schema: Janus.schema_module(),
action: Janus.action(),
allow: [keyword() | boolean()],
deny: [keyword() | boolean()]
}
@doc false
def new(schema, action) do
%__MODULE__{schema: schema, action: action}
end
@doc false
def apply_rule(rule, :deny, []), do: Map.merge(rule, %{allow: [], deny: [[]]})
def apply_rule(rule, field, opts) do
opts = parse_opts!(opts)
if [] in rule.deny do
rule
else
Map.update(rule, field, [opts], &[opts | &1])
end
end
@doc false
def merge(
%{schema: s, action: a} = rule,
%{schema: s, action: a, allow: allow, deny: deny}
) do
rule = Enum.reduce(allow, rule, &apply_rule(&2, :allow, &1))
rule = Enum.reduce(deny, rule, &apply_rule(&2, :deny, &1))
rule
end
defp parse_opts!(opts) do
with true <- Keyword.keyword?(opts),
[] <- opts |> Keyword.keys() |> Enum.uniq() |> Kernel.--(@valid_clauses) do
combine(opts)
else
opts when is_list(opts) -> invalid_opts!(opts)
_ -> invalid_opts!(opts)
end
end
defp invalid_opts!(value) do
raise ArgumentError, "invalid options passed to `allow` or `deny`: `#{inspect(value)}`"
end
defp combine(opts, acc \\ [])
defp combine([{:or_where, or_clause} | opts], acc) do
combine(opts, [{:or, {:where, or_clause}, acc}])
end
defp combine([clause | opts], acc) do
combine(opts, [clause | acc])
end
defp combine([], acc), do: Enum.reverse(acc)
end