DEV Community

Cover image for Building An Authorization Engine In Python...
gaborschulz
gaborschulz

Posted on • Edited on • Originally published at blog.gaborschulz.com

Building An Authorization Engine In Python...

Some time ago I developed a competitive intelligence platform. Not much later the simple authorization model used in the tool turned out to be way too simple for all the demands users came up with.

Where we came from

Before the project started, we had a solution in place that was based solely on sending Excel files back and forth. Permission management was easy: you got the file, so you're authorized to view and edit it.
These files were split by country, so it was obvious that permissions in the tool would also be based on countries. If you were assigned to a country, so you could go in to view and edit all entries in that country.
Otherwise you couldn't even see any entries. Later, management decided that we could gain more benefits from opening up the tool a bit, so everybody should be able to see everything
but only edit items in the countries they were assigned to. But, since there is also an option to download data in bulk, there was also some risk.
What if someone who might be leaving the company on bad terms would go and download the entire database. So, let's limit that to only own countries.

Of course, there are some key users who are authorized to edit master data in the tool. And they are the only people who can delete customer entries from the database.
Key users were approaching me for a way to give other users the permission to delete stuff but with limitations. And, as always, there are some edge cases that need to have more
limitations than the tool could currently offer. Or have more rights and less limitations.

This whole authorization and permission thing started to get veeery complex, to say the least.

Where we wanted to be

I've always been a big fan of how AWS handles authorization to access resources. IAM policies are great: they give you a way to grant any permission on any level to any user and make sure that they
can do everything they need to, nothing more, nothing less.

Before, I'd already created a prototype that helped me understand how such a model works. Not that sophisticated, of course, but an authorization system that had at least the following properties:

  • it should be based on policies written in JSON
  • it should be able to deny or allow any action on any resource in the platform
  • you should be able to grant access based on groups and individual policies and these should be effective together
  • it should not put too big a performance burden on the end user, i.e. they should not wait more than 10% in addition to what they would spend waiting without the authorization framework

How we got there

Let me give you a quick rundown of the process I used to develop a solution that ended up in the productive application. It is based on my authorization experiment I mentioned above.

Step 1: The concept of my experiment

The concept is surprisingly simple.

Each user and group (country assignment) has a policy document, which is a JSON string. This policy document consists of a policy name, an action (what the users wants to do), a resource
(what the users wants to do it with) and an effect (allow or deny). Each of these policy elements uses regex notation to allow fine tuning. At runtime, these permission regexes are compiled and cached
so they can be reused (this helps to achieve the performance goal).

Each resource publishes its own resource document as a JSON-like object as well. JSON-like, because all quotation marks and white spaces after colons are stripped away, so it's not a valid JSON,
but it's very similar.

Authorization of a user on an individual item goes like this:

  1. Check if there is an explicit DENY policy for the resource and action by matching all Deny policy regexes of the user. If there is, the request is denied.
  2. Check if there is an explicit ALLOW policy for the resource and action by matching all Allow policy regexes of the user. If there is, the request is granted.
  3. If there is no explicit ALLOW for the resource the request is implicitly denied.

Advanced Authorization

Step 2: The prototype

I had created a prototype in Python for my experiment (https://github.com/gaborschulz/authorization-prototype) to understand how policy-based
authorization could work in a language-agnostic framework. The resource and the policy document processing were nice and smooth, so my idea was to recreate the whole thing in C# for this project.

Step 3: Putting it into action

Since the platform is developed in C# I had to convert the prototype into C#. I've built the authorization resource object into each model and added a central method for checking authorization
on individual objects. I also wanted to get lists filtered by authorizations.

Step 4: Learnings

The solution has proven to be amazingly effective as we are now able to write policies with almost any desired effect. We can hide items generally, from certain users only, hide only their details, etc.
The full power of regular expressions is making this tool very flexible and still fairly easy to use.

It uses only standard library stuff that is available in almost any programming language.

It's very important to use proper caching of the compiled regex statements to maintain performance.
It's enough to purge the user's authorization from the cache whenever a user policy gets changed or a predefined amount of time has passed.

Top comments (0)