mmmrrr

mmmrrr

So I was thinking about the options we have on authorization (read: methods to find out what a user is allowed to do in our application). Of course I know about the plug / elixir based solutions like policy_wonk and canary, but I have the feeling that this is simply not the correct approach, given we have a database like postgres as the default choice for a datastore.

While researching about this topic I found PostgREST [1], a haskell library for auto generated REST apis based on a postgres dataschema. Long story short, with this package I miss a lot I take for granted with elixir and phoenix and it makes the deployment a lot more complicated. But I must say that I love how they solved the data access story.

The idea, as I understand it, is that every DB request is performed using different database roles with varying permission levels. That way you minimize impact if a rouge query manages to get through your defense. The next step is to check, on the database level, if the requesting user can see the specified row (i. e. there is the id of the owner embedded into the row). For an example please see [2]

The only similar thing I have seen in elixir land is mbuhot/ecto_row_level_security, which looks promising.

Question

So now on to the real questions:

  • Do you think it is a good approach, to let the database handle the authorization for your application?
  • Would you still rather use a dataloader library like canary?

Sources
[1] : PostgREST Documentation — PostgREST 14 documentation
[2] : postgrest-starter-kit/db/src/authorization/privileges.sql at master · subzerocloud/postgrest-starter-kit · GitHub

Showing Posts 1 to 9

tadasajon

tadasajon

I’m surprised this thread did not receive any comments. Row-level security seems like an elegant way to increase security while simplifying the elixir/phoenix code. Shouldn’t this be the default way all phoenix apps are built? Is there any good reason not to use it?

mmmrrr

mmmrrr OP

Actually, there is: you are bound to Postgres (or at least systems which implement such a concept), which makes it an ill fit for a lot of problems.

The main problem I saw back then (which was why I tried to gather some opinions here :wink:), was that you start coupling business logic with the datastore. This makes it - imho - completely inadequate for the usage with Ecto, which is an abstraction layer for database queries.

IMO there are two options one could implement in Elixir:

  1. Make a nice library which wraps all those details in a consumer friendly Elixir API and goes all in with Postgres and nothing else
  2. Do it like Hasura.io and implement an “SQL-compiler” which you can feed some DSL to express the rules that apply to a specific cell and then emits a query that fulfills all those rules

I started a sloppy implementation of the second back then but it is relatively complicated to get right - and it simply had no priority, so it went nowhere.

The first solution might be the easier of those two options but is hardly something that could be adopted in phoenix by default because the framework needs to also support SQLite, MariaDB and MSSQL out of the box.

dimitarvp

dimitarvp

Not sure but didn’t Supabase offer this? Recently they started publishing some of their code as open source so that could help, maybe.

tadasajon

tadasajon

I agree that this shouldn’t be the approach that Phoenix uses by default. I would compare using Postgres this way to using Tailwind – Phoenix shouldn’t default everyone into using Tailwind, but it seems to be the approach to CSS that many people in the the community agree is the best. You mention being “bound to Postgres” like it is a drawback, but many people see “access to Postgres” as a feature – we are talking about one of the most fully-featured and reliable database systems on offer – certainly more than adequate for over 90% or maybe over 99% of the scenarios that anyone would be using Phoenix for. In any case, there is a fair amount of commitment to a database that I guess I have come to expect – so, given that we’re making a commitment to Postgres anyway, why not make full use of it’s power and just go with row-level security? Building the app closer to the metal is always a good idea, other things being equal, it seems.

mmmrrr

mmmrrr OP

While I agree that Postgres is a fantastic database, support for features only it provides would drastically reduce the use cases where Phoenix can be utilized.

For example: every migration project that is not already using Postgres would be very complicated or not possible at all. Implementing authorization this way would also make it hard to have data readable from different stores: e. g. how do you make sure the user is authorized to access data in a redis store? Suddenly you have to check this in two places and you probably have two implementations to do so.

