Before starting to code anything about Multi Authentication support in Moodle, I would like to share what I have in mind, so others can see the flaws and everybody can spot the problems with this approach.
There are some big questions (see below) I don't know the answer for, so if others with more knowledge of Moodle internals (specially authentication internals) than me could answer them, that would be great.
Some random notes about multi-auth in Moodle
- Multiple simultaneous sources for authentication
- Multiples sources of the same type (LDAP, database, etc)
- Each source has its own configuration
- The order in which we try the sources is configurable
How it works
- Each source has at least the auth_user_login function (like now).
- This function can return one of the following values:
- AUTH_OK: the user and the password are valid
- AUTH_DECLINED: The user or the password are not correct or unknown to this source
- AUTH_DENIED: This user cannot login, even if the username and password are valid
- AUTH_ERROR: The source found and error during user and password validation and can't say wether they are valid or not.
- We try each source in the configured order.
- For each order, we call auth_user_login with the user credentials and test the result.
- if the result is AUTH_DENIED, we break out of the loop and deny the user login.
- if the result is AUTH_OK, we break out of the loop and allow the user login.
- in any other case, we continue the loop.
- If we finish the loop, we deny the user login (all the sources returned either AUTH_DECLINED or AUTH_ERROR, so we can't let the user log in).
The (proposed) database tables
(MySQL syntax, Postgresql gurus please adjust definition ;-)
CREATE TABLE `prefix_auth_type` ( `id` integer(10) NOT NULL auto_increment, `name` varchar(20) NOT NULL default '', `isinternal` tinyint(1) NOT NULL default '0', `multiple` tinyint(1) NOT NULL default '0', `candisable` tinyint(1) NOT NULL default '0', `removable` tinyint(1) NOT NULL default '0', PRIMARY_KEY(`id`), UNIQUE KEY(`name`) )
This table stores the different authentication types present in this Moodle installation: internal, none, ldap, db, cas, etc.
Each auth type has to autoregister in this table when it's installed, specifying:
- 'id': the primary key of the record.
- 'name': The name of the autentication type: 'internal', 'none', 'ldap', 'db', etc. There can't be duplicated values in this field.
- 'isinternal' if this auth type is internal to Moodle (1) or not(0), i.e., if it doesn't depend on any external entity to provide authentication. Tipically this is for 'manual', 'email' and 'none'.
- 'multiple': if this auth type supports multiple instances (1) or not (0).
- 'candisable': if this auth type can be temporarily disabled (1) or not (0)
- 'removable': if this auth type can be removed from the system (1) or not (0).
If we decide that 'internal', 'none' and/or 'email' are special authentication types and are handled separately form the rest, then this last field can be removed.
BIG QUESTION HERE: Can 'internal' be disabled? I'm not sure about this, as all the admin users can be locked out of Moodle if we mistakenly disable all the auth types needed for admin users.
BIG QUESTION HERE: Should 'internal' auth type be handled separately from all this multi-auth mess? I guess there are some benefits to it, but I'm not sure about it. For one, it would make the above question irrelevant.
CREATE TABLE `prefix_auth_instance` ( `id` integer(10) NOT NULL auto_increment, `typeid` integer(10) NOT NULL default '0', `name` varchar(20) NOT NULL default '', `description` varchar(50) NOT NULL default '', `sortorder` integer(10) NOT NULL default '0' PRIMARY_KEY(`id`), UNIQUE KEY(`name`) )
This table stores the different auth instances available for each auth type. It's managed from the authentication setup page, using the add/modify/delete functionality available there. We can also specify the order in which we want to user each auth-instance when authenticating users.
The meaning of the fields are:
- 'id': the primary key of the record. We will put this value into the 'auth' field in prefix_user table when we correctly authenticate a user using this auth instance (to later track where to update values from, where to check for password expiration, etc.)
- 'typeid': the auth type id of this instance. It's a foreign key into the 'type' field of auth_type table. We need this to check if there is already an instance of a given auth type, in case that auth type doesn't allow for multiple instances.
- 'name': the name of this instance. It's the text shown in the authentication setup page to refer to this instance.
- 'description': a short description text for this instance (e.g., 'UK Students LDAP server').
- 'sortorder': the order in which we use each auth instance when trying to authenticate users.
BIG QUESTION HERE: If we use 'id' to fill in the 'auth' field in prefix_user, then we should only use 'sortorder' when the user has that field empty (i.e., we don't know where to authenticate this user so far). Otherwise we could directly authenticate with the known auth-instance. But this prevents users from "moving" between authentication instances (i.e, this user "was" in our UK student's LDAP server, but now "is" in our US staff database server). Is this good or bad?
ANSWER TO BIG QUESTION: After talking with Martin Langhoff at MoodleMoot Spain '06, this is clearly a Bad(tm) thing. If we allow users to "float" between authentication instances, things like auth_ldap_sync_users.php could break havoc, deleting users it doesn't know about in this auth_instance (but perfectly valid in other auth_instance's). So storing the auth_instance 'id' in prefix_user and using it accordingly is The Right Thing(tm).
CREATE TABLE `prefix_auth_instance_settings` ( `id` integer(10) NOT NULL auto_increment, `instanceid` integer(10) NOT NULL default '0', `name` varchar(255) NOT NULL default '', `value` text NOT NULL PRIMARY_KEY(`id`), UNIQUE KEY `instancesetting` (`instanceid`,`name`) )
This table stores each auth instance configuration settings.
- 'id': the primary key for the record.
- 'instanceid': foreign key into the 'id' field of auth_instance table.
- 'name': name of the setting.
- 'value': value for that setting.
There can't be two (or more) identical 'name' values for the same 'instanceid'.
Some additional questions before starting to code all this mess
- If we store the 'prefix_auth_intance.id' value in the 'prefix_user.auth' field as suggested above, what should we do when we remove that auth instance from our system? Should we allow removal if any users are using that instance? Should we move those users to the default auth instance (internal)? Should we choose a new auth instance for those users as part of the removal? Or should we just ignore the 'prefix_user.auth' field during login and just proceed through the whole loop and try to find which new auth instance allows the user to login (if any)?