Recent Posts


Recent Comments


Archives


Categories


Meta



Sungard AS Labs PORPER

PORPER – Portable Permission Controller, an RBAC Library for Microservices

Thanks to serverless frameworks like AWS Lambda, Google Cloud Function and Azure Functions, applications implemented as microservices are growing rapidly.  However, because microservices frameworks are still evolving, there are a few parts missing that would make development as easy as developing applications using existing frameworks.

One of the missing microservice frameworks is how to manage the permissions/privileges by users/groups. When implementing applications using existing frameworks, you can use the modules that the frameworks provide, however they are not available when you implement applications using serverless computing. Managing permissions and privileges based on Role Based Access Controller (RBAC) is not straightforward and is difficult to architect. To fix this, I developed a simple module called Porper for use when developing serverless applications. Porper is a very simple RBAC  library that manages user permissions based on their privileges.

The Porper module is based on python 2.7 and stores data in a MySQL database. To use Porper, first you must create a database in MySQL server. Once created, please follow the steps below which will populate the database using the initial data and set up the demo environment:

  • Clone this project, https://github.com/SungardAS/porper-demo.git
  • Import the data in ‘porper_initial.sql’ which will populate
    • Tables for users/roles/permissions and other resources
    • Administrator role, ‘admin’, which is an administrator group (You may think the role as a group/department/company)
  • Install the Porper library using pip
    pip install porper [-t .]
  • Set the environment variables with your new database information
    export MYSQL_HOST=<database_host>
    export MYSQL_USER=<databse_user>
    export MYSQL_PASSWORD=<database_password>
    export MYSQL_DATABASE=<database_name>

Now, to start, execute the command below to run ‘init.py’ python file.

python init.py

This command will setup the demo environment with executing below steps so that you don’t need to do them manually.

  • Get the connection to the ‘porper’ database
  • Create the first user, ‘admin’, who is the administrator and belongs to the ‘admin’ role
  • Simulate the login by the ‘admin’ user for authentication. (You can use Google+ and GitHub authentication using their OpenID connect. Please see Porper-API and Porper-UI projects for more detail)
  • Create a new role, ‘demo’
  • Invite a user so that the user can login to the system (Because this system is based on the authentication using Google+ and GitHub accounts, you need to invite users to be added. After invited, the users will be automatically added to this system once they successfully authenticate using their Google+ or GitHub accounts for the first time)
  • Simulate the login by the ‘demo’ user for authentication
  • Create a table for a sample resource, ‘demo’

Once the script successfully completes the setup of the environment for demonstration, it’s time to dive into how the Porper library works. I already created the controller and model classes to manage the sample resource, demo (which are ‘demo_controller.py’ and ‘demo.py’).

‘DemoController’ inherits from the ‘ResourceController’ in the Porper library and provides interfaces to ‘create’, ‘read’, ‘update’ and ‘delete’ instances. To make this demo simple, ‘DemoController’ only re-defines the ‘create’ interface to give permissions of ‘read’, ‘update’ and ‘delete’ on the newly created instance to the creator once the instance is successfully created. You can add/remove any permissions you need later.

As you see in ‘demo_controller.py’, it implemented the ‘create’ method and it calls ‘add_permission’ method once a new instance is created as below.

def add_permissions(self, id, user_id):
    from porper.controllers.permission_controller import PermissionController
    permission_controller = PermissionController(self.connection)
    # give read/update/delete permissions to this user
    user_permission_params = {
        "user_id": user_id,
        "resource": self.resource,
        "value": '%s' % id
    }
    user_permission_params["action"] = "read"
    permission_controller.create(None, user_permission_params, user_id)
    user_permission_params["action"] = "update"
    permission_controller.create(None, user_permission_params, user_id)
    user_permission_params["action"] = "delete"
    permission_controller.create(None, user_permission_params, user_id)

Let’s try to create a new instance of the ‘demo’ resource using python commands after getting the connection to the ‘porper’ database. We’re using the ‘admin_access_token’ to use the ‘admin’ session, which was created during environment setup.

$ python
>>> from porper.models.connection import mysql_connection
>>> connection = mysql_connection()
>>> from demo_controller import DemoController
>>> from demo_controller import DemoController
>>> demo_controller = DemoController(connection)
>>> admin_access_token = 'ffffffff-ffff-ffff-ffff-ffffffffffff'
>>> demo_create_params = { "id":"1", "name":"first", "description":"first instance" }
>>> demo_controller.create(admin_access_token, demo_create_params)
Exception: not permitted

