Multitenancy
Bosch IoT Things service provides various ways to support solution developers in creating multitenant-ready applications by allowing to restrict the access rights on things and their messages at different levels.
Multitenancy as a principle
Multitenancy is the capability of a single application instance to handle different tenants while the strict separation of their data is ensured.
This principle is fully implemented by the Things service:
- The cloud platform marketplace will not show any customer who else has booked our service
- When working with a thing we apply strict control by always:
- Identifying which solution tries to call the service,
- Authenticating technical clients or users, and
- Authorizing technical clients and user based on the thing’s own data.
Permissions on a thing
Our main concept is, that a thing belongs in the first place to the solution which has “created” the thing. Thus the solution is fully responsible for setting the initial permission for the specific thing. To ensure all your solution parts which need to access your things are not handicapped, grant all your client IDs permission on the thing.
Tenant identifier included in namespace
In addition to the concept on how you can create a thing ID (see Things and features, regarding multitenancy the best practice would be to include the tenant identifier within the namespace. This would allow to reflect the affiliation of a thing to a distinct tenant, even at the persistence level. However, always on top make sure to set the rightful actors as authorized subjects in the thing’s policy.
Including the tenant information is useful for either a setup where different subscriptions of the Things service are used for different tenants, or for a setup where a common Things service subscription is used. In the latter case, please be aware of restrictions regarding the number of namespaces per instance. See restrictions.
Example 1
Tenant 1 and Tenant 2 will share your solution but must not be aware of the existence of one another:
Tenant 1 | Tenant 2 | |
Solution ID | 123 | 123 |
Device Integration Client | 123:deviceIntegration | 123:deviceIntegration |
Web UI Client | 123:WebUI | 123:WebUI |
Namespace | com.bosch.iot.maker.tenant1 | com.bosch.iot.maker.tenant2 |
Thing ID | com.bosch.iot.maker.tenant1:sensorxyz | com.bosch.iot.maker.tenant2:sensorxyz |
Policy | Tenant1-Policy authorizes - users, groups, roles from own Tenant - your solution clients |
Tenant2-Policy authorizes - users, groups, roles from own Tenant - your solution clients |
The setup could be as shown in following picture:
Example 1 - policy for a thing owned by tenant 1
{
"policyId": "com.bosch.iot.maker.tenant1:sensorxyz",
"entries": {
"User-of-Tenant1-Policy": {
"subjects": {
"iot-suite:111-group-id": {
"type": "iot-suite subject"
}
},
"resources": {
"thing:/": {
"grant": ["READ", "WRITE"],
"revoke": []
},
"policy:/": {
"grant": ["READ", "WRITE"],
"revoke": []
},
"message:/": {
"grant": ["READ", "WRITE"],
"revoke": []
}
}
},
"Solutions-Policy": {
"subjects": {
"iot-things:123:WebUI": {
"type": "iot-things-clientid"
},
"iot-things:123:deviceIntegration": {
"type": "iot-things-clientid"
}
},
"resources": {
"thing:/": {
"grant": ["READ", "WRITE"],
"revoke": []
}
}
}
}
}
Example 1 - policy for a thing owned by tenant 2
{
"policyId": "com.bosch.iot.maker.tenant2:sensorxyz",
"entries": {
"User-of-Tenant2-Policy": {
"subjects": {
"iot-suite:222-group-id": {
"type": "iot-suite subject"
}
},
"resources": {
"thing:/": {
"grant": ["READ", "WRITE"],
"revoke": []
},
"policy:/": {
"grant": ["READ", "WRITE"],
"revoke": []
},
"message:/": {
"grant": ["READ", "WRITE"],
"revoke": []
}
}
},
"Solutions-Policy": {
"subjects": {
"iot-things:123:WebUI": {
"type": "iot-things-clientid"
},
"iot-things:123:deviceIntegration": {
"type": "iot-things-clientid"
}
},
"resources": {
"thing:/": {
"grant": ["READ", "WRITE"],
"revoke": []
}
}
}
}
}
Tenant identifier included in client suffix
A further option to make sure one tenant never get to see a thing of another tenant is to fully customize the “tenant-specific” view on the data. In our example we have extended the technical client ID with a tenant identifier, but you can apply the method also for technical client instances respectively. However, always on top make sure to set the rightful actors as authorized subjects.
Example 2
Tenant 1 and Tenant 2 will share your solution but must not be aware of the existence of one another:
Tenant 1 | Tenant 2 | |
Solution ID | 123 | 123 |
Device Integration Client | 123:integration | 123:integration |
Web UI Client | 123:WebUItenant1 | 123:WebUItenant2 |
Namespace | com.bosch.iot.maker.tenant1 | com.bosch.iot.maker.tenant2 |
Thing ID | com.bosch.iot.maker.tenant1:sensorxyz | com.bosch.iot.maker.tenant2:sensorxyz |
Policy | Tenant1-Policy authorizes only users, groups, roles from own Tenant |
Tenant2-Policy authorizes only users, groups, roles from own Tenant |
Example 2 - policy for a thing owned by tenant 1
{
"policyId": "com.bosch.iot.maker.tenant1:sensorxyz",
"entries": {
"User-of-Tenant1-Policy": {
"subjects": {
"iot-suite:/organization.<org-guid-1>.Owner": {
"type": "iot-suite organization Owner"
}
},
"resources": {
"thing:/": {
"grant": ["READ", "WRITE"],
"revoke": []
},
"policy:/": {
"grant": ["READ", "WRITE"],
"revoke": []
},
"message:/": {
"grant": ["READ", "WRITE"],
"revoke": []
}
}
},
"Solutions-Policy": {
"subjects": {
"iot-things:123:WebUItenant1": {
"type": "iot-things-clientid"
},
"iot-things:123:deviceIntegration": {
"type": "iot-things-clientid"
}
},
"resources": {
"thing:/": {
"grant": ["READ", "WRITE"],
"revoke": []
}
}
}
}
}
Example 2 - policy for a thing owned by tenant 2
{
"policyId": "com.bosch.iot.maker.tenant2:sensorxyz",
"entries": {
"User-of-Tenant2-Policy": {
"subjects": {
"iot-suite:/organization.<org-guid-2>.Owner": {
"type": "iot-suite organization Owner"
}
},
"resources": {
"thing:/": {
"grant": ["READ", "WRITE"],
"revoke": []
},
"policy:/": {
"grant": ["READ", "WRITE"],
"revoke": []
},
"message:/": {
"grant": ["READ", "WRITE"],
"revoke": []
}
}
},
"Solutions-Policy": {
"subjects": {
"iot-things:123:WebUItenant2": {
"type": "iot-things-clientid"
},
"iot-things:123:deviceIntegration": {
"type": "iot-things-clientid"
}
},
"resources": {
"thing:/": {
"grant": ["READ", "WRITE"],
"revoke": []
}
}
}
}
}