Published on

Casbin OpenResty Example

Authors
  • Name
    Rushikesh Tote
    Twitter

A simple example of OpenResty application with RBAC using Casbin (Lua)

This is a very simple example of an application using the OpenResty stack with RBAC authorization using Casbin. This is a sample so it isn't secure or anything but it is more of an example template to help integrate Casbin in your OR applications.

The full source code is at casbin-openresty-example.

Installing Casbin

If you haven't installed OpenResty yet, you can do that here. This assumes that your installation is at /usr/bin/openresty, if not you may need to change the relative paths accordingly.

You also need to install LuaRocks first if you haven't, you can do so by:

wget https://luarocks.org/releases/luarocks-3.3.1.tar.gz
tar zxpf luarocks-3.3.1.tar.gz
cd luarocks-3.3.1

./configure --prefix=/usr/local/openresty/luajit \
--with-lua=/usr/local/openresty/luajit/ \
--lua-suffix=jit-2.1.0-beta3 \
--with-lua-include=/usr/local/openresty/luajit/include/luajit-2.1

sudo make
sudo make install

And then to install the dependencies - GCC and PCRE:

sudo apt update
sudo apt install gcc libpcre3 libpcre3-dev

Finally, you can install the latest released version of Casbin by:

sudo /usr/local/openresty/luajit/bin/luarocks install casbin

This should install Casbin (Lua) and all it's dependencies.

Creating example Casbin model file and policy file

We will use the basic RBAC model and policy files for this example. So, creating a example directory by:

mkdir example

And creating a rbac_model.conf in the example directory with the following 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) && r.obj == p.obj && r.act == p.act

Also, creating a rbac_policy.csv in the example directory with the following policy:

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

Creating nginx.conf

Then, we will create 3 more directories in the project - conf, logs and lua by:

mkdir conf logs lua

Then in the conf directory create a nginx.conf config file for the Nginx server by:

vim conf/nginx.conf

which will contain this:

worker_processes 1;

events {
    worker_connections 1024;
}

http {
	init_by_lua_block {
        local Enforcer = require("casbin")

    	-- Initialize a new enforcer at server start
        e = Enforcer:new("example/rbac_model.conf", "example/rbac_policy.csv")
    }
    lua_package_path "$prefix/lua/?.lua;;";

    server {
        listen 8080 reuseport;

        location / {
            default_type text/plain;
            content_by_lua_block {

            }
        }
    }
}

Here the block init_by_lua_block will run only at the server start (or reload) which is:

local Enforcer = require("casbin")

-- Initialize a new enforcer at server start
e = Enforcer:new("example/rbac_model.conf", "example/rbac_policy.csv")

Here, we create a new Casbin Enforcer e which is defined globally.

Creating app.lua

Then we create a lua file as app.lua by:

vim lua/app.lua

which will contain this:

local M = {}

function M.check(e)
    local req = ngx.var.request_uri
    for usr, obj, act in req:gmatch("/(%w+)%?obj=(%w+)&act=(%w+)") do

  		-- If pattern matches, shows the request and enforce result
    	ngx.say("Request:\t", "User:"..usr.."\t", "Object:"..obj.."\t", "Action:"..act)
    	ngx.say("Result = ", e:enforce(usr, obj, act))
    	break
	end
end

return M

and add the following code in content_by_lua_block to nginx.conf:

local app = require("app")
-- Check at every request
app.check(e)

Usage

You can then start the server by:

sudo openresty -p $PWD/

And fetch the page with

curl http://127.0.0.1:8080/

So what this does is, when you send request with curl http://127.0.0.1:8080/alice?obj=data1&act=read, it will match usr = alice, obj = data1 and act = read and send the parameters to e:enforce(usr, obj, act) which will return the enforce result.

For example, if you send a curl request as:

curl http://127.0.0.1:8080/alice?obj=data2&act=read

Since, alice has a role as data2_admin, it can acces the data2 object with read and write actions as defined in policy. So this will output in:

Request:  User:alice  Object:data2  Action:read
Result = true

And if you send the curl request as:

curl http://127.0.0.1:8080/bob?obj=data1&act=write

Since bob has no policy in data1 object, it will output in:

Request:  User:bob  Object:data1  Action:write
Result = false

The full source code is at casbin-openresty-example.