You can implement Role-Based Access Control (RBAC) with Repose using the Authentication and Authorization mechanisms. This guide takes you through the process of setting up RBAC with Repose.

1. Configure the Header Normalization filter

To prevent users from submitting their own roles, you will need to blacklist headers using the Header Normalization filter.

header-normalization.cfg.xml
<header-filters>
...
    <target>
        <blacklist id="ReposeHeaders">
            <header id="X-Authorization"/>
            <header id="X-Identity-Status"/>
            <header id="X-Impersonator-Id"/>
            <header id="X-Impersonator-Name"/>
            <header id="X-PP-Groups" />
            <header id="X-PP-User" />
            <header id="X-Roles"/>
            <header id="X-Tenant-Id"/>
            <header id="X-Tenant-Name"/>
            <header id="X-User-Id"/>
            <header id="X-User-Name"/>
            <header id="X-Map-Roles"/>
            <header id="X-Catalog"/>
        </blacklist>
    <target>
</header-filters>

Please refer to the Header Normalization filter documentation for more information about the available configuration options.

2. Configure the Authentication filter

The Authentication filter will grab the user’s roles from their authentication token and return those roles to Repose.

Please refer to the Keystone v2 filter documentation for more information about the available configuration options.

3. Configure the Authorization filter

The Authorization filter will validate that the user has access to the tenanted resource being requested. If the user is authorized to access a tenanted resource, the user’s data in the request (e.g., the X-Roles, X-Tenant-Id, and X-Map-Roles headers) will be culled to those relevant to the requested resource. Endpoint validation can also be performed, if desired, but is outside the scope of RBAC.

Please refer to the Keystone v2 Authorization filter documentation for more information about the available configuration options.

4. Configure the RBAC filter

There are are a few mechanisms for performing the enforcement. The decision will come down to how complex your API is and the complexity of the authorization scheme.

1. What do you need?

We recommend that you build a table similar to the example below that contains endpoints and the roles that you wish to allow access to those endpoints.

Capability Role

Method name

Endpoint

HTTP Method

a:creator

a:observer

a:admin

Create New Widget

/a/

POST

x

 

x

List Widgets

/a/

GET

 

x

x

Replace Widget

/a/

PUT

x

x

x

Delete Widget

/a/

DELETE

 

 

x

2. Decide on the right filter for you.

From here you have to decide which filter you want to use. If your API is minimal or your just getting started with it, then you might find the Simple RBAC filter most useful. It uses a very simple Domain Specific Language (DSL) that is similar to what other tools use for this basic mechanism. Another powerful, but easy to configure tool is the RegEx RBAC filter. This filter uses a similar configuration, but the resources are defined with Java Regular Expressions. If on the other had your API is large and/or your authorizations are complex, then you will need the heavy lifting of the API Validator filter which uses a WADL to fully define the API. As with anything, the more bells and whistles you need, the more complex the configuration will be.

1. Simple RBAC filter

The Simple RBAC filter is configured using a Domain Specific Language (DSL) similar to the table above.

simple-rbac.cfg.xml
<?xml version="1.0" encoding="UTF-8"?>
<simple-rbac xmlns="http://docs.openrepose.org/repose/simple-rbac/v1.0">
    <resources>
/path/to/this   GET     role1,role2,role3,role4
/path/to/this   PUT     role1,role2,role3
/path/to/this   POST    role1,role2
/path/to/this   DELETE  role1
/path/to/that   GET,PUT ALL
/path/to/that   ALL     role1
/path/{to}/wild GET     role1
    </resources>
</simple-rbac>

The best part about this filter is that if your API grows or simply becomes to complex for the Simple RBAC filter, then you can easily move to the full API Validator filter later. There is even a setting available to save your Simple RBAC filter configuration in a manner that you can use it immediately with the API Validator filter.

Please refer to the Simple RBAC filter documentation for more information about the available configuration options.

2. RegEx RBAC filter

The RegEx RBAC filter is configured using a Domain Specific Language (DSL) similar to the one above.

regex-rbac.cfg.xml
<?xml version="1.0" encoding="UTF-8"?>
<regex-rbac xmlns="http://docs.openrepose.org/repose/simple-rbac/v1.0">
    <resources>