As expected, it will raise an exception of ‘not permitted’ because we didn’t give the permission to create a new instance of ‘demo’ resource to the ‘admin’ user. So, let’s give the ‘create’ permission to ‘admin’ user. (We’re assigning ‘*’ for ‘value’, which allows all instances of the resource because no instance id is available for the instances to be created)

>>> from porper.controllers.permission_controller import PermissionController
>>> permission_controller = PermissionController(connection)
>>> admin_user_id = 'ffffffff-ffff-ffff-ffff-ffffffffffff'
>>> permission_params = {
     "user_id": admin_user_id,
     "resource": "demo",
     "value": "*",
     "action": "create"
   }
>>> permission_controller.create(admin_access_token, permission_params)
>>> connection.commit()

Now, try again adding a new instance of ‘demo’ resource.

>>> demo_controller.create(admin_access_token, demo_create_params)
>>> connection.commit()

Now, the admin user created an instance of ‘demo’ resource with no exception. Once a new instance is created successfully, try retrieving the instance to confirm the creation.

>>> demo_find_params = { "id":"1" }
>>> demo_controller.find_all(admin_access_token, demo_find_params)
[{'description': 'first instance', 'id': '1', 'name': 'first'}]

You may wonder how the ‘find_all’ method successfully retrieved the target instance because we didn’t give the ‘read’ permission to this user. The reason is because ‘read’ access was granted right after the instance is created in ‘add_permissions’ method inside the ‘DemoController’ as shown above.

Now let’s try to retrieve the same instance using another user other than the ‘admin’. We created an additional user, ‘demo@porper.com’, during the demo environment setup. First, find the ‘id’ of the user, ‘demo@porper.com’.

>>> from porper.controllers.user_controller import UserController
>>> user_controller = UserController(connection)
>>> user_find_params = { "email": "demo@porper.com" }
>>> user_info = user_controller.find_all(admin_access_token, user_find_params)
>>> user_id = user_info[0][ "id"]

Next, simulate the login by the user for authentication.

>>> from porper.controllers.token_controller import TokenController
>>> token_controller = TokenController(connection)
>>> user_access_token = 'ffffffff-user-ffff-ffff-ffffffffffff'
>>> user_refresh_token = 'ffffffff-user-ffff-ffff-ffffffffffff'
>>> token_controller.save(user_access_token, user_refresh_token, user_id)
>>> connection.commit()

Now, try finding the newly created first demo instance using the session of ‘demo@porper.com’ user.

>>> demo_controller.find_all(user_access_token, demo_find_params)
Exception: not permitted

As you may expect, it will raise an exception of ‘not permitted’ because this user was not granted access to read that instance. So let’s give the ‘read’ permission to this user. You have to use ‘admin_access_token’ because this user doesn’t have a permission to grant permissions.

>>> permission_params = {
      "user_id": user_id,
      "resource": "demo",
      "value": "1",
      "action": "read"
    }
>>> permission_controller.create(admin_access_token, permission_params)
>>> connection.commit()

Let’s try again to find the newly created demo instance using this users session. As you’ll see, this user now can retrieve the instance.

>>> demo_controller.find_all(user_access_token, demo_find_params)
[{'description': 'first instance', 'id': '1', 'name': 'first'}]

So far, I explained the very basic features of the Porper library to control the access to resources based on user roles. Please see the main repository for this library, Porper-Core, for additional  features. Using this library will make it easy to implement resource management by just granting/revoking appropriate permissions to users and their roles.

You can also choose the RESTful interfaces implemented in Porper-API project, which is built using AWS Lambda and API Gateway along with supporting OpenID authentication using Google+ and GitHub accounts. In addition, please check out the Porper-UI project, which present a basic ReactJS UI application that interfaces with Porper-API to manage a sample resource with Google+ and GitHub authentication.

Alex is a CTO Architect in the CTO Architecture Team, which is responsible for researching and evaluating innovative technologies and processes. He has been working at Sungard Availability Services for more than 14 years.
Before joining this team, he worked on developing public/private cloud orchestration platform and integrating various applications to automate the processes in managed hosting service companies including Sungard Availability Services. Prior to the managed hosting companies, he worked at Samsung for 9 years in developing a reporting tool and RDBMS engine.


Comments are closed.