Image by Kerfin7 on Freepik

Authorization in the modern age — Part 02 of 02

Viduranga Gunarathne
Published in
9 min readSep 17, 2024

--

Continuation from the Part 01

Authorization as a Service

Authorization as a Service means using a third party to manage authorization in your application. When working with large scale systems with lots of micro-services, you need a mechanism to coordinate the authorization between those services while the authorization logic stays decoupled from the application code. This is where an authorization-as-a-service comes to the aid.

Google

Google needed a unified authorization system to manage Drive, Mail, Calendar, YouTube etc. Over a period of several years, a dedicated team built Google Zanzibar which is a highly available, scalable authorization service.

Slack

They needed a shared, modularized authorization service for their enterprise customers. Their team built a micro-service that reads permissions from their data store in the monolith. It took a team of about ten engineers, about an year to design and build this service.

AirBnB

Airbnb got to know that they were duplicating authorization checks in each of their micro-services. It took them more than two years to building and scale their authorization service, Himeji, and it continues to be supported by a full team of engineers.

Carta

Carta had the problem of getting five different services to agree on authorization decisions. They took a similar approach by building AuthZ. It took their team about 9 months to build and deploy their service.

Authorization-As-A-Service Options

Google developed Zanzibar as a centralized system designed to store relationship data, specifically optimized for handling authorization queries. The system relies on a set-rewriting-based configuration language as its core method for defining and expressing authorization logic.

Products based on Zanzibar

Advantages to Zanzibar:

  • The data model is highly optimized to deliver fast and efficient authorization performance.
  • Some core authorization use cases, like role-based access control (RBAC) and relationship-based access control (ReBAC), are supported.

Disadvantages to Zanzibar:

  • Using Zanzibar requires a huge engineering effort. Teams that have adopted Zanzibar (or similar) solutions end up needing to invest many additional months or years working with the application development teams.
  • You’ll need to remodel your data to conform to Zanzibar’s format.
  • You’ll need to learn how to model your authorization using Zanzibar’s language of relation tuples.

When should you use Zanzibar?

Zanzibar is best for authorization across extremely high-scale applications, because of its high engineering cost,

  • Authzed and Auth0’s Sandcastle are some examples of managed services based on Zanzibar.
  • SpiceDB and Ory Keto are open source implementations based on Zanzibar that you can run yourself.

Products based on Open Policy Agent (OPA)

OPA (Open Policy Agent) is a policy engine built with the purpose of controlling access to resources. It has its own language for writing policies called, Rego.

Advantages to OPA:

  • The Rego language is very flexible.
  • OPA has a growing community and good documentation.

Disadvantages to OPA:

  • Rego comes up with some learning curve to it.
  • OPA can be slow when attempting to use with normal application workloads.

When should you use OPA?

  • When controlling infrastructure access, like managing who can create or delete Kubernetes objects.
  • Some companies have built application authorization products, like Aserto and Permit.io, on top of OPA.

Common Authorization frameworks

Zanzibar based frameworks (SpiceDB, Keto …)

Google Zanzibar is a system designed to store and evaluate access control lists by operating in a global scale. It can be used to build relationships in a graph data structure which will determine what relationships are maintained between resources and can be evaluated quickly to determine resource accessibility.

Concept

Relationship tuples are the underlying data type in the Zanzibar model. A tuple represents a relationship between an object (a resource or an application object) and a subject (someone or something trying to perform an action on an object) or a subject group. Relation tuples can be
grouped in namespaces.

Following is the encoding structure used in Zanzibar

<relation-tuple> ::= <object>'#'relation'@'<subject>
<object> ::= namespace':'object_id
<subject> ::= subject_id | <subject_set>
<subject_set> ::= <object>'#'relation

As a rule of thumb, a relation tuple should translated to a sentence as;

Subject has relation on object which is one of the namespace.

Following are some example relation tuples,

  • Template : namespace:object#relation@subject
    Example : components:helloworld#deploy@users:john
    Explanation : John can deploy the helloworld component
  • Template : namespace:object#relation@subject_set
    Example : components:helloworld#deploy@(roles:admin#member)
    Explanation : Members of the role admin can deploy the helloworld component.

Now that we’ve looked into some high level examples, let’s dig a bit into the concepts.

i. Namespaces

namespace:object#relation@subject

Namespaces are used to organize and group relation tuples. Objects that contain a similar set of properties can be grouped using namespaces. Namespaces should be named after the plural form of the objects that they describe.

Eg: A collection of documents (doc1, doc2, doc3) can be grouped into a “documents” namespace.

ii. Objects

namespace:object#relation@subject

Objects are identifiers used for application level objects. They can represent some sort of a resource such as a document, project, folder etc. The application should map the objects to an unambiguous identifier.

In general it’s recommended to use UUIDs as object names. Since the object name is a string, it could be tempting to include application information in the object names. But it would be much better to maintain UUIDs rather than object names, since it would be easier for migrations, renames, maintaining uniqueness and a whole plethora of other advantages.

