5.9 KiB
title
| title |
|---|
| 1. Simple Multi-Tenancy using a single OpenCloud Instance |
- Status: proposed
- Deciders: [@micbar @butonic @dragotin @rhafer]
- Date: 2025-05-20
Technical Story: https://github.com/opencloud-eu/opencloud/issues/877
Context and Problem Statement
To reduce resource usage and cost service providers want a single OpenCloud instance to host multiple tenants. Members of the same tenant should be able to only see each other when trying to share resources. A user can only be a member of a single tenant. Moving a user from one tenant to another is not supported.
OpenCloud does currently not have any concept of multi-tenancy. All users able to login to an OpenCloud instance are able to see each other and share resources with everybody. This ADR is supposed to layout a concept for a minimal multi-tenancy solution that implements the characteristics mentioned above.
To further limit the scope there are a couple of constraints:
- Tenants are rather small (sometimes just a single user, often less than 10)
- There is just a single IDP with a single "realm".
- The user-management is external to OpenCloud
- The membership of a user to a tenant is represented by a tenant id that is provided via a claim in the users' Access Token/UserInfo or by a per User Attribute in the LDAP directory.
- There is no need to support per tenant groups
- There is no need to isolate the storage of the tenants from each other
- Role Assignment happens at first login via OIDC claims
Decision Drivers
- Low Resource Overhead: The solution should not require much additional resources (CPU, Memory, Storage) per tenant on the OpenCloud instance.
- Implementation effort: The solution should be easy to implement and maintain.
- Security: The solution should prevent users from seeing or accessing anything from other tenants.
Considered Options
Option 1: Tenant ID as a new property of the CS3 UserId
The CS3 UserId (https://buf.build/cs3org-buf/cs3apis/docs/main:cs3.identity.user.v1beta1#cs3.identity.user.v1beta1.UserId) is extended by a new property "tenantId".
Pros:
- Everywhere the UserID is used the tenant id is also available.
- This might allow implementing more sophisticated checks e.g. on permission grants and to a certain extend during share creation.
- the tenant id is immediately available e.g. in Events/Auditlog without an additional user lookup
Cons:
- Requires changes to the CS3 API
- Adds even more semantics to the UserId. Ideally the UserID would just be an opaque identifier without carrying and specific semantics other than being globally unique.
- on the GraphAPI the ID of a User is just a opaque string without any additional
meaning. (Currently it is just using the
OpaqueIdproperty of the CS3 UserId, without considering theidpproperty.)
Option 2: Tenant ID is stored as the idp value of the CS3 UserId
Instead of introducing a new property the on the CS3 UserId we'll just override
the idp value with the tenant id.
Pros
- No changes to the CS3 API required
- The pros of Option 1 apply here as well
Cons:
- It's a crutch, we're already "abusing" the
idpproperty of the CS3 UserId to have a different meaning in the context of federated sharing. Adding an additiona meaning could make the code even more complicated. - Apart from the API change the Cons of Option 1 apply here as well.
Option 3: Tenant ID is a property of the CS3 User Object
A new (optional) property "tenantId" is added to the CS3 User Object.
Pros:
- Avoid overloading CS3 UserId with additional semantics.
- The tenant id is available everywhere the User Object is used.
Cons:
- Requires changes to the CS3 API
- Might require more user lookups in places where we need to find out the tenant id of a specific user
Option 4: Tenant ID is invisible to the CS3 API
Reva Tokens would get a new property tenantId. To have the tenant id available
of the signed in user available with every request.
While not being part of the CS3 API objects the users service will be made "tenant aware"
so that is only returns users of the same tenant as the requesting user e.g. by using
proper LDAP filters or subtree searches.
Pros:
- No changes to the CS3 API required
- Code changes could be limited to the
usersandshare-providerservices and the reva token manager - The tenant id of the current user is available everywhere the reva token is used.
Cons:
- Can this work with the App Token feature if the Tenant Id is not part of the User Object in any way, how can the the token manager know with Id to add to the token?
- What about places where the system user is doing stuff on behalf of a user
Option 5: Tenant membership via LDAP group membership
All members of a tenant are assigned to the same group. The users service gets a config
switch to only allow users to search for users that are part of the same group.
Pros:
- ?
Cons:
- The group needs to be hidden somehow from the
groupsservice as this is not supposed to be a "sharing group".
Decision Outcome
Chosen option: Option 1 - Tenant ID as a new property of the CS3 UserId
As part of the OIDC Connect Authentication OpenCloud will receive a tenant id via the Access Token or Userinfo endpoint. For the initial implementation it is assume that OpenCloud has read access to a shared LDAP server, that contains all users of all tenants. Users in LDAP will have a the tenant id as an dedicated attribute.
Implementation Steps
- Extend the CS3 UserId with a new property "tenantId"
- Adapt the
usersservice` to only return users that are part of the same tenant - To cleanup technical debt and reduce code duplication the
cs3users backend in the graph service is being improved so that is can fully replace theLDAPusers backend for read operations. - The reva
share-providerservice only allow shares between users of the same tenant