The comparison with tailwind is - in my opinion - lacking because opting out of it would be fairly easy. Opting out of a Postgres layer that not only provides the data access but also the authorization code is hairy because now you need to provide it by other means (like in the redis example above).

So as long as you’re using a framework that shall be useable for a variety of use cases/datastores you probably want to treat the store as “dumb” and something like Ecto seems to be a reasonable abstraction over all those different types of databases and it’s easy enough to write new adapters for it.

There are other frameworks like postgraphile, PostgREST and prest which use Postgres to it’s full potential and there are cases where this could be useful but in my mind this would be a different framework / library for data access that would be a replacement for Ecto. This should be completely possible using postgrex in Elixir. pRest (which I linked above) is nothing more but an implementation of PostgREST in Golang instead of Haskell - you could do that for Elixir and would probably use Phoenix as the web layer.

So all in all: PostgreSQL is fantastic but using something like Ecto, while having it’s own merits, will make compromises on features necessary.

mmmrrr

mmmrrr OP

Supabase is a clever combination of different “best in class” technologies: PostgreSQL as the datastore, PostgREST as the API layer, Phoenix for database change subscriptions, a storage layer / middleware in TypeScript utilizing AWS S3, GoTrue for authentication and Kong as a proxy.

While I like this approach of using “the right tool for the job” it is definitively harder to operate than a majestic monolith in Phoenix…

tadasajon

tadasajon

I’m interested in your thoughts on this @mmmrrr because the more I think about it the more it seems to make sense to me to build web apps directly in Postgres, particularly using Supabase.

You mention in your Nov/2021 post that the main problem you saw with using row-level security in Postgres is that when you take this approach you end up coupling business logic with the datastore.

From my perspective, business logic ought to be coupled with the datastore because this prevents data corruption and enforces business rules. So I would be interested in learning about some of the practical drawbacks you saw in taking this approach.

As far as I can tell, Supabase is just a Postgres database, so you can fire up a Phoenix app and connect to it just like you can connect a Phoenix app to any Postgres database. This means that you can use Ecto with Supabase from Phoenix because all Ecto needs is a database connection.

I don’t see a conflict between using row-level Postgres security and Supabase as the datastore for a Phoenix app – in fact, since auth is moved out of the Phoenix layer and into the datastore layer, it makes it easier to write multiple different front end clients, thereby making the application more flexible by allowing multitenency.

So the question is why use Phoenix at all if you’re just going to have Supabase handle the data and the API generation and the auth and the business logic and basically everything except the UI widgets on the front end? I think this is in fact a very good question, and it does seem to me that most people would just be better off building their app using Supabase and some lipstick to paint a face on it with.

You make the point in your Dec/2021 post that while you think Supabase uses “the right tool for the right job”, you think it is a more complicated setup to operate than a majestic monolith in Phoenix – but this assumes that a person building an app using Supabase wouldn’t just pay $25/month to have the Supabase people operate everything, which seems to me to be the sensible way to go.

So where does that leave Phoenix? I personally think it still makes sense use Phoenix LiveView as a front-end framework for Supabase, but the particular business concern would be the determining factor here – you’re going to want a BEAM program for situations in which you need massive concurrency, or a way to hold open long connections, for some particular aspect of the business application.

mmmrrr

mmmrrr OP

If you can afford it, then this is not a problem. If you have projects where you have different data stores, e. g. migration projects, or multiple projects with the same team but different databases you simply cannot use Postgres in any situation.

In my experience greenfield projects are just a tiny, tiny fraction of real world scenarios. Most of the time you will have to integrate some legacy data store.

Be careful to be very specific here: you’re moving authorization into the datastore, not authentication. For example Hasura has this problem because you need a companion service to perform the authentication. Supabase is doing a better job in that regard afaict.