Eg: A permission or a role in a system can be considered as an object.

  • permissions:create-project#allowed@john
  • roles:admin#has@derik

When looking at a tenancy/organization model, we can have the following set of relations according to this approach.

Eg: Assume the organizations starkindustries and waynecorp

  • permissions:starkindustries/create-project#allowed@john
  • roles:starkindustries/admin#has@derik
  • permissions:waynecorp/create-project#allowed@sarah
  • roles:waynecorp/admin#has@erika

This approach ensures that a permission “create-project” belongs to a specific organization.

iii. Subjects

namespace:object#relation@subject / namespace:object#relation@subject_set

Subjects are a recursive polymorphic data type. These subjects can refer to a particular individual subject or a collection of subjects.

  • Subject IDs : These can be any strings which are uniquely mapped to any resource in the application. It is recommended using UUIDs as subjects since it provides a higher entropy. URLs or opaque tokens too are an option.
    Eg:
    organizations:<ORG_UUID>#users@
    <USER_UUID>
  • Subject Sets : A subject set is a collection of subject IDs which have a specific relation with an object. Subject sets help realize RBAC or inheritance of relations.
    Eg:
    organizations:<ORG_UUID>#users@(groups:<ORG_UUID>/admin#member)
    Which means that the members of the group <ORG_UUID>/admin are users of the organization.

iv. Relations

namespace:object#relation@subject

The relations define the relationships between the subject/subject_sets and the objects.

Eg:
components:helloworld#edit@john
John has edit access on the `helloworld` under the components namespace.

These relations can be represented by a directed graph which can be called a graph of relations.

// John is the owner of default-project
projects:default-project#owner@john

// Owners of the default-project can edit echo and helloworld components
components:echo#edit@(projects:default-project#owner)
components:helloworld#edit@(projects:default-project#owner)

//Derik can edit the helloworld component
components:helloworld#edit@derik

Open Policy Agent

OPA is an open-source policy engine that enables fine-grained access control and policy enforcement. It provides a declarative language called Rego for expressing the policies and a runtime that can be integrated with various applications and services. OPA follows a “policy-as-code” approach where policies are written as code and executed as part of the application workflow.

https://www.openpolicyagent.org/docs/latest

Assume an example where you have a micro-service where users can create, update and delete resources and you want to enforce a policy so that only users with the admin role are allowed to perform write operations. In OPA, the following Rego policy can be written to define the access control rule.

package myapp

default allow = false

allow {
input.method = "POST"
inpit.user.role = "admin"
}

OPA is an attribute based access control mechanism where in the above example, the role attribute and the HTTP method is considered before allowing a request.

Following is another example policy for an attribute-based access control using OPA.

package app.abac

import future.keywords.if

default allow := false

allow if user_is_owner

allow if {
user_is_employee
action_is_read
}

allow if {
user_is_employee
user_is_senior
action_is_update
}

allow if {
user_is_customer
action_is_read
not pet_is_adopted
}

user_is_owner if data.user_attributes[input.user].title == "owner"
user_is_employee if data.user_attributes[input.user].title == "employee"
user_is_customer if data.user_attributes[input.user].title == "customer"
user_is_senior if data.user_attributes[input.user].tenure > 8
action_is_read if input.action == "read"
action_is_update if input.action == "update"
pet_is_adopted if data.pet_attributes[input.resource].adopted == true

This example can be tried out in OPA Playground.

XACML

eXtensible Access Control Markup Language (XACML) is a general-purpose access control policy language. This means that it provides a syntax (defined in XML) for managing access to resources. This has been standardized by the Technical Committee of the OASIS consortium. XACML is considered to be both a policy language and an access control decision request/response language. The policy language can be used to define access control policies. XACML fundamentally supports attribute-based access control.

XACML architecture

https://dzone.com/articles/a-beginners-guide-to-xacml

Policy Decision Point (PDP)
It is an entity responsible for storing policies, analyzing policies upon request, making decisions and forwarding them to the Policy Enforcement Point (PEP).

Policy Enforcement Point (PEP)
It is an entity responsible for enforcing access control based on the authorization decisions made by the PDP.

Policy Administration Point (PAP)
Manages the process of creating policies. It encapsulates the management of the PDP and PIP.

Policy Information Point (PIP)
The PIP entity is responsible for providing additional attribute values to the PDP to help make its decisions. These additional attributes can be characteristics of environments, actors, resources etc. For example, the PIP may fetch the current time from a system clock.

XACML Policy elements

  • Rule : A rule specifies a condition and an associated decision (Permit, Deny). It is a boolean expression that can be evaluated separately.
  • Policy : Constitutes a set of rules and a specified methodology for combining the results of the rule evaluations. Policy is considered as the basic unit of decision used by the PDP to form the basis of an authorization decision.
  • PolicySet : Constitutes a collection of policy or policy set elements and the procedure for combining the results of each of them to a final answer.
  • Target : Defines the conditions that must be satisfied in the incoming request, for the policy to apply. An empty target matches all the requests.
