defmodule Spear.Acl do
@moduledoc """
A struct representing an access control list (ACL)
See the [Security guide](guides/security.md) for more information on ACLs
"""
@typedoc """
An access control list (ACL) type
See the [Security guide](guides/security.md) for more information on ACLs
ACLs may provide permissions for a single user/group or a list of
user/groups.
## Examples
iex> Spear.Acl.allow_all()
%Spear.Acl{
delete: "$all",
metadata_read: "$all",
metadata_write: "$all",
read: "$all",
write: "$all"
}
"""
@typedoc since: "1.3.0"
@type t :: %__MODULE__{
read: String.t() | [String.t()],
write: String.t() | [String.t()],
delete: String.t() | [String.t()],
metadata_read: String.t() | [String.t()],
metadata_write: String.t() | [String.t()]
}
@fields ~w[read write delete metadata_read metadata_write]a
defstruct @fields
@doc """
Produces an ACL that allows all users access to all resources
Note that clients that do not provide credentials at all fall under the
`$all` group.
## Examples
iex> Spear.Acl.allow_all()
%Spear.Acl{
delete: "$all",
metadata_read: "$all",
metadata_write: "$all",
read: "$all",
write: "$all"
}
"""
def allow_all do
struct(__MODULE__, Enum.zip(@fields, Stream.repeatedly(fn -> "$all" end)))
end
@doc """
Produces an ACL that only allows access to all resources to the `$admins`
group
## Examples
iex> Spear.Acl.admins_only()
%Spear.Acl{
delete: "$admins",
metadata_read: "$admins",
metadata_write: "$admins",
read: "$admins",
write: "$admins"
}
"""
def admins_only do
struct(__MODULE__, Enum.zip(@fields, Stream.repeatedly(fn -> "$admins" end)))
end
@doc """
Converts an ACL struct to a map with the keys expected by the EventStoreDB
This function is used internally by `Spear.set_global_acl/4` to create a
global ACL event body, but may be used to create an acl body on its own.
## Examples
iex> Spear.Acl.allow_all() |> Spear.Acl.to_map()
%{
"$w" => "$all",
"$r" => "$all",
"$d" => "$all",
"$mw" => "$all",
"$mr" => "$all"
}
"""
@doc since: "0.1.3"
@spec to_map(t()) :: %{String.t() => String.t() | [String.t()]}
def to_map(%__MODULE__{} = acl) do
%{
"$w" => acl.write,
"$r" => acl.read,
"$d" => acl.delete,
"$mw" => acl.metadata_write,
"$mr" => acl.metadata_read
}
|> Enum.reject(fn {_k, v} -> v == nil end)
|> Enum.into(%{})
end
@doc false
def from_map(%{} = acl) do
%__MODULE__{
read: Map.get(acl, "$r"),
write: Map.get(acl, "$w"),
delete: Map.get(acl, "$d"),
metadata_read: Map.get(acl, "$mr"),
metadata_write: Map.get(acl, "$mw")
}
end
end