This is indeed a good question and it comes down to taste and skills. Like everything in IT this is a game of tradeoffs: how flexible do I need to be in the frontend (i. e. “can I use the supabase client integrations for all use cases”)? How can I write custom functionality in the backend (i. e. to process incoming datastreams that cannot be expressed using standard features of you choosen platform)? Do I need server side rendering? Do I need to expose non standard APIs? Etc. etc.

So when you’re mostly a frontend engineer and mainly need a relatively simple CRUD API, Supabase, Hasura, SubZero and the likes are probably a smart way to build your app. And this is imho the target audience for all these tools. For real world applications it simply doesn’t matter so much because the complexity is simply shifted to another area (you can solve every problem I described above with those tools but it’s neither simple nor easy).

This is true until it isn’t. To quote the supabase website: $25/month + any additional usage. So this might get expensive very fast. Also depending on the project you maybe cannot afford a vendor lock-in, or you cannot let someone else handle your data. Supabase at least does offer enterprise and on premise support - but I’d assume this is also for enterprise pricing :smiley: Since it is open source you might get away with hosting it yourself but then the operational complexity strikes again.

I don’t think this would be wise. As mentioned Supabase et. al. are awesome when you’re a frontend engineer and cannot be bothered to write a backend. LiveView will not help you at all to consume the awesome APIs you just created since with LV you’re already working directly with the database - so there is no need for an HTTP-API in between.

All in all: it depends :slight_smile: But in my experience in nearly all real world scenerios those tools crumble and you start working around shortcomings with really ugly hacks. That’s why I try those tools every few month and everytime I decide “nope, that’s not for me” :smiley:

al2o3cr

al2o3cr

The simplest reason, IMO, is that there’s a finite amount of CPU available to the database. Spending that budget on stuff besides answering database queries is fine, until you need it for queries.

Historically, scaling databases (especially for writes) is significantly harder than scaling the less-stateful tiers in front of them.

— All posts loaded —

Where Next? Top

Trending in Questions Top

Blokh
Hey guys, I’ve got a huge CSV ( around 10 GB ) that needs to be processed hourly Do you guys have any suggestions what is the best prac...
New
kszambelanczyk
Hello! Could someone please give me a help/sample code, how to delete a file from s3 using waffle/waffle_ecto from Phoenix app. I creat...
New
RemyXRenard
I’m seeing that a list inside a Kino.DataTable will be interpreted as a charlist, even if the Kino.configure() is set to charlists: :as_l...
New
matt-savvy
Anyone here using Honeybadger? My Honeybadger account is being overwhelmed with noise from some bots. Seeing a lot of Bandit.HTTPError...
New
velrest
So my question is quite simple and i have found no conclusive answer on forum, google or AI. Should we use :erlang.float for Integer to ...
New
samoloth
Hi, I’ve just set up an application with ash_authentication. There is only magic link strategy for now, so there is no confirmation add o...
New
FlyingNoodle
If a change or preparation module uses Ash.Changeset.get_argument/2 or Ash.Query.get_argument/2 (or any of the other get_argument functio...
New

Other Trending Topics Top

mudasobwa
I am happy to introduce the very α version of the new programming language compiled to BEAM. Welcome Cure. It has literally three kille...
New
garrison
Hobbes is a low-level distributed database for the Elixir programming language. Hobbes provides a simple, safe, and scalable storage lay...
New
jimsynz
Beam Bots (or just BB for short) is a framework for building fault-tolerant robotics applications in Elixir using familiar OTP patterns. ...
New
mcass19
ExRatatui lets you cook up rich terminal UIs in Elixir, powered by Rust’s ratatui via Rustler NIFs. Build interactive terminal applicatio...
New
Damirados
Hello everyone. After busy few months I am happy to announce v0.1.0 of Emerge & Solve. They are GUI (Emerge) and State management (S...
New
netoum
Corex is an accessible, unstyled UI component library for Phoenix that integrates Zag.js state machines using Vanilla JavaScript and Live...
New

We're in Beta

About us Mission Statement

Options

Thread Display Mode




Thread Preview

Skip Thread Previews