Skip to main content

· 5 min read

2024 was the year AI agents went from demos to production. With the Model Context Protocol (MCP) gaining adoption from Google, OpenAI, Microsoft, and countless others, we're seeing a fundamental shift in how applications interact with external services. And with that shift comes a whole new set of authorization challenges that we at Casbin have been thinking about.

· 7 min read

In this post, I will explain the design and implementation of RBAC using the Casbin library. For a SaaS platform dealing with multiple resource hierarchies and roles that inherit permissions from higher levels, Casbin provides a performant alternative to consider.

Introduction to RBAC

RBAC is a method of restricting access to resources based on the roles that individuals hold. To better understand how hierarchical RBAC works, let's take a look at Azure's RBAC system in the next section and then attempt to implement a similar system.

Understanding Azure's Hierarchical RBAC

Azure Hierarchy

There is a role called Owner for all resources in Azure. Suppose if I have the Owner role assigned to me at the subscription level, that means I am the Owner of all the resource groups and resources under that subscription. If I have Owner at the resource group level, then I am the Owner of all the resources under that resource group.

This image shows that I have Owner access at the subscription level. Subscription Owner

When I check the IAM of a Resource Group under this Subscription, you can see that I have inherited Owner access from the subscription. RG Owner

So, this is how Azure's RBAC is hierarchical. Most enterprise software uses hierarchical RBAC because of the hierarchical nature of the resource levels. In this tutorial, we'll try to implement a similar system using Casbin.

How Does Casbin Work?

Before diving into the implementation, it is important to understand what Casbin is and how it functions at a high level. This understanding is necessary because each Role-Based Access Control (RBAC) system may vary based on specific requirements. By grasping the workings of Casbin, we can effectively fine-tune the model.

What is ACL?

ACL stands for Access Control List. It is a method in which users are mapped to actions and actions to resources.

The model definition

Let's consider a simple example of an ACL model.

[request_definition]
r = sub, act, obj

[policy_definition]
p = sub, act, obj

[policy_effect]
e = some(where (p.eft == allow))

[matchers]
m = r.sub == p.sub && r.obj == p.obj && r.act == p.act
  1. The request_definition is the query template of the system. For example, a request alice, write, data1 can be interpreted as "Can subject Alice perform the action 'write' on object 'data1'?".

  2. The policy_definition is the assignment template of the system. For example, by creating a policy alice, write, data1, you are assigning permission to subject Alice to perform the action 'write' on object 'data1'.

  3. The policy_effect defines the effect of the policy.

  4. In the matchers section, the request is matched with the policy using the conditions r.sub == p.sub && r.obj == p.obj && r.act == p.act.

Now let's test the model on the Casbin editor

Open the editor and paste the above model in the Model editor.

Paste the following in the Policy editor:

p, alice, read, data1
p, bob, write, data2

and the following in the Request editor:

alice, read, data1

The result will be:

true

Visual representation of the ACL model, policy, and request matching

acl

What is RBAC?

RBAC stands for Role-Based Access Control. In RBAC, a user is assigned a role for a resource, and a role can contain arbitrary actions. The request then checks if the user has the permission to perform the action on the resource.

The model definition

Let's consider a simple example RBAC model:

[request_definition]
r = sub, act, obj

[policy_definition]
p = sub, act, obj

[role_definition]
g = _, _
g2 = _, _

[policy_effect]
e = some(where (p.eft == allow))

[matchers]
m = r.sub == p.sub && g(p.act, r.act) && r.obj == p.obj
  1. The role_definition is a graph relation builder that uses a Graph to compare the request object with the policy object.

Now let's test the model on Casbin editor

Open the editor and paste the above model in the Model editor.

Paste the following in the Policy editor:

p, alice, reader, data1
p, bob, owner, data2

g, reader, read
g, owner, read
g, owner, write

and the following in the Request editor:

alice, read, data1
alice, write, data1
bob, write, data2
bob, read, data2
bob, write, data1

The result will be:

true
false
true
true
false

Visual representation of the RBAC model, policy, and request matching

rbac