/path/[^/]+/this   GET     role1,role2,role3,role4
/path/[^/]+/this   put     role1,role2,role3
/path/[^/]+/this   POST    role1,role2
/path/[^/]+/this   DELETE  role1
/path/[^/]+/that   get,PUT ANY
/Path/[^/]+        GET     admin,role1
/Path/[^/]+        GET     admin,role2
/Path/[^/]+/.*     ALL     admin,role with space
    </resources>
</regex-rbac>

This filter provides an RBAC mechanism for API’s that don’t conveniently fit into the restrictions imposed by the WADL specification.

Please refer to the RegEx RBAC filter documentation for more information about the available configuration options.

3. API Validator filter

If your API is complex or you simply need or are already using some of the extra features available in the API Validator filter, then this is the choice for you.

1. Enable RAX-Roles

When the enable-rax-roles attribute for the API Validator filter is set to true, the check-headers attribute will also be enabled regardless of your setting.

validator.cfg.xml
<?xml version="1.1" encoding="UTF-8"?>
<validators multi-role-match="true" xmlns='http://openrepose.org/repose/validator/v1.0' version="1">
    <validator role="default"
               default="true"
               wadl="file:///my/wadl/filewithraxroles.wadl"
               dot-output="/tmp/default.dot"
               enable-rax-roles="true"
    />
</validators>
2. Utilize RAX-Roles

In the WADL, include rax:roles with appropriate values to ensure access is controlled as expected. When defining rax:roles at the resource level, be aware that all sub-resources and methods will inherit the roles allowed at the resource level. Multiple roles can be specified by separating the role names with a space. If multiple roles are authorized for a resource and method, the user must have one of the allowed roles but is not required to have all roles. The following example shows a WADL for use with the API Validator filter that is configured for RBAC.

api_with_roles.wadl
<application xmlns="http://wadl.dev.java.net/2009/02" xmlns:rax="http://docs.rackspace.com/api">
    <resources base="https://test.api.openstack.com">
        <resource path="/a" rax:roles="a:admin">
            <method name="POST" rax:roles="a:creator">
                <request>
                    <representation mediaType="application/xml"/>
                </request>
            </method>
            <method name="GET" rax:roles="a:observer">
                <request>
                    <representation mediaType="application/xml"/>
                </request>
            </method>
            <method name="PUT" rax:roles="a:observer a:creator">
                <request>
                    <representation mediaType="application/xml"/>
                </request>
            </method>
            <method name="DELETE">
                <request>
                    <representation mediaType="application/xml"/>
                </request>
            </method>
        </resource>
    </resources>
</application>

With the above WADL and API Validator filter configuration, the following behavior will apply for a request given the user has the a:observer role in the X-Roles header.

  • GET or PUT is allowed.

  • DELETE will return Forbidden (403) as the DELETE method inherits the a:admin role from its parent resource.

  • PATCH will return Method Not Allowed (405).

  • POST will return a Forbidden (403), as the method is allowed for the resource but the user does not have the a:admin or the a:creator role.

Please refer to the API Validator filter documentation for more information about the available configuration options.

Table 1. Return codes and conditions
Description Response Code Returned When:

Forbidden

403

A requested resource or method requires a specific X-Roles header and that header is not found.

Method Not Allowed

405

The URI is valid, but the method is not appropriate for the URI.

The status codes returned by authorization failures, via rax:roles extensions (403), differs from the statuses returned when roles are defined directly in the validator.cfg.xml (404 and 405).

3. Utilize Tenanted RAX-Roles

In the WADL, add to the rax:roles the name of appropriate values to ensure access is controlled as expected. As always, rax:roles is a space separated list of names. Also if they are defined at the resource level, all sub-resources and methods will inherit the roles allowed at the resource level. If multiple roles are authorized for a resource and method, the user must have one of the allowed roles but is not required to have all roles. The following example shows a WADL for use with the API Validator filter that is configured for Tenanted RBAC.