https://dzone.com/articles/a-beginners-guide-to-xacml

Eg:
If both conditions A and B should be matched,

<Target>
<AnyOf>
<AllOf>
<Match>conditionA</Match>
<Match>conditionB</Match>
</AllOf>
</AnyOf>
</Target>

If either condition A or B should be matched,

<Target>
<AnyOf>
<AllOf>
<Match>conditionA</Match>
</AllOf>
<AllOf>
<Match>conditionB</Match>
</AllOf>
</AnyOf>
</Target>

Rule combining algorithms

These determine the methodology by which the results of multiple rules of a policy are combined to give out a single solution.

  • Deny-Overrides : If any decisions is “Deny”, the final result is “Deny”
  • Permit-Overrides : If any decision is “Permit”, the final result is “Permit”
  • First-applicable : Each of the rules are evaluated in the specified order and the first rule to generate a permit, deny or indeterminate will be the applicable rule and its effect will be considered as the result of the evaluation of the policy.

Eg:

  • Target : Policy is applied if the subject-id is “John” and the action is “GET”
  • Rule : Permission given if the subject-id is “John”
  • Obligation : Returns the value “john@abc.com” if the above rule evaluates to permit.
  • Advice : Returns the value of the claim “http://wso2.org/claims/emailaddress” if the rule evaluates to permit.
<Policy xmlns="urn:oasis:names:tc:xacml:3.0:core:schema:wd-17"
PolicyId="sample_policy_8"
RuleCombiningAlgId="urn:oasis:names:tc:xacml:1.0:rule-combining-algorithm:first
-applicable" Version="1.0">
<Target>
<AnyOf>
<AllOf>

<Match

MatchId="urn:oasis:names:tc:xacml:1.0:function:string-equal">

<AttributeValue

DataType="http://www.w3.org/2001/XMLSchema#string">john</AttributeValue>

<AttributeDesignator

AttributeId="urn:oasis:names:tc:xacml:1.0:subject:subject-id"
Category="urn:oasis:names:tc:xacml:1.0:subject-category:access-subject"
DataType="http://www.w3.org/2001/XMLSchema#string"
MustBePresent="true"></AttributeDesignator>

</Match>
<Match

MatchId="urn:oasis:names:tc:xacml:1.0:function:string-equal">

<AttributeValue

DataType="http://www.w3.org/2001/XMLSchema#string">GET</AttributeValue>

<AttributeDesignator

AttributeId="urn:oasis:names:tc:xacml:1.0:action:action-id"
Category="urn:oasis:names:tc:xacml:3.0:attribute-category:action"
DataType="http://www.w3.org/2001/XMLSchema#string"
MustBePresent="true"></AttributeDesignator>

</Match>
</AllOf>
</AnyOf>
</Target>
<Rule Effect="Permit" RuleId="rule1">
<Condition>
<Apply
FunctionId="urn:oasis:names:tc:xacml:1.0:function:string-equal">

<Apply

FunctionId="urn:oasis:names:tc:xacml:1.0:function:string-one-and-only">

<AttributeDesignator

AttributeId="urn:oasis:names:tc:xacml:1.0:subject:subject-id"
Category="urn:oasis:names:tc:xacml:1.0:subject-category:access-subject"
DataType="http://www.w3.org/2001/XMLSchema#string"
MustBePresent="true"></AttributeDesignator>

</Apply>
<AttributeValue

DataType="http://www.w3.org/2001/XMLSchema#string">john</AttributeValue>
</Apply>
</Condition>
</Rule>
<Rule Effect="Deny" RuleId="8b52dcce-cd23-44f0-a223-f80ca6cc1a05"></Rule>
<ObligationExpressions>
<ObligationExpression FulfillOn="Permit" ObligationId="email">
<AttributeAssignmentExpression AttributeId="email">

<AttributeValue

DataType="http://www.w3.org/2001/XMLSchema#string">john@abc.com</AttributeValue
>
</AttributeAssignmentExpression>
</ObligationExpression>
</ObligationExpressions>
<AdviceExpressions>
<AdviceExpression AdviceId="email_advice" AppliesTo="Permit">
<AttributeAssignmentExpression AttributeId="email">
<AttributeDesignator

AttributeId="http://wso2.org/claims/emailaddress"
Category="urn:oasis:names:tc:xacml:3.0:subject-category:access-subject"
DataType="http://www.w3.org/2001/XMLSchema#string"
MustBePresent="true"></AttributeDesignator>
</AttributeAssignmentExpression>
</AdviceExpression>
</AdviceExpressions>
</Policy>

Now let’s take a look at how some of the cloud vendors manages their in house authorization.

With this I would conclude my two part articles on introduction to modern authorization concepts. I do hope you found this useful and as always keep learning …!

--

--

Viduranga Gunarathne
vlgunarathne

Computer Science Graduate | Software Engineer @WSO2 | Tech enthusiast | Cinephile