Multi-tenancy

The Intrexx multi-tenancy facilitates weak data separation in large, adjoined company structures.

If you have multiple tenants, the setup and usability (e.g. in intranets) is made easier in many ways. Multi-tenancy also provides valuable support for B2B and B2C.

Because the data separation is weak, having isolated filebox locations and exporting portals with data are impossible.

Multi Tenancy in the "User" module

The three objects

  • Country

  • Tenant

  • City

are the basis for multi-tenancy in the "User" module. In the schema manager, you can define your own new objects that can act as clients.

Create tenant dependency

With the following, very simple structure we will show you how can link use objects to tenants.

User A will be assigned to tenant "Organizational unit 1", User B to tenant "Organizational unit 2", and User C to both.

The two organizational units have no special features; they are created with the "Organizational unit" base class. As you can see, you do not have to use the tenant object to create a tenant.

In the first step, the user object, which should be assigned to a tenant, will be edited in the Schema Manager.

In our example, User A, B and C are to be assigned to a tenant. These users are based on the "User" class.

Once the class has been selected from the left-hand area of the Schema Manager, an attribute can be created for this purpose via the "Edit menu / New attribute". In our example, we will create the attribute "Organizational assignment".

If multiple tenants are assigned to one object (1:n), the "Text" type should be selected for the data field so that a list of tenant memberships can be saved. If there is only a 1:1 relationship and a string field is also selected as a reference attribute, then a string data field can also be used.

So that this attribute can be used to save the tenant ID, the "Contains tenant ID" is activated. Click on "Edit reference" to open a dialog where the reference can be edited.

For our example, we select the class "Organizational unit", because "Organizational unit 1" and "Organizational unit 2" were created on this basis, as was described at the beginning.

Any reference attribute can be specified, i.e. not only the LID or GUID, but also another attribute that contains a unique identification value (e.g. organization abbreviation, cost centre). The displayed attribute of the referenced class is also a free choice. In this case, we select the object name. So that User C can be assigned to both tenants, the "Support multiple values" option is selected here.

Close all dialogs with "OK". The tenant dependency has now been configured.

Tenant assignment

In the properties of a user, the client can now be assigned on the "Other attributes" tab. To do this, highlight the attribute, which was created for the tenant dependency and click on "Edit value" in the "Value" column.

In our example User A will be assigned to "Organizational unit 1", User B to "Organizational unit 2", and User C to both.

Multi-tenancy in applications

Tenant control in data groups

A strict tenant control is achieved using the tenant filter in data groups. There, the relationship between the user's tenant membership and the data record can be established. There is an n:1 binding directly in the data group or an n:n binding via a child data group, which enables multiple tenant assignments per data record.

Tenant control in filters

For tables, selection lists, multiple selection and data pickers, there is the "Is contained in clients" operator. If the UserID is compared, only the entries assigned to the corresponding tenant will be shown.

Tenant control in distribution lists

In the "Multiple selection" element, any objects can be selected to define a distribution list using the "Distribution list selection" setting. A filter can be applied to the objects to only provide users or roles for selection that have been assigned to the tenant of the current user, for example.

Groovy API

The Groovy API enables you to

  • create objects for defining tenants and organizational structures

  • create groups, roles, distribution lists and their memberships in other objects

  • edit all objects

  • delete all objects

Here is a script example:

// Create "Organisation"

def strTitle = g_record["GUID"].value /* datafield Title <string> */
def strParentNode = g_record["GUID"].value /* datafield Tenant <string> */

def organization1 = g_om.createContainer ("ORGANIZATION", {
       container      = strParentNode
       NAME           = strTitle
       EXTERNALGUID   = "1235"
       PRIORITY       = 25
       DN             = "testDN"
       DISABLED       = false
       DESCRIPTION    = "Organization created by Groovy around ${new Date()}"
       INTERNALUSN    = 2
       STATE          = "BW"
       POBOX          = "UP"
       STREET         = "UP-Alley"
       ISOCOUNTRYCODE = "DE"
       CITY           = "Freiburg"
       POSTALCODE     = "79098"
       COUNTRY        = "Germany"
})

