z The Flat Field Z
[ @@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ ]

Authorization

Any back-end service with users will end up implementing some form of authorization (authz). While this is often done intuitively, there are more formal models for authz. Furthermore there are off the shelf libraries you can use to help implement those models.

This post will not discuss identifying the caller (authentication), only what the caller can do (authz).

Authz Models

Let's a take a look at some of the commonly used authz models. All of these models are trying to answer questions like: can a thing take an action on another thing? For instance: alice read data1. We are asking whether the subject alice can read the object data1.

Access Control Lists (ACL)

This is the simplest one. A list of which actions can be done on which objects by which subjects.

The components here are:

  • Subject: A thing that can perform an action
  • Object: A thing that can have an action performed on it
  • Action: An action that can be performed

For instance:

SubjectObjectAction
alicedata1read
bobdata2write

Role-Based Action Controls (RBAC)

Here we add one more ingredient: the role. A role is basically a collection of the ACLs (generally called permissions in this model) that you then assign to a user. The components are:

  • Role: A grouping of one ore more permissions
  • Subject: A thing that can perform an action
  • Object: A thing that can have an action performed on it
  • Action: An action that can be performed

For instance:

RoleObjectAction
role_readdata1write
role_readdata1read
SubjectRole
alicerole_read
bobrole_read

One thing to note here is that roles can nested within other roles.

Attribute-Based Action Controls (ABAC)

This is like ACL but instead of everything being a string they are objects. Then the properties of those objects can be used in expressions. The components are:

  • Expression: An expression involving properties of the object that has to evaluate to true
  • Object: A thing that can have an action performed on it
  • Action: An action that can be performed

For instance:

ExpressionActionObject
r.name == 'alice'/data1read
r.name == 'bob'/data2write

Relationship Based Access Control (ReBAC)

This is a more recent model. Instead of having an object and an action, you define the relationships between objects and the permissions those relationships grant. For instance:

Object1RelationObject2
User:aliceownerData:data1
User:bobownerData:data2
ObjectRelationAction
Dataownerread
Dataownerwrite

This might seem similar to the previous models on its face, but because you can nest these relationships you can model complex things. Finally unlike the trees that the previous models formed this model can form a graph.

Mix and Match

There aren't always hard lines between these models. In particular RBAC is commonly used with ABAC. You can mix and match models to meet your needs.

Libraries

If you plan to implement authz there are libraries you can use to kick start that process. For example:

Using these libraries can help guide you towards thinking of authz in a more rigorous way as well.

Central Authz

Implementing correct and thorough authz is much easier in monolith architectures than micro-service architectures. Consider that a micro-service architecture consists of multiple services with their own data stores. The data needed to make authz decisions can and will cross over multiple data stores.

There are a couple of ways you can solve this issue. One is intuitive: you have the services directly call each other as needed. For example:

#+begin_src mermaid :file "authz-calls.svg" :pupeteer-config-file "~/.emacs.d/pupeteer-config.json" :mermaid-config-file "~/.emacs.d/mermaid-config.json" :background-color "transparent" flowchart LR caller[Caller] --> service1[Service 1] service1 --> service2[Service 2] service1 --> service3 subgraph sub[Service 3] service3[Service 3] --> service4[Service 4] end #+end_src #+RESULTS: [[file:authz-calls.svg]]

Services calling one another for authz

Each call to get the data needed for authz will trigger the authz process for the service being called. This can result in a tree like structure of a calls for each authz check that can increase response times and service coupling.

The other option is less intuitive: central authz. In this model you have a service just for authz. It has everything it needs to make authz decisions quickly and reliably, which removes the cross service calls around authz entirely:

#+begin_src mermaid :file "authz-central.svg" :pupeteer-config-file "~/.emacs.d/pupeteer-config.json" :mermaid-config-file "~/.emacs.d/mermaid-config.json" :background-color "transparent" stateDiagram-v2 direction LR state "Central Authz" as authz [*] --> authz authz --> [*] #+end_src #+RESULTS: [[file:authz-central.svg]]

Service calling central authz

This idea has been around for a long time, but there is renewed interest after Google released a paper about their general purpose central authz solution called Zanzibar.

If you want something off the shelf there are some SAAS offerings in this space:

Despite that most companies that need central authz build their own bespoke central authz server.

While there are advantages to central authz there are disadvantages too:

  • A subset of data needs to be pushed into it by all services, and that process needs to be totally reliable
  • If the authz service goes down then everything goes down
  • The cost and effort of maintaining another service

Conclusion

Authz is a hard subject with lots of options for implementation. By thinking about in a more formalized way you are more likely to get it right.