Is it possible to convert policies expression to Ecto expressions?
Kinda of a weird request but I'm wondering if it's possible to resolve the policies expressions so I can reuse them in a regular Ecto query.
I started with this code but I'm new to Ash and a bit lost here:
I'm aware that's private but that would ease the migration process. I can give more context if needed.
43 Replies
Does it have to be just the relevant policy expressions?
It might be worth stepping back and figuring out what the main thing you're trying to accomplish is
Hey @Zach Daniel
So we have to add some new permissions in a bunch of existing contexts and ecto queries. Migrating to Ash resources and
actions
is not viable at the moment so the idea I'm trying to validate here is creating resources with attributes and the policies we need then extract out the underlying ecto conditions to use in those existing ecto queries. I know that's not the usual use-case but I found Ash Authz to have the best engine compared to other libraries and we could migrate to actions
gradually
But I'm still learning how policies work so that might not even be doableGotcha 🤔 I mean, its likely to be possible, but YMMV
There are some easier ways to go about it though
that might work better
The first thing that comes to mind is to use
Ash.data_layer_query(query)
which will give you a query back you can run
Which will have policies and any other rules included
You could then take that query and use Repo.all
etc.the problem is that I don't have a Ash.Query
I do have instead regular Ecto queries in a context
You could start your queries w/ the Ash query
like:
sorry my initial example was misleading
let me give you a better example
suppose we're introducing a new permission system wide to check if "something" is owned by current user like so:
the actual permissions are way more complex and we're talking about a lot of functions
so the idea was to define Ash resources to leverage its Authz engine like so:
but then we'd need a way to apply those policies in raw ecto queries:
So you wouldn't have one resource per table? Its just in the abstract in some way?
mostly one resource per table but I don't think that's a problem?
the thing is... I've considered bodyguard, let_me, permit, etc, etc and found Ash
policies
to be better but migrating all those context functions/queries to Ash actions (to use policies
automatically) is quite inviable at the moment so I'm trying to leverage only policies while keeping the original Ecto queries, does it make sense? 😄Yeah it makes sense I'm just trying to find a reasonable way to arrive at that point
Try
Ash.DataLayer.filter
instead of this: AshSql.Filter.add_filter_expression(query, filter)
how can I convert a ecto query to ash?
Ah, right
you cannot
but
Ash.DataLayer.filter
doesn't take an Ash query
You may need to set it up though
Sorry, just not really a use case I'd considered, but I'm sure you can get thereno worries thanks for the help!
I know that's not usual lol
So, i know you are talking about not starting with an ash query, but ultimately i still think that you should, and you can then modify an existing query
so ash resources can take ecto queries? I think I saw something about it in the docs
Only kind of
well, no
I mean could I create actions without modifying the ecto queries?
Something like this may work
yeah but that would require writing the
actions
sorry my initial example was really misleading (was a piece of code I was testing and didn't realize it was not aligned with my question)
I don't have a query = Ash.Query.for_read(MyApp.Resource, :my_query, actor: actor)
because I don't have any actions
defined in that resourceWithout actions, you really won't be able to do this
if I could do something like:
that would be useful but I don't think that would work right
but just having actions doesn't mean that you have to call them
Actions are just descriptions
they can be used any numbe of ways
If you want to use policies, it is always going to be in the context of a resource and an action, you can't get around that part
gotcha
ultimately we'd have all resources and actions well defined but getting there is the problem
🤔 I don't see what the trouble is
if you can define policies
why can't you define a single read action to apply them to?
You don't have to define the whole world of possible actions
the new permissions the system must take are gonna be applied pretty much everywhere
thats all you need
with one read action on the resource, you can then apply policies to it
I have this situation:
- Context A
- Function A, B, C
- Context B
- Function A, B, C
and so on (each functions is an ecto query)
in order to use policies and actions I'd have to rewrite each one of them as action
for more context we already have a couple resources but the majority of the system is currently non-Ash
🤔
I don't understand why you have to do that
Do each of those functions have different policies?
Lets rephrase
You want to use Ash.Resource just for its policies, right?
yep
So you're going to have to define a resource
Right?
y
What is an example of one such policy you might write on the resource? or rather, what would that resource look like in your perfect world?
something along these lines (there are a couple other rules but the point is that those rules are being created now and need to be applied pretty much everywhere)
the reason I'm trying to use Ash policies is due to its capability to enforce permissions on relationships, fields, the
can?
functions, enforcing data security, and so on
note that those rules should be applied to 10 different resources and those rules do not exist yet (that's what I'm trying to achieve)
so what I'm getting here is that in order to use those policies
I'd have to rewrite existing ecto queries (thousands of lines) as ash actions, right?
some queries are dead simple but many of them are composed of fragments, dynamic parts, joins, etc, etc
in other words, the options seems to be:
1) use bodyguard, let_me, or permit instead of Ash authz to define and enforce those new permissions in existing contexts, queries, templates
2) use Ash Authz (policies) but need to write Ash resources and rewrite Ecto queries as Ash actionsNo
You don't have to rewrite all those ecto queries to Ash actions
well......
You want to use some pretty advanced stuff
like
accessing_from
that requires a context to be set about where you are loading the data from
but let me jsut make my point regardless and maybe it will get you where you want to goit might need more complex constructs but so far the most complex one is
accessing_from
With a resource like that, and a helper like this:
You now have a setup that can apply those Ash policies to any given ecto query
There would probably be some kinks to workout with that pattern
But it could be done
For
accessing_from
though, I'm not sure how you're expecting taht to work. Are you using like Repo.preload
?accessing_from
here is used to allow accessing data from a parent resourceAh, so thats not how it works in Ash
Accessing from is set when you load from a source resource
If you want to allow accessing from a parent resource you'd use an expression policy for example
i.e
authorize_if expr(parent_id == ...)
I could be wrong tho
that specific line wasn't written by me
but thanks I'm gonna double check
did an initial pass with
authorize_as_ash_action
and it did work with a simple forbid_if always()
🔥
actually I think it's not working 🤔
The first test is passing but the second one fails. In the second test I see:
the error contains:
changing to:
fails the first test and the second one pass so I might be using policies incorrectly here
The expressions should result in a modified query though, not in a forbidden error
oh, yeah
you're using it wrong in that first example
make sure to read the policies guide
policies default to forbidden, and checks apply from top to bottom
something has to produce an authorized result for each policy, which
forbid_unless
cannot do
and each policy that applies to a request has to passhey Zach thanks for the support! I think I have enough to continue here now
🥳
LMK if you run into any issues
We can also likely enhance this and make it a core tool at some point
like
authorize_data_layer_query