How to programatically add a policy to a resource?

I might be missing something here related to Spark, but how do I programatically add a policy? (e.g. using a Spark extension)
def transform(dsl_state) do
{:ok, policy} =
Transformer.build_entity(Ash.Policy.Authorizer, [:policies], :bypass,
condition: Ash.Policy.Check.Builtins.always(),
description: "Admins bypass all checks"
)

{:ok, check} =
Transformer.build_entity(Ash.Policy.Authorizer, [:policies, :bypass], :authorize_if,
check: auth_if_admin
)
# how do I add `check` to `policy`???
end
def transform(dsl_state) do
{:ok, policy} =
Transformer.build_entity(Ash.Policy.Authorizer, [:policies], :bypass,
condition: Ash.Policy.Check.Builtins.always(),
description: "Admins bypass all checks"
)

{:ok, check} =
Transformer.build_entity(Ash.Policy.Authorizer, [:policies, :bypass], :authorize_if,
check: auth_if_admin
)
# how do I add `check` to `policy`???
end
6 Replies
quartz
quartzOP2y ago
have tried something along the lines of:
{:ok, policy} = Transformer.add_entity(policy, [], check)
{:ok, policy} = Transformer.add_entity(policy, [:policies], check)
%{ policy | policies: [check] }
{:ok, policy} = Transformer.add_entity(policy, [], check)
{:ok, policy} = Transformer.add_entity(policy, [:policies], check)
%{ policy | policies: [check] }
as well
ZachDaniel
ZachDaniel2y ago
the strategy you're using is essentially the correct approach but Transformer.add_entity for example is not used for nested entities, only for adding them to the DSL Probably the best approach would be for us to implement a utility for this in Ash.Resource.Builder, and there is inspiration there for handling nested entities
@doc """
Builds and adds an action
"""
@spec add_action(
Spark.Dsl.Builder.input(),
type :: Ash.Resource.Actions.action_type(),
name :: atom,
opts :: Keyword.t()
) ::
Spark.Dsl.Builder.result()
defbuilder add_action(dsl_state, type, name, opts \\ []) do
with {:ok, action} <- build_action(type, name, opts) do
Transformer.add_entity(dsl_state, [:actions], action)
end
end

@doc """
Builds an action
"""
@spec build_action(
type :: Ash.Resource.Actions.action_type(),
name :: atom,
opts :: Keyword.t()
) ::
{:ok, Ash.Resource.Actions.action()} | {:error, term}
def build_action(type, name, opts \\ []) do
with {:ok, opts} <-
handle_nested_builders(opts, [:changes, :arguments, :metadata, :pagination]) do
Transformer.build_entity(
Ash.Resource.Dsl,
[:actions],
type,
Keyword.merge(opts, name: name)
)
end
end
@doc """
Builds and adds an action
"""
@spec add_action(
Spark.Dsl.Builder.input(),
type :: Ash.Resource.Actions.action_type(),
name :: atom,
opts :: Keyword.t()
) ::
Spark.Dsl.Builder.result()
defbuilder add_action(dsl_state, type, name, opts \\ []) do
with {:ok, action} <- build_action(type, name, opts) do
Transformer.add_entity(dsl_state, [:actions], action)
end
end

@doc """
Builds an action
"""
@spec build_action(
type :: Ash.Resource.Actions.action_type(),
name :: atom,
opts :: Keyword.t()
) ::
{:ok, Ash.Resource.Actions.action()} | {:error, term}
def build_action(type, name, opts \\ []) do
with {:ok, opts} <-
handle_nested_builders(opts, [:changes, :arguments, :metadata, :pagination]) do
Transformer.build_entity(
Ash.Resource.Dsl,
[:actions],
type,
Keyword.merge(opts, name: name)
)
end
end
If you were interested, you could PR an add_policy 🙂 Just keep in mind, the order of policies actually matters. if they have a bypass on the resource, and your policy is added to the end, then the bypass will bypass your added policies. But if you add your policy onto the beginning of the list, then your policy will trump the bypass 🙂
barnabasj
barnabasj2y ago
we created our own little extension for this: https://github.com/traveltechdeluxe/ash-rbac
GitHub
GitHub - traveltechdeluxe/ash-rbac
Contribute to traveltechdeluxe/ash-rbac development by creating an account on GitHub.
ZachDaniel
ZachDaniel2y ago
🔥 🔥 🔥
natterstefan
natterstefan2y ago
🔥 🙂
quartz
quartzOP2y ago
@barnabasj Haha nice timing ❤️ 🔥

Did you find this page helpful?