[size=xx-large]Argument for Inclusion of Authentication Services Into the XOOPS Core[/size]
IntroductionThere is a discussion thread on the Xoops.org. forum created by mercibe introducing a hack to XOOPS that provides for LDAP and CAS authentication. This is, to my knowledge, the only way to add LDAP functionality to the XOOPS environment. This hack was created by mercibe nearly 2 years ago and submitted to the XOOPS team for consideration. It was rejected (or at least not accepted) on the grounds that it wasn't entirely coded in a manner appropriate to the XOOPS philosophy.As each new version of XOOPS gets released, the hack must be checked against the newly updated XOOPS files to ensure that it is still appropriate and doesn't conflict. This takes time and is not an ideal situation.In a recent post (#44), I asked what could be done to have this hack considered again for inclusion into the XOOPS core. Mithrandir replied saying that (and I am paraphrasing, please see the thread for more information) the biggest problem with regards to philosophy was the fact that variables in the code had to be altered. A better solution would see those values updated via the standard XOOPS administration interface. Furthermore, Mithrandir mentioned that he had tried to update the code himself in the past, only to find that the resulting administration interface was confusing. He closed with the observation that he had to neither an LDAP server nor a CAS server for testing purposes anyway.I would like to argue for the inclusion of the 'Authentication Services' hack to the XOOPS core, and also provide for the beginning of its implementation.ArgumentFirst Front - Coding PhilisophyI understand and agree with Mithrandir's comments regarding this. It would definitely be undesirable to have a single feature inside of XOOPS that had to be altered by editing the text inside of core files. Massaging the hack as it was originally submitted is necessary. Hopefully, however, the work that I've done below (Implementation section) alleviates these issues. If not, they should at least bring the hack one step closer to a proper implementation.Second Front - Administration InterfaceMithrandir has expressed dissatisfaction with the idea of having an admin screen full of preferences that aren't relevant if you aren't using the feature. Specifically, if Authentication Services were rolled into XOOPS with support for 3 initial services (default, LDAP, and CAS), then the preferences screen addressing these three methods would contain elements relevant to all three of them. For example, if you needed LDAP connectivity, your screen would still be cluttered with options for implementing CAS integration.Again, I see Mithrandir's point. Having all these options visible together is less than desirable at best and confusing at worst. However, this isn't a problem with XOOPS coding philosophy. The preferences screen in the System Admin section is currently coded into main pages followed by sub-pages. You cannot cleanly configure an interface that adapts to a selection in a drop-down list box because no such interface is currently implemented by Xoops. This is a XOOPS shortcoming, but fixing it falls outside of the scope of the Authentication Services hack.The best case scenario is adding a 7th preferences category called "Authentication Services". The resulting page would start with a drop-down list box allowing you to select which method you would like to use for authentication (default, LDAP, or CAS). Below this would be each of the preferences for each of the implemented authentication services grouped logically.Third Front - TestingI can also sympathize with Mithrandir's concern about his inability to test the code against LDAP and CAS servers. The reason I didn't implement the CAS portion below is that I don't have access to a CAS server either. I also believe that this problem may be the biggest hurdle to the insertion into the XOOPS core of this hack. But honestly, that is what betas are for! Insert the code as it was submitted and allow other people who do have these network resources to help troubleshoot any problems for you. Maybe I'm being entire too naive here, but I thought that was the big point behind open-source software; getting others to help pitch in on the work effort. I know everyone wants to do the best job they can at releasing the most solid code possible. That's great and it is the strength of XOOPS thus far! Please don't neglect an important feature based on an inability to thoroughly test it however. Even if the hack is flawed and doesn't work as advertised, its default settings are the same code XOOPS uses today for authentication! In other words, those who don't care about LDAP or CAS authentication won't be bothered. It is only those who configure XOOPS to use LDAP or CAS who might run into a bug that causes it not to work.ConclusionAuthentication services via LDAP, CAS, etc. are extremely important to the future of Xoops. The ability to authenticate your users against standard directory services is a requirement for the adoption of XOOPS software for use behind company firewalls. Speaking for myself, I literally wouldn't be using it without the current hack. To restate it bluntly, Authentication Services is literally a make or break feature for a potentially huge audience.Don't wait for some mythical perfect future environment before inserting this feature, insert it now! The down sides aren't any greater than where you are at the moment, but the upsides include potentially greater proliferation of the XOOPS environment and a more vibrant XOOPS community as a result. And although this isn't the ideal implementation for the long term, it is a very adequate implementation until the preferences code in the XOOPS Core is enhanced.In other words, it seems a little silly to me to reject important functionality from the XOOPS core simply on the basis of an ugly preferences page. Let's face it; the preferences page as it currently exists is already very ugly in a few places. My preference would be to create an Authentication Services category now, and update how it behaves later with a comprehensive update to how all preferences are displayed or managed.[size=x-large]Implementation (sans CAS)[/size]
I don't just expect to post this and stand around waiting for everyone else to do the hard work. With that in mind, I have written instructions for anyone to use that will create an additional category on the preferences page in the system admin section. Furthermore, I will show that once this category exists, you can easily remove the hard coded variables from mercibe's original code and allow the entire thing to be administrated from the standard interface that already exists in Xoops.Note: The implementation below allows for default XOOPS authentication and authentication via an LDAP server. I've left CAS as an exercise for whoever cares.Install Xoops[list=1]
[*]Install the latest version of XOOPS on a machine for testing purposes.
[*]Apply the authentication services hack as it was written by mercibe.
[/list]
Add the 'Authentication Services' Category[list=1]
[*]Insert the following record into the xoops_configcategory table:
INSERT INTO `xoops_configcategory` (`confcat_id`, `confcat_name`, `confcat_order`) VALUES (7, '_MD_AM_AUTHENTICATION', 0);
[*]Insert the following records into the xoops_configoption table:
INSERT INTO `xoops_configoption` (`confop_id`, `confop_name`, `confop_value`, `conf_id`) VALUES (68, 'default (Xoops Authentication)', 'xoops', 90);
INSERT INTO `xoops_configoption` (`confop_id`, `confop_name`, `confop_value`, `conf_id`) VALUES (69, 'LDAP (Lightweight Directory Access Protocol)', 'ldap', 90);
[*]Insert the following records into the xoops_config table:
INSERT INTO `xoops_config` (`conf_id`, `conf_modid`, `conf_catid`, `conf_name`, `conf_title`, `conf_value`, `conf_desc`, `conf_formtype`, `conf_valuetype`, `conf_order`) VALUES (90, 0, 7, 'authentication_service', '_MD_AM_AUTHSERVICE', 'ldap', '_MD_AM_AUTHSERVICEDESC', 'select', 'text', 0);
INSERT INTO `xoops_config` (`conf_id`, `conf_modid`, `conf_catid`, `conf_name`, `conf_title`, `conf_value`, `conf_desc`, `conf_formtype`, `conf_valuetype`, `conf_order`) VALUES (92, 0, 7, 'ldap_mail_attr', '_MD_AM_LDAP_MAIL_ATTR', 'mail', '_MD_AM_LDAP_MAIL_ATTR_DESC', 'textbox', 'text', 6);
INSERT INTO `xoops_config` (`conf_id`, `conf_modid`, `conf_catid`, `conf_name`, `conf_title`, `conf_value`, `conf_desc`, `conf_formtype`, `conf_valuetype`, `conf_order`) VALUES (93, 0, 7, 'ldap_name_attr', '_MD_AM_LDAP_NAME_ATTR', 'cn', '_MD_AM_LDAP_NAME_ATTR_DESC', 'textbox', 'text', 7);
INSERT INTO `xoops_config` (`conf_id`, `conf_modid`, `conf_catid`, `conf_name`, `conf_title`, `conf_value`, `conf_desc`, `conf_formtype`, `conf_valuetype`, `conf_order`) VALUES (94, 0, 7, 'ldap_surname_attr', '_MD_AM_LDAP_SURNAME_ATTR', 'sn', '_MD_AM_LDAP_SURNAME_ATTR_DESC', 'textbox', 'text', 8);
INSERT INTO `xoops_config` (`conf_id`, `conf_modid`, `conf_catid`, `conf_name`, `conf_title`, `conf_value`, `conf_desc`, `conf_formtype`, `conf_valuetype`, `conf_order`) VALUES (95, 0, 7, 'ldap_givenname_attr', '_MD_AM_LDAP_GIVENNAME_ATTR', 'givenname', '_MD_AM_LDAP_GIVENNAME_ATTR_DSC', 'textbox', 'text', 9);
INSERT INTO `xoops_config` (`conf_id`, `conf_modid`, `conf_catid`, `conf_name`, `conf_title`, `conf_value`, `conf_desc`, `conf_formtype`, `conf_valuetype`, `conf_order`) VALUES (96, 0, 7, 'ldap_location_attr', '_MD_AM_LDAP_LOCATION_ATTR', 'l', '_MD_AM_LDAP_LOCATION_ATTR_DESC', 'textbox', 'text', 10);
INSERT INTO `xoops_config` (`conf_id`, `conf_modid`, `conf_catid`, `conf_name`, `conf_title`, `conf_value`, `conf_desc`, `conf_formtype`, `conf_valuetype`, `conf_order`) VALUES (98, 0, 7, 'ldap_port', '_MD_AM_LDAP_PORT', '389', '_MD_AM_LDAP_PORT', 'textbox', 'int', 2);
INSERT INTO `xoops_config` (`conf_id`, `conf_modid`, `conf_catid`, `conf_name`, `conf_title`, `conf_value`, `conf_desc`, `conf_formtype`, `conf_valuetype`, `conf_order`) VALUES (99, 0, 7, 'ldap_server', '_MD_AM_LDAP_SERVER', 'directory.whatever.com', '_MD_AM_LDAP_SERVER_DESC', 'textbox', 'text', 1);
INSERT INTO `xoops_config` (`conf_id`, `conf_modid`, `conf_catid`, `conf_name`, `conf_title`, `conf_value`, `conf_desc`, `conf_formtype`, `conf_valuetype`, `conf_order`) VALUES (100, 0, 7, 'ldap_base_dn', '_MD_AM_LDAP_BASE_DN', 'ou=Employees,o=Company', '_MD_AM_LDAP_BASE_DN_DESC', 'textbox', 'text', 3);
INSERT INTO `xoops_config` (`conf_id`, `conf_modid`, `conf_catid`, `conf_name`, `conf_title`, `conf_value`, `conf_desc`, `conf_formtype`, `conf_valuetype`, `conf_order`) VALUES (103, 0, 7, 'ldap_office_attr', '_MD_AM_LDAP_OFFICE_ATTR', 'physicaldeliveryofficename', '_MD_AM_LDAP_OFFICE_ATTR_DESC', 'textbox', 'text', 11);
INSERT INTO `xoops_config` (`conf_id`, `conf_modid`, `conf_catid`, `conf_name`, `conf_title`, `conf_value`, `conf_desc`, `conf_formtype`, `conf_valuetype`, `conf_order`) VALUES (101, 0, 7, 'ldap_uid_attr', '_MD_AM_LDAP_UID_ATTR', 'uid', '_MD_AM_LDAP_UID_ATTR_DESC', 'textbox', 'text', 4);
[*]Insert the following lines into xoops\modules\system\language\english\admin\preferences.php:
//Authentication Services
define("_MD_AM_AUTHENTICATION", "Authentication Options");
define("_MD_AM_AUTHSERVICE", "Authentication Service");
define("_MD_AM_AUTHSERVICEDESC", "Which authentication service would you like to use for signing on users.");
define("_MD_AM_LDAP_MAIL_ATTR","LDAP - Mail Field Name");
define("_MD_AM_LDAP_MAIL_ATTR_DESC","The name of the E-Mail field in your LDAP directory tree.");
define("_MD_AM_LDAP_LOCATION_ATTR","LDAP - Location Field Name");
define("_MD_AM_LDAP_LOCATION_ATTR_DESC","The name of the Location Name field in your LDAP directory.");
define("_MD_AM_LDAP_OFFICE_ATTR","LDAP - Office Location Field Name");
define("_MD_AM_LDAP_OFFICE_ATTR_DESC","The name of the Physical Delivery Office Name field in your LDAP directory.");
define("_MD_AM_LDAP_NAME_ATTR","LDAP - Common Name Field Name");
define("_MD_AM_LDAP_NAME_ATTR_DESC","The name of the Comman Name field in your LDAP directory.");
define("_MD_AM_LDAP_SURNAME_ATTR","LDAP - Surname Fiend Name");
define("_MD_AM_LDAP_SURNAME_ATTR_DESC","The name of the Surname field in your LDAP directory.");
define("_MD_AM_LDAP_GIVENNAME_ATTR","LDAP - Given Name Field Name");
define("_MD_AM_LDAP_GIVENNAME_ATTR_DSC","The name of the Given Name field in your LDAP directory.");
define("_MD_AM_LDAP_UID_ATTR","LDAP - UID Field Name");
define("_MD_AM_LDAP_UID_ATTR_DESC","The name of the User ID field in your LDAP directory.");
define("_MD_AM_LDAP_BASE_DN", "LDAP - Base DN");
define("_MD_AM_LDAP_BASE_DN_DESC", "The base DN (Distinguished Name) of your LDAP directory tree.");
define("_MD_AM_LDAP_PORT","LDAP - Port Number");
define("_MD_AM_LDAP_PORT_DESC","The port number needed to access your LDAP directory server.");
define("_MD_AM_LDAP_SERVER","LDAP - Server Name");
define("_MD_AM_LDAP_SERVER_DESC","The name of your LDAP directory server.");
[/list]
Update XOOPS Core CodeI've added one function to one file and have used this new function in authenticationservice.php. The new function is simply a copy of the get function in configcategory.php. I've named it getName and altered it so that it instantiates the configcategory object with values based on the name of the category rather than the get functions id of the category. Does that make any sense? The code should help:[list=1]
[*]Insert the following code into xoops\kernel\configcategory.php:
/**
* Retrieve a {@link XoopsConfigCategory}
*
* @param string $catName
*
* @return object {@link XoopsConfigCategory}, FALSE on fail
*/
function &getName($catName)
{
if ($catName != '') {
$sql = "SELECT * FROM ".$this->db->prefix("configcategory")." WHERE confcat_name='".$catName."'";
if (!$result = $this->db->query($sql)) {
return false;
}
$numrows = $this->db->getRowsNum($result);
if ($numrows == 1) {
$confcat = new XoopsConfigCategory();
$confcat->assignVars($this->db->fetchArray($result), false);
return $confcat;
}
}
return false;
}
[*]Remove the following line from xoops\mainfile.php:
define('XOOPS_AUTHENTICATION_SERVICE', 'xoops');
[/list]
Update the Authentication Hack Files[list=1]
[*]Remove the following line from the file xoops\kernel\authenticationservice.php:
require_once XOOPS_ROOT_PATH.'/include/authenticationservices/'.XOOPS_AUTHENTICATION_SERVICE.'.php';
[*]Add the following lines instead:
$config_handler =& xoops_gethandler('config'); //The config object allows us to look at the configuration options that are stored in the database.
$criteria = new CriteriaCompo(); //We need to ask the database which authentication service the admin wants to use. We'll use the Criteria Compo object to peform this query.
$criteria->add(new Criteria('conf_name', 'authentication_service')); //Search for the configuraion record with a conf_name of 'authentication service'.
$config =& $config_handler->getConfigs($criteria); //Perform the query.
require_once XOOPS_ROOT_PATH.'/include/authenticationservices/'.$config[0]->getVar('conf_value').'.php'; //The first item returned from the query will have a value that equals the filename (sans extension) of the authentication service to use.
[*]Add the following lines to the constructor in xoops\include\authenticationservices\ldap.php:
//Which category contains the Authentication Services configuration data?
$configCategory_handler =& xoops_gethandler('configcategory'); //The config object allows us to look at the configuration options that are stored in the database.
$configCategory = $configCategory_handler->getName("_MD_AM_AUTHENTICATION"); //getName initalizes the configCategory object with the data related to the row '_MD_AM_AUTHENTICATION'.
//Load in the initial values for the variables used by this class from the database.
$config_handler =& xoops_gethandler('config'); //The config object allows us to look at the configuration options that are stored in the database.
$criteria = new CriteriaCompo(); //We need to ask the database which authentication service the admin wants to use. We'll use the Criteria Compo object to peform this query.
$criteria->add(new Criteria('conf_catid', $configCategory->getVar("confcat_id"))); //Search for the configuraion record with the conf_ID associated with '_MD_AM_AUTHENTICATION'.
$config =& $config_handler->getConfigs($criteria); //Perform the query.
$confcount = count($config);
//Loop through the rows returned above. They should be those rows in the config table that are associated with the Authentication Services
//row in the configCategory table. For each row, interrogate which variable that row's data is associated with and load the value of that
//data into the appropriate class-level variable.
for ($count = 0; $count < $confcount; $count++) {
switch ($config[$count]->getVar('conf_title')) {
case '_MD_AM_LDAP_MAIL_ATTR':
$this->mail_attr = $config[$count]->getVar('conf_value');
break;
case '_MD_AM_LDAP_NAME_ATTR':
$this->name_attr = $config[$count]->getVar('conf_value');
break;
case '_MD_AM_LDAP_SURNAME_ATTR':
$this->surname_attr = $config[$count]->getVar('conf_value');
break;
case '_MD_AM_LDAP_GIVENNAME_ATTR':
$this->givenname_attr = $config[$count]->getVar('conf_value');
break;
case '_MD_AM_LDAP_LOCATION_ATTR':
$this->location_attr = $config[$count]->getVar('conf_value');
break;
case '_MD_AM_LDAP_PORT':
$this->ldap_port = $config[$count]->getVar('conf_value');
break;
case '_MD_AM_LDAP_SERVER':
$this->ldap_server = $config[$count]->getVar('conf_value');
break;
case '_MD_AM_LDAP_BASE_DN':
$this->base_dn = $config[$count]->getVar('conf_value');
break;
case '_MD_AM_LDAP_OFFICE_ATTR':
$this->office_attr = $config[$count]->getVar('conf_value');
break;
case '_MD_AM_LDAP_UID_ATTR':
$this->uid_attr = $config[$count]->getVar('conf_value');
break;
}
}
[/list]
Note: I have not intended to misrepresent anyone's views or to offend anyone with the remarks I have made.Note 2: I have edited this note and replaced HTML with BB codes. The 'list' codes do not work apparently. I think it is still more readable than it was though!