9
votes

I am studying about various types of access control models and came across to know that and are the popular ones.

I've a basic scenario for one of my project and I couldn't understand should I go with RBACor ABAC. Obviously RBAC is subset of ABACso definitely I should go for ABAC but ABAC requires some experience to write polices in . We are using WSO IS and APIM.

I have admin, owner and member roles in my identity server (IS).

  • Admin can view, delete and update users.
  • Owners can view and update.
  • Members can view only.

At a moment I am using HTTP verbs to achieve desire results i.e. owners can not access DELETE requests and members can't access PUT & DELETE.

Problem

I have a dashboard where I am displaying different sections like top-users, billing, services, top-consumers etc.

  1. I need to populate nav-bar based on user role and attributes from server e.g. members should not have access to see other users (Add, List) in nav-bar. nav-bar items dependents on user role so we can manage them via RBAC?
  2. We've a plan to add roles like ops, marketing, support etc. Does this means we need to create a separate db-schema to maintain access rights for each role?
  3. In dashboard I need to hide/show view, update and delete buttons in users, services etc. Now members can see users but have no permission to update or delete them. The can not view stats, billing and other private information.
  4. Owners can see all users related to their departments/organization but Admin can see all the users for all departments/organization. Here we need to consume same api for all consumers but api should response differently for different roles. Roles can be 10s and 100s so ee can not create different apis for each role.

Question

We can implement all these scenarios via RBAC but for managing nav-bar and view related implementation we need to add business logic in our server instead of using WSO2-IS and WSO2-APIM. Is there any way to manage view permissions like hide/show buttons and sections and even consume same API but it should return different result for different api-consumers.

2

2 Answers

9
votes

First of all my apologies for the late response. Here are my comments inline.

ACL, RBAC, ABAC

I am studying about various types of access control models and came across to know that abac and rbac are the popular ones.

