t10d/kingdom-python-server

Improvements on Authorization functionality

Closed this issue · 1 comments

Compliance of Permissions feature to ABAC specs

Overview

Authorization is a wide and broad range, and many forms of access control have emerged. Nowadays, there are mainly two kinds of access control that permeates implementations: ABAC[3], RBAC[1] and a mixture of both. There are plenty of material available online that discusses both in-depth so I'm discussing only trade-offs and decisions that are relevant to this project.

What should always guide design decisions is input space. This is primarily important when dealing with authorization due to the fact that there are environments that needs overly restrictive policies, which must call for sophisticated and articulated authorization systems. In our scenario, which comprises environments and business that do not enforce nor implement strict policies (such as military force or large enterprises, for instance), we've come to choose a a RBAC with ABAC features, that cover both of RBAC and ABAC shortcomings[4].

Requirements

High-level

  1. Fine-grained permission control on specific resources instances e.g. user can only list a subset of a product list
  2. Granting must be explicit, that is, for a given user it should be cheap to query which permissions him/her have
  3. Design should be flexible in order to leverage even richer attribute-based authorization

Functional

Administrative commands

  1. Add a Role
  2. Delete a Role
  3. Assign a role to a User
  4. Dessign a role of a User
  5. Add a Permission
  6. Grant permission to a Role (creates a valid permission)
  7. Revoke permission to a Role (deletes a valid permission)

Administrative views

  1. Query all Roles and Permissions assigned to a given User
  2. Query all Roles and Permissions registered
  3. Query all Sessions (and with which activated Roles) that were created for a given User

Supporting system commands

  1. Create a Session
  2. Drop a Session
  3. Check if a User is authorized to perform an operation on a given Resource
  4. Check if an added Permission is valid and hold valid conditional clauses

Proposition

Our proposition mostly resembles AERBAC from Rajpoot et al[5]. In simple terms, it is a RBAC model enhanced with attributes that enable context-aware and fine-grained authorization cases.

Consider a subject trying to access a given resource, we consider context only attributes of both the subject and the resource:

class Context(object):
    cm: ChainMap
    resource: Entity

    def __init__(self, subject: User, resource: Entity):
        map_subject = {f"subject.{key}" for k in subject.__dict__.keys()}
        map_resource = {f"resource.{key}" for k in resource.__dict__.keys()}
        self.resource = resource
        self.cm = ChainMap(map_subject, map_resource)
    
    def __enter__(self) -> ChainMap:
        return self.cm

requested_resource = Product
logged_user = User("ee7851b8")
ctx = Context(subject=logged_user, resource=requested_resource, operation=CREATE):
logged_user.is_authorized(ctx)  # role association is dealt within domain

It is a stricter kind of ABAC in a sense that if we're trying to prevent access with attributes that are both not from the subject nor from the resource, we find that it's better modeled as a business rule instead of an authorization directive.

p1 = Permission(Product, CREATE, "resource.id=*")
p2 = Permission(Product, CREATE, "resource.id=7fg6ab756d && resource.name='Foo'")
p3 = Permission(Product, DELETE, "resource.id=7fg6ab756d")
r1, r2 = Role(p1, p2), Role(p3)
user.associate_roles([p1, p2, p3])
user.permissions  # Product.*.CREATE, Product.7fg6ab756d.DELETE

Please note that these snippets are anecdotes and are very likely to improve.

Limitations

However, since it makes sense to avoid premature optimisation, our design have a few limitations when constrasted to AERBAC and RBAC:

  1. There are no SSD (static separation of duty) and DSD (dynamic separation of duties) constraints or conflicting roles. We shall enforce conflict free role assignment in assign-time i.e. analogous to a static separation of duties.
  2. Attribute filtering is only related to the resource or the subject (logged user) of a given active session.
  3. There is no need for role activation in sessions; every user is activated to all of roles assigned to him/her at every login.

Premises

  1. Least permission principle i.e. a user defaults to a DEFAULT_ROLE, which has the minimum possible set of permissions of a system. It also means that a user can only do something if it is stated through a permission statement.
  2. Operations and Resources are static. Their management —inclusion/update/removal— is to be done exclusively through database migrations.
  3. Permissions are cumulative and additive. In the example on section above, user will be able to CREATE on every instance of Product, even though p2 scope is limiting.
  4. Overlapping permissions have no effect.
  5. Operation CREATE can only be associated to a selector that is not a unique id.

This premises aim to simplify conflicts that are major pain-points of RBAC systems.

Phasing Attribute Selection

In order to make things more tangible, we'll split Attribute Condition feature to gradually expand:

  1. first only support resource.id selecting,
  2. support resource's all attributes selecting then
  3. support user's all attributes selecting

References

Glossary

  • Role: an organizational job function with a clear definition of inherent responsibility and authority (permissions)
  • Operation: operation that can be performed on an instance e.g. CREATE, EDIT, REMOVE
  • Selector: an identifier that maps one or many instances of a Resource.
  • AttrCondition: predicate expression that selects rows of Context.
  • Permission: is a tuple of (Resource, Operation, Selector, AttrCondition)
  • Subject: A user that has an active session on a system.
  • User: a registered user.
  • Object: is a domain's mapped entity.

External links

[1]: Role Based Access Control – ANSI, https://profsandhu.com/journals/tissec/ANSI+INCITS+359-2004.pdf
[2]: LI, Ninghui. Critique of the ANSI Standard on Role Based Access Control, https://www.cs.purdue.edu/homes/ninghui/papers/aboutRBACStandard.pdf
[3]: Guide to Attribute Based Access Control (ABAC) Definition and Considerations — NIST, https://nvlpubs.nist.gov/nistpubs/specialpublications/NIST.sp.800-162.pdf
[4]: Adding Attributes to Role-Based Access Control — NIST, https://csrc.nist.gov/publications/detail/journal-article/2010/adding-attributes-to-role-based-access-control
[5]: Attributes Enhanced Role-Based Access Control Model, https://backend.orbit.dtu.dk/ws/files/110988163/AERBAC_TrustBus_20150618_.pdf

After a detailed overview on how to best place authorization policy translation, I believe that doing so under a common shared module is the best place to be: kingdom.core.adapters.authorization. In that sense, all microservices would share of common knowledge on how to validate an incoming resource access request.

As importantly, this results in some clear refactor on where authentication sits. Since it'd be just as expected for it to seat under kingdom.core.adapters.authentication. Exposing to every service how to decode a JWT.

It would also make sense to place these common middlewares under kingdom.core.entrypoint.middlewares.

This decision was made after careful thoughts on how to make Access service as decoupled as possible. With this design, each microservice will be responsible for ensuring that an incoming request from a subject is both trusted and allowed to do what it is requesting. And this knowledge would be centralized and controlled by kingdom package. Meaning that changes to those mechanics are being done only in one place.