The g - Role to action mapping table has a Graph mapping the role to action. This Graph can be coded as a list of edges, as shown in the policy which is a common way of representing a Graph:

g, reader, read
g, owner, read
g, owner, write
info

p indicates a normal policy that can be compared using the == operator. g is a Graph-based comparison function. You can define multiple Graph comparators by adding a numerical suffix like g, g2, g3, ... and so on.

What is Hierarchical RBAC?

In Hierarchical RBAC, there are more than one type of resources and there is an inheritance relationship between the resource types. For example, "subscription" is one type and "resourceGroup" is another type. A sub1 of type Subscription can contain multiple resourceGroups (rg1, rg2) of type ResourceGroup.

Similar to the resource hierarchy, there will be two types of roles and actions: Subscription roles and actions, and ResourceGroup roles and actions. There is an arbitrary relationship between the Subscription role and ResourceGroup role. For example, consider a Subscription Role sub-owner. This role is inherited by a ResourceGroup Role rg-owner, which means that if I am assigned the sub-owner role on Subscription sub1, then I automatically also get the rg-owner role on rg1 and rg2.

The model definition

Let's take a simple example of the Hierarchical RBAC model:

[request_definition]
r = sub, act, obj

[policy_definition]
p = sub, act, obj

[role_definition]
g = _, _
g2 = _, _

[policy_effect]
e = some(where (p.eft == allow))

[matchers]
m = r.sub == p.sub && g(p.act, r.act) && g2(p.obj, r.obj)
  1. The role_definition is a graph relation builder which uses a Graph to compare the request object with the policy object.

Now let's test the model on the Casbin editor

Open the editor and paste the above model in the Model editor.

Paste the following in the Policy editor:

p, alice, sub-reader, sub1
p, bob, rg-owner, rg2

// subscription role to subscription action mapping
g, sub-reader, sub-read
g, sub-owner, sub-read
g, sub-owner, sub-write

// resourceGroup role to resourceGroup action mapping
g, rg-reader, rg-read
g, rg-owner, rg-read
g, rg-owner, rg-write

// subscription role to resourceGroup role mapping
g, sub-reader, rg-reader
g, sub-owner, rg-owner

// subscription resource to resourceGroup resource mapping
g2, sub1, rg1
g2, sub2, rg2

And paste the following in the Request editor:

alice, rg-read, rg1

The result will be:

true

Visual representation of the RBAC model, policy, and request matching

hrbac

The g - Role to (Action, Role) Mapping table has a graph mapping the role to the action, role mapping. This graph can be coded as a list of edges, as shown in the policy, which is a common way of representing a graph:

// subscription role to subscription action mapping
g, sub-reader, sub-read
g, sub-owner, sub-read
g, sub-owner, sub-write

// resourceGroup role to resourceGroup action mapping
g, rg-reader, rg-read
g, rg-owner, rg-read
g, rg-owner, rg-write

// subscription role to resourceGroup role mapping
g, sub-reader, rg-reader
g, sub-owner, rg-owner

The g2 - Sub to RG Mapping table has a graph mapping subscription to resourceGroup:

// subscription resource to resourceGroup resource mapping
g2, sub1, rg1
g2, sub2, rg2

Subject Matching Visual representation

hrbac-sub-match

Action Matching Visual representation

hrbac-act-match

Object Matching Visual representation

hrbac-obj-match

info

When a request is submitted to Casbin, this matching happens for all the policies. If at least one policy matches, then the result of the request is true. If no policy matches the request, then the result is false.

Conclusion

In this tutorial, we learned about how different authorization models work and how they can be modeled using Casbin. In the second part of this tutorial, we will implement this in a demo Spring Boot Application and secure the APIs using Casbin.

· 6 min read
Rushikesh Tote

Introduction

APISIX is a high performance and scalable cloud native API gateway based on Nginx and etcd. It is an open source project by the Apache Software Foundation. Besides that, what makes APISIX so good is the support of many great built in plugins that could be used to implement features like authentication, monitoring, routing, etc. And the fact that plugins in APISIX are hot-reloaded (without restarts) makes it very dynamic.

