Note:

If you want to create a new page for developers, you should create it on the Moodle Developer Resource site.

Multitenant support

From MoodleDocs

Note: This page is a work-in-progress. Feedback and suggested improvements are welcome. Please join the discussion on moodle.org or use the page comments.

Multi-tenant support
Project state Draft open for public feedback
Tracker issue MDL-28946
Discussion n/a
Assignee Petr Škoda (škoďák)

Moodle 2.2


Overview

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, this is not intended to be used for logical splitting of one organisation.

http://en.wikipedia.org/wiki/Multitenancy

http://tracker.moodle.org/browse/MDL-28946

multi-tenant == more companies on one server == less maintenance work

Use cases:

  1. 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.
  2. 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.

Backwards compatibility

100%, the changes in DB tables would be trivial, 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 same system settings.

List of changes

Database changes

Difficulty: easy

  • add tenant database table - id, fullname, shortname, wwwroot, idnumber, description, description_format, status, lang, theme, maxusers, usernameregex, emailregex, idnumberregex, timecreated, timemodified
  • tenantid required in tables: user, course, course_category, sessions, log, context, course_request, event
  • most probably will be required also in: blog, tag, scale and ws token table

There are multiple semi-unique identifiers in different database tables, technically we could enforce them unique separately in each tenant - candidates are: course->shortname, course->idnumber, user->idnumber, etc. These unique constraints might be relaxed later, they can be left unique for now.

Context changes and access control

New tenant context level

Difficulty: hard

  • 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 the same time we could hardcode $CFG->forcelogin restriction in lib/setup.php
  • new capabilities: 'moodle/tenant:view' (enter the tenant sub-site), 'moodle/tenant:manage' (edit the tenant db record)
  • at he same time we could create a new moodle_context class

PAGE

Difficulty: normal

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:

$PAGE->set_url('/mod/page/view.php', array('id'=>$id);
list($page, $cm, $context, $course) = $PAGE->init_module_by_id('mod_page', $id);
$PAGE->require_login('mod/page:view');

Navigation

Difficulty: hard

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.

Frontpage

Difficulty: normal

Separate 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.

Logging

Difficulty: easy

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.

Reports

Difficulty: normal

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

Difficulty: easy

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

Auth

Difficulty: normal

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.

Tenant switching

Difficulty: normal

Tenant users can not switch to different tenant, they have to logout and login again to access different tenant using different username. The current tenant is determined directly from $USER->tenantid

Standard global users my switch the tenant via "Change tenant" UI selector and in case of separate domains also via login page. The current tenant is determined from $USER->tenantid_session. In order to access tenant user must have 'moodle/tenant:view' capability.


User management

Difficulty: hard

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.)


Enrol

Difficulty: easy

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.

User upload

Difficultly: easy

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

Difficulty: easy

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

Difficulty: easy

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.

Cron

Difficulty: unknown

There will be a problem with the links from emails pointing back to tenant sites. Hopefully faking of PAGE will work aroudn taht

Backup and restore

Difficulty: easy

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.

Blog

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

Tags

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

Difficulty: easy

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.


Externallib/WS

Difficulty: normal

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.

MNET

Difficulty: easy No support for multitenant, we only need to prevent migration of tenant users, only standard users would be allowed to roam.

Implementation plan

  1. Preparation work and public discussions - 2 weeks
    1. discuss the multitenant with the public and partners
    2. convert reports to use context id and move to new /report/ plugin type
    3. create new /admin/tool/ plugin type
    4. refactor require_login() logic and add new init and access control features to $PAGE
    5. create new moodle_context class that may encapsulates some logic in the future
    6. review all navigation code
    7. create new cohort context
  2. Create a basic prototype in a test branch - 1 month
    1. add new DB fields
    2. create new tenant context
    3. implement tenant separation in accesslib
    4. hack support for more wwwroots in setup lib
    5. hack navigation to work in tenant sites
    6. hack separate login form
  3. Discussion and planning - 1 week
    1. is it a good solution?
    2. are there any future problems?
    3. plan all features to be implemented now and later
  4. Implementation in separate branch - 1 month
    1. implement decided features
    2. add experimental switch
    3. test, test, test
  5. Final decision before integration into master
    1. is it ready and tested enough for 2.2 or should we wait till 2.3?

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

FAQs

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.
Can system guest or not-logged-in user access tenant site?
No, they can only access classic frontpage. We want to hide/separate the tenants as much as possible. Workaround is to create tenant user accounts with a known password.
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, it would be slow and very complex to deal with a tree hierarchy of tenants.
Is it possible to have all configuration settings in each tenants?
No, please use normal Moodle instances if you need different system settings for tenants.