// Create "default roles"
def role1 = g_om.createSet ("ROLE", {
	container      = organization1
	NAME           = strTitle + ": Employee"
	DESCRIPTION    = "Role created by Groovy on ${new Date()}"
	memberOf       = ["Users"]
})

// Create "additional roles"
def role2 = g_om.createSet ("ROLE", {
	container      = organization1
	NAME           = strTitle + ": User"
	DESCRIPTION    = "Role created by Groovy on ${new Date()}"
	memberOf       = ["Users"]
})

// Delete roles
g_om.deleteSet(role1.GUID)
 
// Delete container (All included objects will also be deleted!)
g_om.deleteContainer(containerBase1.GUID)

To identify the tenant memberships of a logged-in user and the tenant objects selected for the session, the GroovyTenantHelper is available in the Groovy context. When defining a GroovyTenantHelper object, the current session is stated as a parameter. Furthermore, the GroovyTenantHelper can be used to modify the selection of the active tenants in the session. The variants entered as a character string list (||) enable you to directly edit distribution list and multiple selection information from long text fields.

// Import of class
import de.uplanet.lucy.server.usermanager.groovy.GroovyTenantHelper

// Definition of a new TenantHelper object
def thelper = new  GroovyTenantHelper(g_session)

//
thelper.getTenants():Set<String>thelper.getSelectedTenants():Set<String>thelper.getAllTenantValues():String seperated by ||
thelper.getSelectedTenantValues():String seperated by ||

// Define an active tenant
thelper.setSelectedTenant(String)

// Define multiple active tenants - the definition is made by a list of tenants
thelper.setSelectedTenants(Set<String>)

//Define multiple active tenants - the definition
// of tenants is made in the form of a list as a string seperated by || 

thelper.setSelectedTenants(String)
thelper.setSelectedTenants("TENANT1_ID||TENANT2_ID")
import de.uplanet.lucy.server.ServerFeature

// Determines the setting in the portal that determines the selection of the active 
// tenants limited to one tenant

ServerFeature.isSingleTenantMode()

Velocity API

The Velocity API enables you to

  • check the tenant membership (user object against tenant object)

  • determine the attributes of an object (tenant, organization, organizational unit, role, group)

## Check for tenant match (user - record)
#set($TenantMembersSelected = $Tenants.getAllTenantValues($Session))
#set($TenantMembersSqlList = $TenantMembersSelected.replace("||", "','") )

#set($stmtTable = $PreparedQuery.prepare($DbConnection, 
"SELECT STR_TITEL FROM DATAGROUP('GUID') WHERE STR_MANDANT IN (?)"))
$stmtTable.setString(1, $TenantMembersSqlList)
#set($rsTable = $stmtTable.executeQuery())

#foreach($element in $rsTable)
	#set($Title = $element.getStringValue(1))
	$Title <br>#end
$rsTable.close()
$stmtTable.close()

## Read object information (attributes)
#set($ObjectGuid = "8808E768D142663695576F533A27E5CAC2739552")
#set($stmtObject = $PreparedQuery.prepare($DbConnection, "SELECT STRIDENT FROM VDSTENANT WHERE STRGUID = ?"))
$stmtObject.setString(1, $ObjectGuid)
#set($ObjectIdent= $stmtObject.executeAndGetScalarStringValue(""))
$stmtObject.close()

The following methods are provided for tenants with the $Tenants object:

## List of all tenants of the current user
$Tenants.getTenants($Session)

## List of all selected tenants of the current user/session
$Tenants.getSelectedTenants($Session)

## Piped-List of all selected tenants of the current user
$Tenants.getSelectedTenantValues ($Session)

## Number of tenants of the current user
$Tenants.getTenants($Session).size()

## All tenants of the current user as string list 
## (seperated by ||)
$Tenants.getAllTenantValues($Session)

## Number of selected tenants
$Tenants.getSelectedCount($Session))

The following methods can be applied to the list of tenant objects (getTenants / getSelectedTenants):

## Display of the name of the first tenant
#set($TenantList = $Tenants.getTenants($Session))
$TenantList.get(0).getTitle()

## Check if first tenant is selected
$TenantList.get(0).isSelected()

## ID of the tenant
$TenantList.get(0).getValue()

More information

Parameter dialog

Velocity Docs

Groovy Docs