But while using APISIX, there may be scenarios where you might want to add complex authorization logic in your application. This is where authz-casbin might help you, authz-casbin is an APISIX plugin based on Lua Casbin that enables powerful authorization based on various access control models. Casbin is an authorization library which supports access control models like ACL, RBAC, ABAC. Originally written in Go, it has been ported to many languages and Lua Casbin is the Lua implementation of Casbin. The development of authz-casbin started when we proposed a new plugin for authorization in the APISIX repository (#4674) to which the core members agreed. And after the helpful reviews which led to some major changes and improvements, the PR (#4710) was finally merged.

In this blog, we will use the authz-casbin plugin to show how you can implement an authorization model based on Role Based Access Control (RBAC) in APISIX.

NOTE: You will need to use some other plugin or custom workflow for authenticating the user since Casbin will only do authorization and not authentication.

Creating a model

The plugin uses three parameters for authorizing any request - subject, object and action. Here, subject is the value of the username header, which could be something like [username: alice]. Then, the object is the URL path that is being accessed and the action is request method being used.

Let's say we want to create a model with three resources at the paths - /, /res1 and /res2. And we want to have a model like this:

image

This would mean that all users (*) like for example jack can access the homepage (/). And users with admin permissions like alice and bob can access all the pages and resources (like res1 and res2). Also, let's restrict users without any admin permissions to using only GET request method. For this scenario, we could define the model as:

[request_definition]
r = sub, obj, act

[policy_definition]
p = sub, obj, act

[role_definition]
g = _, _

[policy_effect]
e = some(where (p.eft == allow))

[matchers]
m = (g(r.sub, p.sub) || keyMatch(r.sub, p.sub)) && keyMatch(r.obj, p.obj) && keyMatch(r.act, p.act)

Creating a policy

From the above scenario, the policy would be:

p, *, /, GET
p, admin, *, *
g, alice, admin
g, bob, admin

The matcher from the model means:

  1. (g(r.sub, p.sub) || keyMatch(r.sub, p.sub)): Either the request's subject has a role as the policy's subject or the request's subject matches the policy's subject in keyMatch. keyMatch is built in function in Lua Casbin, you can take a look at the function's description and more such functions that could be useful here.
  2. keyMatch(r.obj, p.obj): The request's object matches the policy's object (URL path here).
  3. keyMatch(r.act, p.act): The request's action matches the policy's action (HTTP request method here).

Enabling the plugin on route

Once you have created the model and policy, you can enable it on a route using the APISIX Admin API. To enable it using model and policy file paths:

curl http://127.0.0.1:9080/apisix/admin/routes/1 -H 'X-API-KEY: edd1c9f034335f136f87ad84b625c8f1' -X PUT -d '
{
"plugins": {
"authz-casbin": {
"model_path": "/path/to/model.conf",
"policy_path": "/path/to/policy.csv",
"username": "username"
}
},
"upstream": {
"nodes": {
"127.0.0.1:1980": 1
},
"type": "roundrobin"
},
"uri": "/*"
}'

Here, the username field is the header name that you will be using to pass in the subject. For example, if you will be passing the username header as user: alice, you would use "username": "user".

For using model/policy text instead of files, you can use the model and policy fields instead:

curl http://127.0.0.1:9080/apisix/admin/routes/1 -H 'X-API-KEY: edd1c9f034335f136f87ad84b625c8f1' -X PUT -d '
{
"plugins": {
"authz-casbin": {
"model": "[request_definition]
r = sub, obj, act

[policy_definition]
p = sub, obj, act

[role_definition]
g = _, _

[policy_effect]
e = some(where (p.eft == allow))

[matchers]
m = (g(r.sub, p.sub) || keyMatch(r.sub, p.sub)) && keyMatch(r.obj, p.obj) && keyMatch(r.act, p.act)",

"policy": "p, *, /, GET
p, admin, *, *
g, alice, admin
g, bob, admin",

"username": "username"
}
},
"upstream": {
"nodes": {
"127.0.0.1:1980": 1
},
"type": "roundrobin"
},
"uri": "/*"
}'

Enabling the plugin using a global model/policy

There may be situations where you might want to use a single model and policy configuration across multiple routes. You can do that by first by sending a PUT request to add the model and policy configuration to the plugin's metadata by:

curl http://127.0.0.1:9080/apisix/admin/plugin_metadata/authz-casbin -H 'X-API-KEY: edd1c9f034335f136f87ad84b625c8f1' -i -X PUT -d '
{
"model": "[request_definition]
r = sub, obj, act

[policy_definition]
p = sub, obj, act

[role_definition]
g = _, _

[policy_effect]
e = some(where (p.eft == allow))

[matchers]
m = (g(r.sub, p.sub) || keyMatch(r.sub, p.sub)) && keyMatch(r.obj, p.obj) && keyMatch(r.act, p.act)",

"policy": "p, *, /, GET
p, admin, *, *
g, alice, admin
g, bob, admin"
}'

And then to enable the same configuration on some route, send a request using the Admin API:

curl http://127.0.0.1:9080/apisix/admin/routes/1 -H 'X-API-KEY: edd1c9f034335f136f87ad84b625c8f1' -X PUT -d '
{
"plugins": {
"authz-casbin": {
"username": "username"
}
},
"upstream": {
"nodes": {
"127.0.0.1:1980": 1
},
"type": "roundrobin"
},
"uri": "/route1/*"
}'

This will add the plugin metadata configuration to the route. You can also easily update the plugin metadata configuration by resending the request to plugin's metadata with updated model and policy configuration, the plugin will automatically update all the routes using the plugin metadata.

Use Cases

  • The primary use case of this plugin would be in implementing authorization in your APIs. You can easily add this plugin on any API route that you are using with your authorization model and policy configuration.
  • If you want to have a single authorization model for all your APIs, you can use global model/policy method. This makes updating the policy easy for all routes, since you only need to update the metadata in etcd.
  • While if you would like to use a different model for every different route, you can use the route method. This is helpful when different API routes have different sets of user permissions. You can also use this when you are dealing with larger policies, since it will make the authorization faster when filtered into multiple routes.

· One min read
Casbin

Today, we are pleased to announce that Casbin's founder, Yang Luo is awarded the "Google Open Source Peer Bonus winners" for his work on Casbin, Npcap and Nmap in 2019 Q3.

ospb

The original award letter can be accessed here.

The Google Open Source Peer Bonus program is described as:

In the same way that a Google Peer Bonus is used to recognize a fellow Googler who has gone above and beyond, an Open Source Peer Bonus recognizes external people who have made exceptional contributions to open source.

The announcement for the 2019 winners is available at:

https://opensource.googleblog.com/2020/01/announcing-2019-second-cycle-google.html

Yang and Casbin are listed among open source developers and projects that have a relevant impact out there, like Git, TensorFlow, V8, CPython, LLVM, Apache projects, Angular or Jenkins.

We are glad to see Casbin recognized in this way for its contribution to open source and cloud security!

Thanks for flying Casbin!

· One min read
Yang Luo

Today, we migrated Casbin's documentation from GitHub Wiki to Docs of this website, which is powered by Docusaurus. Docusaurus provides lots of awesome features like better Markdown styles, full-text search, versioning, translation.

The documentation is not perfect yet and still needs tuning. The source code is hosted on GitHub: https://github.com/casbin/casbin-website-v2 .

Any contribution or suggestion is welcome!

· One min read
Helong Zhang

Some of our customers ask if Casbin can be used as a service instead of a library. The answer is YES. Today, we launched the Casbin Server project as a concrete solution for Access Control as a Service.

Casbin Server is under active development by our core team. It has several features:

  • Purely developed in Golang.
  • Can manage thousands of Casbin instances, so you can move policy enforcement logic from multiple services into one Casbin Server.
  • gRPC is used to communicated with Casbin Server. We also consider to add the RESTful support in near future.
  • A friendly web administrator portal is provided for non-developer administrators to manage all details like Casbin instances, models, policy storage and load balancing.

The source code is hosted on GitHub: https://github.com/casbin/casbin-server

Any issues or pull requests are welcome :)