|Assignee||Petr Škoda (škoďák)|
The fundamental idea of this proposal is that the tenants are separated as much as possible. Each tenant is expected to be an entity completely separate from the rest of tenants.
multi-tenant == more companies on one server == less maintenance work
- Commercial company wants to sell training services to multiple smaller customers. Customers get only student accounts and inspector accounts. All courses are created and managed by the commercial company. The major benefit would be the full separation of tenants including custom theme and frontpage.
- Somebody wants to create free moodle hosting site, instead of giving away courses they can give limited admin rights to tenant instances.
Each tenant site has different domain name - more secure, allows login page customisations per tenant, you need to set-up DNS, each tenant separately or DNS wildcard. (ex: customer1.example.com, customer2.example.com)
Security is significantly lower compared to independent virtualised Moodle instances. Using separate domains for each customer improves security, but vulnerabilities like SQL injections, code execution or missing access control may affect all tenants at the same time.
The performance should be comparable to current 2.x, no overhead expected when tenants not enabled. Admins might be tempted to create mega sites for hundreds of organisations with thousands of users - the scalability is NOT going to improve with this solution at all.
Technically it should be possible to implement most of this in 2.2dev reaching experimental status in Moodle 2.2.
The changes in DB tables would be relatively simple, adding new fields with 0 default. No upgrade code required. Pretty much anything that depends on contexts lower than system should work just fine. We have to concentrate on adding support to current system level features - either prevent access from tenant or add/fix support for lower contexts.
The full tenant separation greatly simplifies the necessary changes. This proposal creates small virtual independent Moodle instances that share the most of the system settings.
List of changes
- add tenant database table - id, fullname, shortname, wwwroot, idnumber, description, description_format, status, lang, theme, maxusers, timecreated, timemodified
- tenantid required in tables: user, course, course_category, sessions, log, context, course_request, event, cohort
- most probably will be required also in: blog, tag, scale and ws token table
There are multiple unique identifiers in different database tables, we should probably enforce them unique separately in each tenant - candidates are: course->shortname, course->idnumber, user->idnumber, user->username, etc.
Tenant setting overrides
Some global $CFG settings need to be overridden for each tenant - global theme, wwwroot, etc. We could store all these overrides in a separate table config_tenants and merge then into CFG early in lib/setup.php It will be more difficult to create UI where these settings can be changed.
Context changes and access control
- new context level CONTEXT_TENANT in between system and course_category context, parent to all tenant user contexts
- hardcoded restrictions in has_capability() and similar methods - tenant can not have any capabilities outside of their part of the context tree.
- hardcoded restrictions in require_login()
- at he same time we could create a new 'context' class
- one tenant user may access only one tenant site, global user may access only global site
Add $PAGE->get_current_tenant(), this would be used by navigation. The logic would be: if user->tenantid not empty, use only this tenant, if global user first look at current page context, in system context use $USER->tenantid_session. The tenantid affects also current theme and language.
At the same time we should probably rework the PAGE setup:
list($page, $cm, $context, $course) = $PAGE->init_module_by_id('mod_page', $id);
Navigation has to find the current tenant and build the nav tree accordingly. This will require significant number of IFs, but at the same time we could review/cleanup the current code and improve perf. There should not be any logic problems.
Separate frontpage course for each tenant. We would need some new UI for this or we may simply copy the info from the tenant record and do not allow customisations of name, description from inside.
The tenantid is added because we can not join the information later because the original object (course, category) may not exist any more. It is very important to have tenant level logs and reports. In add_to_log() the tenant id can be derived from current parameters - $USER and $course.
Course reports are not a problem at all. The system reports would have to be updated and improved. The biggest problem would be probably to inject them to proper context in the navigation.
At the same time we could split some admin reports and put them into /admin/tool/ and keep only real "reports" in the original place and make them all play nice with the tenants and navigation block.
Admin settings and UI
No system level admin settings in the tenant instances. Some settings pages already work in system and other contexts - permission overrides, role assignments, some grade stuff
Experimental setting: Multi-tenant - no, yes, separate ($CFG->enablemultitenant) 0,1,2
No changes expected, we should not mess with the user table and auth plugins at this point because tenants are expected to be small. If somebody needs to set up multiple ext DB or LDAP syncs they should definitely use full separate moodle sites. This could be improved in future separately in each plugin if really necessary.
The only problem here is the uniqueness of usernames and emails. One of the simpler solutions would be to enforce username uniqueness with a regex patters in the tenant table - you would be allowed to create new user only if the username matches the regex (for example 'sk1_.*' for "school 1").
The email does not have to be unique because we do not use it as an identifier, the only trouble is password resetting. I think I have found a solution: if you find multiple accounts with the same email address we can add extra password reset step email - ask user to choose which account to reset. This should be simple and 100% reliable.
tenantsso auth plugin
There will be a way to create global user aliases, this will allow us to implement SSO from the global site to tenant site.
At least some basic user management will be necessary in each tenant. We can either alter the global user management UI or add a simplified version. We need to address user filtering and edit forms, etc. (It would be great to ajaxify this at the same time of possible.)
Course level enrol is going to work fine, the only change is that the enrol will allow enrolment from the current tenant of global users. LDAP and external DB will not be configurable from inside the tenant sites.
Available from the global scope only, the CSV user upload would work for one tenant at a time, there would be a selection box for tenants. It would not be possible to upload users from multiple tenants through one CSV files.
User profile and preferences
Most of the problems are solved by the new context level. No big changes expected, a couple of extra ifs maybe.
Bulk user actions
Difficulty: normal (skip)
Can be left at system level for now for admins only. Later can be updated to work in tenants.
Course category editing
Categories and courses can be moved only inside one tenant. It is not possible to migrate standard course to tenant course or category, or the opposite direction.
Each tenant site will have separate execution of cron script, the tenant is determined from the URL or CLI script parameter.
We will have to significantly cleanup and improve this subsystem.
Backup and restore
No problems expected at the course level. Some new UI options could help with user mapping and conflicts.
Tenant migration to different site
Difficulty: hard (deferred)
It would require a new XML definition for course categories, cohorts and logs + mapping of global users that are enrolled in tenant courses. This can be implemented later, it should be possible to export users via CSV/XML and transfer courses one by one.
Difficulty: unknown (deferred)
The problem is that blogs are not linked to context, they live in site context only. We can:
- disable blogs completely for tenants - easy
- add tenantid to blocks - tricky to separate these, it could probably work with different tenant domains only
Difficulty: unknown (deferred)
This is going to be a big mess, tags are global across all contexts. The easies way is to ignore this problem and simply disable tags completely. In the future somebody could come up with major cleanup and support for tenants. Options:
- ignore this and disable tags
- hacks the tags to support the tenants at least partially
Cohorts are not affected by this change at all, we could add support for cohorts at the tenant context, but very few changes would be necessary - the tenantid is copied from the parent context.
This may get a bit tricky, unique identifiers may cause problems. Also the token table and token management UI has to be updated to support tenant separation.
Difficulty: easy No support for multitenant, we only need to prevent migration of tenant users, only standard users would be allowed to roam.
Other ideas and improvements
- option to limit number of logged in users in one tenant - custom auth plugin
- migration of tenant user to standard user - technically possible, the design allows standard users to be given access to tenant sites
- tenant support in custom profile fields
- multiple sites in the same domain - might be technically possible but very difficult
- Can one user belong to two, three, four tenants?
- Definitely no, because this would break the tree context access model. Create independent user accounts in each tenant or one global account with tenant aliases.
- Can system guest or not-logged-in user access tenant site?
- Can I move a user from one tenant to another.
- No, this would break the concept of separation of tenants, create a second account in new tenant and delete the old one.
- Can I migration of standard user to a tenant user.
- No, this would leave user data outside of tenant breaking the concept of tenant separation, create a new tenant account instead, delete old account.
- Can I move course from one tenant to another or to global scope?
- No, this would again break separation, use backup/restore to create a new course in different tenant.
- Can my tenant users participate in the standard global Moodle site?
- No, this completely defeats the idea of separate tenants, please use course categories or separate Moodle servers with SSO.
- Can I create sub-tenants and sub-sub-tenants?
- No, only one level of tenants is allowed because each tenant must have separate domain name.
- Is it possible to have all configuration settings in each tenants?
- No, only a limited number of settings can be made available in each tenant - both for security and technical reasons.