Historically access control has been tackled through access control lists (ACL), then role-based access control (RBAC) and lately attribute-based access control (ABAC). ACLs grew unwieldy and hard to manage which is why NIST came up with RBAC in 1992 (yep it's that old). RBAC is well-known, mature, and built into most IAM products and applications. For instance, a user directory (LDAP, AD...) maintains users and role assignements and provides applications with those roles which the app can then use to determine whether access should be granted. With RBAC, finer-grained access (e.g. access based on relationships as in your case whereby a user can only see their own data) is impossible so either (a) the application developer writes custom code to achieve the right access or (b) you use ABAC.

Why ABAC?

ABAC gives you the ability to define fine-grained access based on any kind of attribute (not just role and not just user attributes) by using policies to describe what can (or cannot) happen. ABAC is sometimes called PBAC (policy-based access control). You refer to XACML which is the language in which ABAC policies are implemented. You can also look into (Wikipedia), a simpler language that maps directly into XACML.

ABAC also defines an architecture with the idea of a Policy Decision Point (PDP) which processes your authorization requests against the policies it's been configured with. The PDP (in your case WSO2 Balana part of WSO2 IS) is called from a Policy Enforcement Point (PEP) such as your app or something sitting in front of your app (e.g. an API gateway or interceptor in your case WSO2 API Manager).

The ABAC Architecture

Your use case

I've a basic scenario for one of my project and I couldn't understand should I go with RBACor ABAC. Obviously RBAC is subset of ABACso definitely I should go for ABAC but ABAC requires some experience to write polices in xacml. We are using WSO IS and APIM.

I wouldn't say RBAC is a subset of ABAC. It is indeed from a functionality perspective. But it's not one vs. the other. ABAC will extend RBAC by introducing more attributes, policies, and the aforementioned architecture.

I have admin, owner and member roles in my identity server (IS).

  • Admin can view, delete and update users.
  • Owners can view and update.
  • Members can view only.

This is great. What you are doing is defining your authorization requirements. These will map directly into your ALFA / XACML policies.

At a moment I am using HTTP verbs to achieve desire results i.e. owners can not access DELETE requests and members can't access PUT & DELETE.

In ABAC, we also use actions. These could be plain old human actions (view, edit, delete, approve...) which can then be mapped to the HTTP verbs.

Your challenge

In your text below, I marked in bold what I consider to be your additional authorization requirements.

I have a dashboard where I am displaying different sections like top-users, billing, services, top-consumers etc.

I need to populate nav-bar based on user role and attributes from server e.g. members should not have access to see other users (Add, List) in nav-bar. nav-bar items dependents on user role so we can manage them via RBAC?

This would be handled via an ABAC policy. See below

We've a plan to add roles like ops, marketing, support etc. Does this means we need to create a separate db-schema to maintain access rights for each role?

No! You should not have to create new DB schemas let alone maintain access rights in custom-built systems. Use the policies to do so.

In dashboard I need to hide/show view, update and delete buttons in users, services etc. Now members can see users but have no permission to update or delete them. They cannot view stats, billing and other private information.

Owners can see all users related to their departments/organization but Admin can see all the users for all departments/organization. Here we need to consume the same API for all consumers but api should respond differently for different roles. Roles can be 10s and 100s so ee can not create different apis for each role. Question

We can implement all these scenarios via RBAC but for managing nav-bar and view related implementation we need to add business logic in our server instead of using WSO2-IS and WSO2-APIM. Is there any way to manage view permissions like hide/show buttons and sections and even consume same API but it should return different result for different api-consumers.

Yes, definitely. This is the purpose of using ABAC and policies. Given you are using WSO2 IS, look into Balana, the PDP inside of that product. There are other solutions e.g. AuthZForce (open source) or Axiomatics (where I work)

The solution

Here is a sample policy written in ALFA and the XACML translation below

namespace haris {
    /**
     * User Records
     */
    policyset users {
        target clause axiomatics.objectType == "user record"
        apply firstApplicable
        /**
         * View user record
         */
        policy viewUser {
            target clause axiomatics.actionId == "view" // This can be the HTTP verb
            apply firstApplicable

            /**
             * Administrators can view all users
             */
            rule administrator{
                 target clause axiomatics.user.role == "administrator"
                permit
            }
            /**
             * Owners can view users in their department
             */
            rule owners{
                 target clause axiomatics.user.role == "owner"
                 permit
                 condition axiomatics.user.department == axiomatics.record.department
             }
            /**
             * Members can view their own user record only
             */
            rule member{
                  permit
                  condition axiomatics.user.username == axiomatics.record.owner
            }
        }
        /**
         * Update user
         */
        policy updateUser {
            target clause axiomatics.actionId == "update" // This can be the HTTP verb
            apply firstApplicable

            /**
             * Administrator can update any user
             */
            rule administrator{
                target clause axiomatics.user.role == "administrator"
                permit
            }
            /**
             * Owner can update any user
             */
            rule owner{
                target clause axiomatics.user.role == "owner"
                permit
                // TODO: determine what an owner can update
            }
        }
        /**
         * Delete user
         */
        policy deleteUser {
            target clause axiomatics.actionId == "delete" // This can be the HTTP verb
            apply firstApplicable
            /**
             * Administrator can delete any user
             */            
            rule administrator{
                target clause axiomatics.user.role == "administrator"
                permit                
            }
        }
    }
}

And the XML version

<?xml version="1.0" encoding="UTF-8"?><!--This file was generated by the 
    ALFA Plugin for Eclipse from Axiomatics AB (http://www.axiomatics.com). --><!--Any modification to this file will 
    be lost upon recompilation of the source ALFA file -->
<xacml3:PolicySet
    PolicyCombiningAlgId="urn:oasis:names:tc:xacml:1.0:policy-combining-algorithm:first-applicable"
    PolicySetId="http://axiomatics.com/alfa/identifier/haris.users"
    Version="1.0"
    xmlns:xacml3="urn:oasis:names:tc:xacml:3.0:core:schema:wd-17">
    <xacml3:Description>User Records</xacml3:Description>
    <xacml3:PolicySetDefaults>
        <xacml3:XPathVersion>http://www.w3.org/TR/1999/REC-xpath-19991116
        </xacml3:XPathVersion>
    </xacml3:PolicySetDefaults>
    <xacml3:Target>
        <xacml3:AnyOf>
            <xacml3:AllOf>
                <xacml3:Match
                    MatchId="urn:oasis:names:tc:xacml:1.0:function:string-equal">
                    <xacml3:AttributeValue
                        DataType="http://www.w3.org/2001/XMLSchema#string">user record</xacml3:AttributeValue>
                    <xacml3:AttributeDesignator
                        AttributeId="axiomatics.objectType"
                        Category="urn:oasis:names:tc:xacml:3.0:attribute-category:resource"
                        DataType="http://www.w3.org/2001/XMLSchema#string"
                        MustBePresent="false" />
                </xacml3:Match>
            </xacml3:AllOf>
        </xacml3:AnyOf>
    </xacml3:Target>
    <xacml3:Policy
        PolicyId="http://axiomatics.com/alfa/identifier/haris.users.viewUser"
        RuleCombiningAlgId="urn:oasis:names:tc:xacml:1.0:rule-combining-algorithm:first-applicable"
        Version="1.0">
        <xacml3:Description>View user record</xacml3:Description>
        <xacml3:PolicyDefaults>
            <xacml3:XPathVersion>http://www.w3.org/TR/1999/REC-xpath-19991116
            </xacml3:XPathVersion>
        </xacml3:PolicyDefaults>
        <xacml3:Target>
            <xacml3:AnyOf>
                <xacml3:AllOf>
                    <xacml3:Match
                        MatchId="urn:oasis:names:tc:xacml:1.0:function:string-equal">
                        <xacml3:AttributeValue
                            DataType="http://www.w3.org/2001/XMLSchema#string">view</xacml3:AttributeValue>
                        <xacml3:AttributeDesignator
                            AttributeId="axiomatics.actionId"
                            Category="urn:oasis:names:tc:xacml:3.0:attribute-category:action"
                            DataType="http://www.w3.org/2001/XMLSchema#string"
                            MustBePresent="false" />
                    </xacml3:Match>
                </xacml3:AllOf>
            </xacml3:AnyOf>
        </xacml3:Target>
        <xacml3:Rule Effect="Permit"
            RuleId="haris.users.viewUser.administrator">
            <xacml3:Description>Administrators can view all users
            </xacml3:Description>
            <xacml3:Target>
                <xacml3:AnyOf>
                    <xacml3:AllOf>
                        <xacml3:Match
                            MatchId="urn:oasis:names:tc:xacml:1.0:function:string-equal">
                            <xacml3:AttributeValue
                                DataType="http://www.w3.org/2001/XMLSchema#string">administrator</xacml3:AttributeValue>
                            <xacml3:AttributeDesignator
                                AttributeId="axiomatics.user.role"
                                Category="urn:oasis:names:tc:xacml:1.0:subject-category:access-subject"
                                DataType="http://www.w3.org/2001/XMLSchema#string"
                                MustBePresent="false" />
                        </xacml3:Match>
                    </xacml3:AllOf>
                </xacml3:AnyOf>
            </xacml3:Target>
        </xacml3:Rule>
        <xacml3:Rule Effect="Permit"
            RuleId="haris.users.viewUser.owners">
            <xacml3:Description>Owners can view users in their department
            </xacml3:Description>
            <xacml3:Target>
                <xacml3:AnyOf>
                    <xacml3:AllOf>
                        <xacml3:Match
                            MatchId="urn:oasis:names:tc:xacml:1.0:function:string-equal">
                            <xacml3:AttributeValue
                                DataType="http://www.w3.org/2001/XMLSchema#string">owner</xacml3:AttributeValue>
                            <xacml3:AttributeDesignator
                                AttributeId="axiomatics.user.role"
                                Category="urn:oasis:names:tc:xacml:1.0:subject-category:access-subject"
                                DataType="http://www.w3.org/2001/XMLSchema#string"
                                MustBePresent="false" />
                        </xacml3:Match>
                    </xacml3:AllOf>
                </xacml3:AnyOf>
            </xacml3:Target>
            <xacml3:Condition>
                <xacml3:Apply
                    FunctionId="urn:oasis:names:tc:xacml:3.0:function:any-of-any">
                    <xacml3:Function
                        FunctionId="urn:oasis:names:tc:xacml:1.0:function:string-equal" />
                    <xacml3:AttributeDesignator
                        AttributeId="axiomatics.user.department"
                        Category="urn:oasis:names:tc:xacml:1.0:subject-category:access-subject"
                        DataType="http://www.w3.org/2001/XMLSchema#string"
                        MustBePresent="false" />
                    <xacml3:AttributeDesignator
                        AttributeId="axiomatics.record.department"
                        Category="urn:oasis:names:tc:xacml:3.0:attribute-category:resource"
                        DataType="http://www.w3.org/2001/XMLSchema#string"
                        MustBePresent="false" />
                </xacml3:Apply>
            </xacml3:Condition>
        </xacml3:Rule>
        <xacml3:Rule Effect="Permit"
            RuleId="haris.users.viewUser.member">
            <xacml3:Description>Members can view their own user record only
            </xacml3:Description>
            <xacml3:Target />
            <xacml3:Condition>
                <xacml3:Apply
                    FunctionId="urn:oasis:names:tc:xacml:3.0:function:any-of-any">
                    <xacml3:Function
                        FunctionId="urn:oasis:names:tc:xacml:1.0:function:string-equal" />
                    <xacml3:AttributeDesignator
                        AttributeId="axiomatics.user.username"
                        Category="urn:oasis:names:tc:xacml:1.0:subject-category:access-subject"
                        DataType="http://www.w3.org/2001/XMLSchema#string"
                        MustBePresent="false" />
                    <xacml3:AttributeDesignator
                        AttributeId="axiomatics.record.owner"
                        Category="urn:oasis:names:tc:xacml:3.0:attribute-category:resource"
                        DataType="http://www.w3.org/2001/XMLSchema#string"
                        MustBePresent="false" />
                </xacml3:Apply>
            </xacml3:Condition>
        </xacml3:Rule>
    </xacml3:Policy>
    <xacml3:Policy
        PolicyId="http://axiomatics.com/alfa/identifier/haris.users.updateUser"
        RuleCombiningAlgId="urn:oasis:names:tc:xacml:1.0:rule-combining-algorithm:first-applicable"
        Version="1.0">
        <xacml3:Description>Update user</xacml3:Description>
        <xacml3:PolicyDefaults>
            <xacml3:XPathVersion>http://www.w3.org/TR/1999/REC-xpath-19991116
            </xacml3:XPathVersion>
        </xacml3:PolicyDefaults>
        <xacml3:Target>
            <xacml3:AnyOf>
                <xacml3:AllOf>
                    <xacml3:Match
                        MatchId="urn:oasis:names:tc:xacml:1.0:function:string-equal">
                        <xacml3:AttributeValue
                            DataType="http://www.w3.org/2001/XMLSchema#string">update</xacml3:AttributeValue>
                        <xacml3:AttributeDesignator
                            AttributeId="axiomatics.actionId"
                            Category="urn:oasis:names:tc:xacml:3.0:attribute-category:action"
                            DataType="http://www.w3.org/2001/XMLSchema#string"
                            MustBePresent="false" />
                    </xacml3:Match>
                </xacml3:AllOf>
            </xacml3:AnyOf>
        </xacml3:Target>
        <xacml3:Rule Effect="Permit"
            RuleId="haris.users.updateUser.administrator">
            <xacml3:Description>Administrator can update any user
            </xacml3:Description>
            <xacml3:Target>
                <xacml3:AnyOf>
                    <xacml3:AllOf>
                        <xacml3:Match
                            MatchId="urn:oasis:names:tc:xacml:1.0:function:string-equal">
                            <xacml3:AttributeValue
                                DataType="http://www.w3.org/2001/XMLSchema#string">administrator</xacml3:AttributeValue>
                            <xacml3:AttributeDesignator
                                AttributeId="axiomatics.user.role"
                                Category="urn:oasis:names:tc:xacml:1.0:subject-category:access-subject"
                                DataType="http://www.w3.org/2001/XMLSchema#string"
                                MustBePresent="false" />
                        </xacml3:Match>
                    </xacml3:AllOf>
                </xacml3:AnyOf>
            </xacml3:Target>
        </xacml3:Rule>
        <xacml3:Rule Effect="Permit"
            RuleId="haris.users.updateUser.owner">
            <xacml3:Description>Owner can update any user</xacml3:Description>
            <xacml3:Target>
                <xacml3:AnyOf>
                    <xacml3:AllOf>
                        <xacml3:Match
                            MatchId="urn:oasis:names:tc:xacml:1.0:function:string-equal">
                            <xacml3:AttributeValue
                                DataType="http://www.w3.org/2001/XMLSchema#string">owner</xacml3:AttributeValue>
                            <xacml3:AttributeDesignator
                                AttributeId="axiomatics.user.role"
                                Category="urn:oasis:names:tc:xacml:1.0:subject-category:access-subject"
                                DataType="http://www.w3.org/2001/XMLSchema#string"
                                MustBePresent="false" />
                        </xacml3:Match>
                    </xacml3:AllOf>
                </xacml3:AnyOf>
            </xacml3:Target>
        </xacml3:Rule>
    </xacml3:Policy>
    <xacml3:Policy
        PolicyId="http://axiomatics.com/alfa/identifier/haris.users.deleteUser"
        RuleCombiningAlgId="urn:oasis:names:tc:xacml:1.0:rule-combining-algorithm:first-applicable"
        Version="1.0">
        <xacml3:Description>Delete user</xacml3:Description>
        <xacml3:PolicyDefaults>
            <xacml3:XPathVersion>http://www.w3.org/TR/1999/REC-xpath-19991116
            </xacml3:XPathVersion>
        </xacml3:PolicyDefaults>
        <xacml3:Target>
            <xacml3:AnyOf>
                <xacml3:AllOf>
                    <xacml3:Match
                        MatchId="urn:oasis:names:tc:xacml:1.0:function:string-equal">
                        <xacml3:AttributeValue
                            DataType="http://www.w3.org/2001/XMLSchema#string">delete</xacml3:AttributeValue>
                        <xacml3:AttributeDesignator
                            AttributeId="axiomatics.actionId"
                            Category="urn:oasis:names:tc:xacml:3.0:attribute-category:action"
                            DataType="http://www.w3.org/2001/XMLSchema#string"
                            MustBePresent="false" />
                    </xacml3:Match>
                </xacml3:AllOf>
            </xacml3:AnyOf>
        </xacml3:Target>
        <xacml3:Rule Effect="Permit"
            RuleId="haris.users.deleteUser.administrator">
            <xacml3:Description>Administrator can delete any user
            </xacml3:Description>
            <xacml3:Target>
                <xacml3:AnyOf>
                    <xacml3:AllOf>
                        <xacml3:Match
                            MatchId="urn:oasis:names:tc:xacml:1.0:function:string-equal">
                            <xacml3:AttributeValue
                                DataType="http://www.w3.org/2001/XMLSchema#string">administrator</xacml3:AttributeValue>
                            <xacml3:AttributeDesignator
                                AttributeId="axiomatics.user.role"
                                Category="urn:oasis:names:tc:xacml:1.0:subject-category:access-subject"
                                DataType="http://www.w3.org/2001/XMLSchema#string"
                                MustBePresent="false" />
                        </xacml3:Match>
                    </xacml3:AllOf>
                </xacml3:AnyOf>
            </xacml3:Target>
        </xacml3:Rule>
    </xacml3:Policy>
</xacml3:PolicySet>

Enforcing the policies

How will I return different data for a single api but for different roles/users.

Let's assume you have an API e.g. /api/profiles/{profileID}. There are 2 ways you could use the API:

  • GET /api/profiles would return all the profiles the user is entitled to
  • GET /api/profiles/123 would return profile 123 if the user is entitled to or HTTP 403 otherwise (or 404 - you could argue you do not want to even reveal that the said profile does exist).

To do so, you need to implement a Policy Enforcement Point (PEP). This could be WSO2's API Manager. The PEP is responsible for

  1. parsing the incoming API call (GET /api/profile/123)
  2. converting it into an authorization request e.g. Can Alice view profile 123?
  3. sending the request to the PDP
  4. processing the response received back from the PDP - and notably extracting the decision (e.g. Permit).

If the decision is a Permit then the call is forwarded to your backend API. If it isn't you can return HTTP 403 / 404 as discussed.

If it is a 403, then the call does go to the backend and eventually a response is turned from your backend and goes through the PEP where it can once again call the PDP, for instance to redact data.

Do I need to involve business logic in my server like getting nav-bar items, getting api-usage stats, full data access for admins and organization/department for owners and restricted data for members. How to perform these basic operations?

No you don't. When building menus or navigation items, you can also invoke the PDP and ask whether a given user can get access to a given set of functionality e.g. "Can Alice view nav-bar item #123?". You need minimal business logic to invoke the PDP.

2
votes

After some observation I can think of one thing.

Use above WSO2 APIM api's to get swagger.json for given API (these should/will have all the available api's). Now use relevant HTTP-verbs to map resources with roles and response.

E.g. if members shouldn't have access to DELETE then using this approach we can ask for server to return all permissions for current page/view and map those values in front-end to hide/show button/views or whole content.

Downside: To avoid duplication and repetition we can save these mapping in our database. But this logic requires some business logic in your own server and access to database read/write operations.