Multi-tenancy is the ability to have multiple virtual Moodle sites running on one copy of Moodle.
Example use case: A commercial training company create Moodle courses and wants to sell training services to multiple customers (eg companies). They do not want to run a whole Moodle for every customer because they want easy management and reporting, yet they also don’t want users from one customer seeing users from another.
Each customer can manage their own users, as well as having a custom FrontPage and a custom theme, enabling them to have what seems like a whole Moodle site each.
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
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.
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
Each tenant site will have separate execution of cron script, the tenant is determined from the URL or CLI script parameter
Tenant migration to different site
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.
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
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.
Article reference from — Moodle