api_with_tenanted_roles.wadl
<application xmlns="http://wadl.dev.java.net/2009/02" xmlns:rax="http://docs.rackspace.com/api">
    <resources base="https://test.api.openstack.com">
        <resource path="/a" rax:roles="a:admin/{X-TENANT-ID}">
            <method name="POST" rax:roles="a:creator/{X-TENANT-ID}">
                <request>
                    <param name="X-TENANT-ID"
                           style="header"
                           required="true"
                           type="xsd:int"
                           repeating="true"
                           rax:isTenant="true"/>
                </request>
            </method>
            <method name="GET" rax:roles="a:observer/{X-TENANT-ID}">
                <request>
                    <param name="X-TENANT-ID"
                           style="header"
                           required="true"
                           type="xsd:int"
                           repeating="true"
                           rax:isTenant="true"/>
                </request>
            </method>
            <method name="PUT" rax:roles="a:observer/{X-TENANT-ID} a:creator/{X-TENANT-ID}">
                <request>
                    <param name="X-TENANT-ID"
                           style="header"
                           required="true"
                           type="xsd:int"
                           repeating="true"
                           rax:isTenant="true"/>
                </request>
            </method>
            <method name="DELETE">
                <request>
                    <param name="X-TENANT-ID"
                           style="header"
                           required="true"
                           type="xsd:int"
                           repeating="true"
                           rax:isTenant="true"/>
                </request>
            </method>
        </resource>
    </resources>
</application>

With the above WADL and API Validator filter configuration, the following behavior will apply for a request given the user has the a:observer role in the X-Map-Roles header for a tenant identified in X-Tenant-Id header.

  • GET or PUT is allowed.

  • DELETE will return Forbidden (403) as the DELETE method inherits the a:admin role from its parent resource.

  • PATCH will return Method Not Allowed (405).

  • POST will return a Forbidden (403), as the method is allowed for the resource but the user does not have the a:admin or the a:creator role.

Please refer to the API Validator filter documentation for more information about the available configuration options.

Table 2. Return codes and conditions
Description Response Code Returned When:

Forbidden

403

A requested resource or method requires a specific X-Roles header and that header is not found.

Method Not Allowed

405

The URI is valid, but the method is not appropriate for the URI.

The case of the actual header does not matter, but the case of the rax:roles tenant must match the case of the param element’s name attribute.

The difference between the regular RAX-Roles and the Tenanted RAX-Roles is subtle from a configuration standpoint, but they have very different expectations and outcomes. The regular RAX-Roles uses the X-Roles header which is simply a plain text splitable header that contains all of the roles associated with the user. The Tenanted RAX-Roles uses the X-Map-Roles header which is a base 64 encoded JSON map of strings to arrays of strings (e.g., the base 64 encoding of {"someTenant": ["someRole", "sharedRole"], "otherTenant": ["otherRole", "sharedRole"]}). This allows for much more precise control over what access a user has to an API based on the current context (i.e. tenant) they are accessing it for.

4. Enable Tenant Culling based on Relevant Roles

1. Do you need only Relevant Tenants?

If your origin service requires the X-Tenant-Id header to contain only the tenant id’s pertinent to the RBAC Authorization roles that were provided in the X-Relevant-Roles header, then enable the Tenant Culling filter.

2. How to enable Tenant Culling

The following example shows a basic System Model that enables the Tenant Culling filter.

system-model.cfg.xml
<?xml version="1.0" encoding="UTF-8"?>

<system-model xmlns="http://docs.openrepose.org/repose/system-model/v2.0">
    <repose-cluster id="repose">
        <nodes>
            <node id="repose_node1" hostname="localhost" http-port="8080"/>
        </nodes>

        <filters>
            <filter name="header-normalization"/>
            <filter name="keystone-v2"/>
            <filter name="simple-rbac"/>
            <filter name="tenant-culling"/>
        </filters>

        <destinations>
            <endpoint id="local" protocol="http" hostname="localhost" root-path="/" port="8000" default="true"/>
        </destinations>
    </repose-cluster>
</system-model>

There is no further configuration of this feature. Simply by including the Tenant Culling filter in the System Model after the Authentication and RBAC filters, it is enabled.

Even though the Header Normalization filter isn’t strictly required for tenant culling to work, it is a good idea to always include it before the first Authentication or Authorization filter.

Please refer to the Tenant Culling filter documentation for more information about this feature.