Working with multi-tenant roles

When provisioning users in the multi-tenant environment, you have many organizations in your organization structure and your users belong to different organizations. Their access rights are combinations of roles AND tenants to which they belong. For example you can use a directory system, where users are provisioned, and assigned into groups, which are created and managed per-tenant. The number of application access rights is multiplied by the number of the tenants. How can we avoid role explosion to cover the multi-tenant roles?

Let’s start with a simple example: customer has an application with 4 access rights (Read only user, Content manager, Content reviewer and Administrator) which are implemented as groups in directory system (LDAP or Active Directory). The application is multi-tenant, so one user can be the Content manager for one tenant, but with no access rights for other tenants. Or, user can be Read only user for two tenants at the same time.

The user/groups data are stored in the already created container: “OU=MyTenants,DC=example,DC=com”:

  • the container: “OU=MyUsers,OU=MyTenants,DC=example,DC=com” should contain all users (flat)
  • the container: “OU=MyGroups,OU=MyTenants,DC=example,DC=com” should contain all groups for all tenants, but structured hierarchically

This means that groups for tenants “Tenant1, Tenant2 … TenantN” should be created in containers:

  • for Tenant1: “OU=Tenant1,OU=MyGroups,OU=MyTenants,DC=example,DC=com”
  • for Tenant2: “OU=Tenant2,OU=MyGroups,OU=MyTenants,DC=example,DC=com”
  • for TenantN: “OU=TenantN,OU=MyGroups,OU=MyTenants,DC=example,DC=com”

All these containers and the following groups must be created when new tenant is introduced (in this example for “Tenant1″ in the directory system):

  • for read-only access: “CN=ReadOnly_Tenant1,OU=Tenant1,OU=MyTenants,DC=example,DC=com”
  • for content manager access: “CN=ContentManager_Tenant1,OU=Tenant1,OU=MyTenants,DC=example,DC=com”
  • for content reviewer access: “CN=ContentReviewer_Tenant1,OU=Tenant1,OU=MyTenants,DC=example,DC=com”
  • for admin access: “CN=Admin_Tenant1,OU=Tenant1,OU=MyTenants,DC=example,DC=com”

The groups above are named “CN=<groupname>_<tenantId>” because this was my real life scenario. The same naming convention is used for both “CN” and “sAMAccountName” attributes (in Active Directory). There are numerous other alternatives such as having tenant information in one attribute and keep the group name randomized etc.

As you see, such directory structure (container and 4 groups) should be created when creating tenant. This can be done manually or we can use our generic synchronization concept of assignments and create the structure with midPoint!

Regarding the roles, obviously, I would like to have midPoint roles for:

  • Read-only access
  • Content Manager access
  • Content Reviewer access
  • Admin access

That’s four roles. But you may ask: wait, we now have many tenants, do we need to have different roles for each tenant?

No, we don’t! We will create only four roles at all.

Let’s see an example of such role – “Read-only tenant access”. We suppose:

  • the groups named “ReadOnly_” already exist (and the value is present in both “CN” and “sAMAccountName” attributes (in case of Active Directory))

The tenant “Tenant1″ is already created in midPoint with name “Tenant1″ and “tenant” checkbox checked:
Tenant - in organizational structure editor

The role definition will work for midPoint 3.1 without any problems (for 3.0.1 the query in “associationTargetSearch” can never return null so the expressions needs a bit of tweaking):

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
<role oid="00000000-0000-0000-9999-000000000001"
    <name>Read-only tenant access</name>
    <inducement>
        <construction>
            <resourceRef oid="00000000-0000-0000-0001-000000000001" type="c:ResourceType"/>
        <kind>account</kind>
        <association>
            <ref>ri:adGroups</ref>
            <outbound>
                <source>
                    <name>tenantRef</name>
                    <path>$assignment/tenantRef</path>
                </source>
                <expression>
                    <associationTargetSearch>
                        <filter>
                            <q:equal>
                                <q:path>
                declare namespace icfs="http://midpoint.evolveum.com/xml/ns/public/connector/icf-1/resource-schema-3";
                declare namespace ri="http://midpoint.evolveum.com/xml/ns/public/resource/instance-3";
                attributes/ri:samAccountName
                </q:path>
                                <expression>
                                    <script>
                                        <code>
if (!basic.isEmpty(tenantRef)) {
  org = midpoint.getOrgByOid(tenantRef?.getOid())
  if (org != null) return 'ReadOnly_' + org?.getName()
}
</code>
                                    </script>
                                </expression>
                            </q:equal>
                        </filter>
                    <searchOnResource>true</searchOnResource>
                    </associationTargetSearch>
                </expression>
            </outbound>
        </association>
        </construction>
    </inducement
</role>

The role defined above will:

  • imply the (default) account on resource identified by oid “00000000-0000-0000-0001-000000000001″
  • use the role assignment parameter named “tenantRef” accessible as source variable “$assignment/tenantRef” to search for a group on the resource using “associationTargetSearch” query
  • if group with “sAMAccountName=ReadOnly_Tenant1″ exists, user will be put into that group on resource. This usage of “associationTargetSearch” will lookup the resource, but if your groups get moved in the directory tree, this will continue to work provided that the “sAMAccountName” attribute has not changed

Now all we need is to assign the role:
Assign tenant role - step 1

And before submitting, we need to set the “tenantRef” parameter (popup with all midPoint organizations marked as tenants will appear):
Assign tenant role - step 2

After saving the request, user account will be created on the resource and will be put into the correct group.

Leave a Reply

Your email address will not be published.