How to secure a dynamic filter in Ecto?

Hello everyone,

I am trying to insert a dynamic filter to an ecto query.

After importing Ecto.Query in the console, I was able to run those commands.

iex> {eval, _} = Code.eval_string("s.id", s: current_user)
iex> Entities.list_subjects_query |> (fn q -> 
  from o in q, where: fragment("(?).owner_id=(?)", o, ^eval) 
end).() |> Rbax.Repo.all

The idea is to filter dynamically a list, for the current user, on a list of resources, based on a filter, and bindings.

filter = "(?).owner_id=(?)"
bindings = "s.id"

For context, it is an RBAC system for Phoenix, where I would like to filter resources for a given user, this example is a rule for listing only resources owned by user.

While it is working, my question is how to secure this? Because using Code.eval_string is not safe, and fragment too.

I was thinking of parsing the filter and bindings with leex/yecc… Is there an easier way?

Thanks for taking time

NB: The rules are set by admin, and meant to be simples as checking attributes.

2 Likes

Let’s take a step back and evaluate options that don’t involve Code.eval_string, because even if you set aside security concerns, it’s still slow, hard to debug, and hacky.

What I think you want to do is have a way of defining filters that isn’t in Ecto syntax, and then a generic function that takes whatever data structure you have for filters and turns them into an ecto query as a nice pure operation.

As an aside, this reads more like an ABAC not an RBAC if you’re evaluating filters. Another option therefore would be to ditch trying to build an ABAC and actually do RBAC. For each operation, define the permissions you need to do the operation. Then check those permissions against the permissions provided by the current user’s role set or current role. If they match, you go ahead and do the operation, if not, you don’t. There isn’t any filtering of properties or bindings, that’s all ABAC stuff. You can create scoped RBAC where different users have different roles in different scopes, and it may be therefore relevant to figure out or filter the items that are available depending on scope. That doesn’t require arbitrary bindings and filters though. We looked into this pretty heavily at our company and decided a scoped RBAC would be enough, and that ABACs are universally a lot of work to build.

1 Like

Thank You for your answer,

It is RBAC trying to do ABAC too :slight_smile:

It’s role based, because you receive permissions when You join a role, on domain resource, in a given context.

When You receive a permission, it applies in a context, and the rights are correctly working.

But I am trying to extends it to filter what the user can see.

ABAC basically subsumes RBAC because you can treat the role as an attribute. Doing so is arguably easier cause then your ABAC engine has only one thing to worry about: attributes. Your API can treat the role attribute differently if you like.

Based on what? Arbitrarily complex rules? Or “is this thing in the context and with the right role”? For the latter thing we just wrote a bunch of helpers and made sure that all the public APIs routed through the helpers middleware(reasonably easy to do w/ GraphQL ).

Arbitrarily complex rules will require something with real syntax and a real parser. I looked around a fair bit at “sql like” query syntaxes I could adopt for the ABAC we were considering and didn’t really turn up anything. You could try to adopt one of the XPath style libraries, but it’s all a lot of work.

Have you looked at Postgres’s built in row based security for this?

1 Like

Yes, this is what I am looking

Did You find your solution?

Thanks for sharing your experience, this is the kind of problem You might have encounter when securing Absinthe GraphQL api. I hope this will be in version 2 of your book :slight_smile:

I need to support mysql too.

No, we gave up and decided to just do RBAC. We just have a large set of function clauses that match on an ecto schema and provide a query which scopes that kind of thing to the current user’s context. Then we do a permissions comparison. That way the actual permission system doesn’t need arbitrary fancy rules or bindings or anythings. The permission system only needs to do traditional RBAC stuff. We make it the job of the caller of the permissions system to figure out the scope, and we do that with normal elixir pattern matching / ecto query building.

2 Likes

Thank You for the help,

Although my question was on not using Code.eval_string, I am happy it gets into RBAC/ABAC.

I see it’s better to take the attribute approach, which solve the initial question.

I am using Dataloader in this project :slight_smile:

Perhaps this would work? https://github.com/ympons/expreso

EDIT: Specifically the parse / lex functions. Don’t use eval, a simple true / false won’t help you build an ecto query. Rather, you could have the rules written in the expreso syntax, and then use the expreso parser / lexer to get expreso AST, and then reduce that into an ecto query you build.

1 Like

That looks nice, it seems it’s what I need. I will test it and come back later.

Sorry to ask another question… is it possible now to use dataloader with relay connection? Or is it better not to use both together?

Thanks for your help, I appreciate… a lot :slight_smile: