From 491bd76c1befd484a8a0707c13d14f0743815354 Mon Sep 17 00:00:00 2001 From: Laurent Destailleur Date: Wed, 18 Sep 2013 21:22:24 +0200 Subject: [PATCH] Work on openif authentication mode --- .../openid.class.php} | 170 ++++++++---------- htdocs/core/lib/geturl.lib.php | 47 +++-- htdocs/core/login/functions_openid.php | 116 ++++++++++++ htdocs/core/tpl/login.tpl.php | 21 +++ htdocs/langs/en_US/errors.lang | 1 + htdocs/langs/en_US/users.lang | 3 +- htdocs/langs/fr_FR/errors.lang | 1 + htdocs/langs/fr_FR/users.lang | 2 + htdocs/main.inc.php | 2 +- htdocs/user/fiche.php | 33 ++-- 10 files changed, 266 insertions(+), 130 deletions(-) rename htdocs/core/{login/functions_myopenid.php => class/openid.class.php} (77%) create mode 100644 htdocs/core/login/functions_openid.php diff --git a/htdocs/core/login/functions_myopenid.php b/htdocs/core/class/openid.class.php similarity index 77% rename from htdocs/core/login/functions_myopenid.php rename to htdocs/core/class/openid.class.php index bb46e5fcea0..0b4652fb0bf 100644 --- a/htdocs/core/login/functions_myopenid.php +++ b/htdocs/core/class/openid.class.php @@ -1,6 +1,5 @@ - * Copyright (C) 2007-2009 Regis Houssin +/* Copyright (C) 2013 Laurent Destailleur * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -17,102 +16,11 @@ */ /** - * \file htdocs/core/login/functions_myopenid.php + * \file htdocs/core/class/openid.class.php * \ingroup core - * \brief Authentication functions for OpenId mode + * \brief Class to manage authentication with OpenId */ - -/** - * Check validity of user/password/entity - * If test is ko, reason must be filled into $_SESSION["dol_loginmesg"] - * - * @param string $usertotest Login - * @param string $passwordtotest Password - * @param int $entitytotest Number of instance (always 1 if module multicompany not enabled) - * @return string Login if OK, '' if KO - */ -function check_user_password_myopenid($usertotest,$passwordtotest,$entitytotest) -{ - global $_POST,$db,$conf,$langs; - - dol_syslog("functions_dolibarr::check_user_password_myopenid usertotest=".$usertotest); - - $login=''; - - // Get identity from user and redirect browser to OpenID Server - if (isset($_POST['username'])) - { - $openid = new SimpleOpenID(); - $openid->SetIdentity($_POST['username']); - $protocol = ($conf->file->main_force_https ? 'https://' : 'http://'); - $openid->SetTrustRoot($protocol . $_SERVER["HTTP_HOST"]); - $openid->SetRequiredFields(array('email','fullname')); - $_SESSION['dol_entity'] = $_POST["entity"]; - //$openid->SetOptionalFields(array('dob','gender','postcode','country','language','timezone')); - if ($openid->GetOpenIDServer()) - { - $openid->SetApprovedURL($protocol . $_SERVER["HTTP_HOST"] . $_SERVER["SCRIPT_NAME"]); // Send Response from OpenID server to this script - $openid->Redirect(); // This will redirect user to OpenID Server - } - else - { - $error = $openid->GetError(); - return false; - } - return false; - } - // Perform HTTP Request to OpenID server to validate key - elseif($_GET['openid_mode'] == 'id_res') - { - $openid = new SimpleOpenID(); - $openid->SetIdentity($_GET['openid_identity']); - $openid_validation_result = $openid->ValidateWithServer(); - if ($openid_validation_result == true) - { - // OK HERE KEY IS VALID - - $sql ="SELECT login"; - $sql.=" FROM ".MAIN_DB_PREFIX."user"; - $sql.=" WHERE openid = '".$db->escape($_GET['openid_identity'])."'"; - $sql.=" AND entity IN (0," . ($_SESSION["dol_entity"] ? $_SESSION["dol_entity"] : 1) . ")"; - - dol_syslog("functions_dolibarr::check_user_password_myopenid sql=".$sql); - $resql=$db->query($sql); - if ($resql) - { - $obj=$db->fetch_object($resql); - if ($obj) - { - $login=$obj->login; - } - } - } - else if($openid->IsError() == true) - { - // ON THE WAY, WE GOT SOME ERROR - $error = $openid->GetError(); - return false; - } - else - { - // Signature Verification Failed - //echo "INVALID AUTHORIZATION"; - return false; - } - } - else if ($_GET['openid_mode'] == 'cancel') - { - // User Canceled your Request - //echo "USER CANCELED REQUEST"; - return false; - } - - return $login; -} - - - /** * Class to manage OpenID */ @@ -416,9 +324,22 @@ class SimpleOpenID return $ret; } - function GetOpenIDServer() + + /** + * Get openid server + * + * @param string $url Url to found endpoint + * @return string Endpoint + */ + function GetOpenIDServer($url='') { - $response = $this->CURL_Request($this->openid_url_identity); + global $conf; + + include_once DOL_DOCUMENT_ROOT.'/core/lib/geturl.lib.php'; + if (empty($url)) $url=$conf->global->MAIN_AUTHENTICATION_OPENID_URL; + + $response = getURLContent($url); + list($servers, $delegates) = $this->HTML2OpenIDServer($response); if (count($servers) == 0){ $this->ErrorStore('OPENID_NOSERVERSFOUND'); @@ -517,6 +438,61 @@ class SimpleOpenID return false; } } + + + + + /** + * Get XRDS response and set possible servers. + * + * @param string $url Url of endpoint to request + * @return string First endpoint OpenID server found. False if it failed to found. + */ + function sendDiscoveryRequestToGetXRDS($url='') + { + global $conf; + + include_once DOL_DOCUMENT_ROOT.'/core/lib/geturl.lib.php'; + if (empty($url)) $url=$conf->global->MAIN_AUTHENTICATION_OPENID_URL; + + dol_syslog(get_class($this).'::sendDiscoveryRequestToGetXRDS get XRDS'); + + $addheaders=array('Accept: application/xrds+xml'); + $response = getURLContent($url, 'GET', '', 1, $addheaders); + /* response should like this: + + + + + http://specs.openid.net/auth/2.0/server + http://openid.net/srv/ax/1.0 + ... + https://www.google.com/accounts/o8/ud + + + + */ + $content=$response['content']; + + $server=''; + if (preg_match('/'.preg_quote('','/').'(.*)'.preg_quote('','/').'/is', $content, $reg)) + { + $server=$reg[1]; + } + + if (empty($server)) + { + $this->ErrorStore('OPENID_NOSERVERSFOUND'); + return false; + } + else + { + dol_syslog(get_class($this).'::sendDiscoveryRequestToGetXRDS found endpoint = '.$server); + $this->SetOpenIDServer($server); + return $server; + } + } + } ?> \ No newline at end of file diff --git a/htdocs/core/lib/geturl.lib.php b/htdocs/core/lib/geturl.lib.php index d0c6d29ddea..d762dc8f631 100644 --- a/htdocs/core/lib/geturl.lib.php +++ b/htdocs/core/lib/geturl.lib.php @@ -25,12 +25,14 @@ /** * Function get content from an URL (use proxy if proxy defined) * - * @param string $url URL to call. - * @param string $postorget 'post' = POST, 'get='GET' - * @param string $param Paraemeters of URL (x=value1&y=value2) - * @return array returns an associtive array containing the response from the server. + * @param string $url URL to call. + * @param string $postorget 'POST', 'GET', 'HEAD' + * @param string $param Paraemeters of URL (x=value1&y=value2) + * @param string $followlocation 1=Follow location, 0=Do not follow + * @param array $addhearers Array of string to add into header. Example: ('Accept: application/xrds+xml', ....) + * @return array Returns an associative array containing the response from the server array('content'=>response,'curl_error_no'=>errno,'curl_error_msg'=>errmsg...) */ -function getURLContent($url,$postorget='GET',$param='') +function getURLContent($url,$postorget='GET',$param='',$followlocation=1,$addheaders=array()) { //declaring of global variables global $conf, $langs; @@ -52,6 +54,11 @@ function getURLContent($url,$postorget='GET',$param='') curl_setopt($ch, CURLOPT_URL, $url); curl_setopt($ch, CURLOPT_VERBOSE, 1); curl_setopt($ch, CURLOPT_SSLVERSION, 3); // Force SSLv3 + curl_setopt($ch, CURLOPT_USERAGENT, 'Dolibarr geturl function'); + + curl_setopt($ch, CURLOPT_FOLLOWLOCATION, ($followlocation?true:false)); + if (count($addheaders)) curl_setopt($ch, CURLOPT_HTTPHEADER, $addheaders); + curl_setopt($ch, CURLINFO_HEADER_OUT, true); // To be able to retrieve request header and log it //turning off the server and peer verification(TrustManager Concept). curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, FALSE); @@ -60,9 +67,21 @@ function getURLContent($url,$postorget='GET',$param='') curl_setopt($ch, CURLOPT_CONNECTTIMEOUT, empty($conf->global->MAIN_USE_CONNECT_TIMEOUT)?5:$conf->global->MAIN_USE_CONNECT_TIMEOUT); curl_setopt($ch, CURLOPT_TIMEOUT, empty($conf->global->MAIN_USE_RESPONSE_TIMEOUT)?30:$conf->global->MAIN_USE_RESPONSE_TIMEOUT); - curl_setopt($ch, CURLOPT_RETURNTRANSFER,1); - if ($postorget == 'POST') curl_setopt($ch, CURLOPT_POST, 1); - else curl_setopt($ch, CURLOPT_POST, 0); + curl_setopt($ch, CURLOPT_RETURNTRANSFER,1); // We want response + if ($postorget == 'POST') + { + curl_setopt($ch, CURLOPT_POST, 1); // POST + curl_setopt($ch, CURLOPT_POSTFIELDS, $param); // Setting param x=a&y=z as POST fields + } + else if ($postorget == 'HEAD') + { + curl_setopt($ch, CURLOPT_CUSTOMREQUEST, 'HEAD'); // HTTP request is 'HEAD' + curl_setopt($ch, CURLOPT_NOBODY, true); + } + else + { + curl_setopt($ch, CURLOPT_POST, 0); // GET + } //if USE_PROXY constant set to TRUE in Constants.php, then only proxy will be enabled. if ($USE_PROXY) @@ -73,18 +92,18 @@ function getURLContent($url,$postorget='GET',$param='') if ($PROXY_USER) curl_setopt($ch, CURLOPT_PROXYUSERPWD, $PROXY_USER. ":" . $PROXY_PASS); } - //setting the nvpreq as POST FIELD to curl - curl_setopt($ch, CURLOPT_POSTFIELDS, $param); - //getting response from server $response = curl_exec($ch); + $status = curl_getinfo($ch, CURLINFO_HEADER_OUT); // Reading of request must be done after sending request + dol_syslog("getURLContent request=".$status); + + dol_syslog("getURLContent response=".$response); + $rep=array(); $rep['content']=$response; $rep['curl_error_no']=''; $rep['curl_error_msg']=''; - - dol_syslog("getURLContent response=".$response); if (curl_errno($ch)) { @@ -98,7 +117,7 @@ function getURLContent($url,$postorget='GET',$param='') { $info = curl_getinfo($ch); $rep['header_size']=$info['header_size']; - + //closing the curl curl_close($ch); } diff --git a/htdocs/core/login/functions_openid.php b/htdocs/core/login/functions_openid.php new file mode 100644 index 00000000000..e183bc40795 --- /dev/null +++ b/htdocs/core/login/functions_openid.php @@ -0,0 +1,116 @@ + + * Copyright (C) 2007-2009 Regis Houssin + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +/** + * \file htdocs/core/login/functions_openid.php + * \ingroup core + * \brief Authentication functions for OpenId mode + */ + +include_once DOL_DOCUMENT_ROOT.'/core/class/openid.class.php'; + + +/** + * Check validity of user/password/entity + * If test is ko, reason must be filled into $_SESSION["dol_loginmesg"] + * + * @param string $usertotest Login + * @param string $passwordtotest Password + * @param int $entitytotest Number of instance (always 1 if module multicompany not enabled) + * @return string Login if OK, '' if KO + */ +function check_user_password_openid($usertotest,$passwordtotest,$entitytotest) +{ + global $_POST,$db,$conf,$langs; + + dol_syslog("functions_openid::check_user_password_openid usertotest=".$usertotest); + + $login=''; + + // Get identity from user and redirect browser to OpenID Server + if (isset($_POST['username'])) + { + $openid = new SimpleOpenID(); + $openid->SetIdentity($_POST['username']); + $protocol = ($conf->file->main_force_https ? 'https://' : 'http://'); + $openid->SetTrustRoot($protocol . $_SERVER["HTTP_HOST"]); + $openid->SetRequiredFields(array('email','fullname')); + $_SESSION['dol_entity'] = $_POST["entity"]; + //$openid->SetOptionalFields(array('dob','gender','postcode','country','language','timezone')); + if ($openid->sendDiscoveryRequestToGetXRDS()) + { + $openid->SetApprovedURL($protocol . $_SERVER["HTTP_HOST"] . $_SERVER["SCRIPT_NAME"]); // Send Response from OpenID server to this script + $openid->Redirect(); // This will redirect user to OpenID Server + } + else + { + $error = $openid->GetError(); + return false; + } + return false; + } + // Perform HTTP Request to OpenID server to validate key + elseif($_GET['openid_mode'] == 'id_res') + { + $openid = new SimpleOpenID(); + $openid->SetIdentity($_GET['openid_identity']); + $openid_validation_result = $openid->ValidateWithServer(); + if ($openid_validation_result == true) + { + // OK HERE KEY IS VALID + + $sql ="SELECT login"; + $sql.=" FROM ".MAIN_DB_PREFIX."user"; + $sql.=" WHERE openid = '".$db->escape($_GET['openid_identity'])."'"; + $sql.=" AND entity IN (0," . ($_SESSION["dol_entity"] ? $_SESSION["dol_entity"] : 1) . ")"; + + dol_syslog("functions_openid::check_user_password_openid sql=".$sql); + $resql=$db->query($sql); + if ($resql) + { + $obj=$db->fetch_object($resql); + if ($obj) + { + $login=$obj->login; + } + } + } + else if($openid->IsError() == true) + { + // ON THE WAY, WE GOT SOME ERROR + $error = $openid->GetError(); + return false; + } + else + { + // Signature Verification Failed + //echo "INVALID AUTHORIZATION"; + return false; + } + } + else if ($_GET['openid_mode'] == 'cancel') + { + // User Canceled your Request + //echo "USER CANCELED REQUEST"; + return false; + } + + return $login; +} + +?> \ No newline at end of file diff --git a/htdocs/core/tpl/login.tpl.php b/htdocs/core/tpl/login.tpl.php index a31dc7e466b..ebc6fea7626 100644 --- a/htdocs/core/tpl/login.tpl.php +++ b/htdocs/core/tpl/login.tpl.php @@ -152,6 +152,27 @@ if ($forgetpasslink || $helpcenterlink) } echo ''; } + +if (isset($conf->file->main_authentication) && preg_match('/openid/',$conf->file->main_authentication)) +{ + $langs->load("users"); + + //if (! empty($conf->global->MAIN_OPENIDURL_PERUSER)) $url= + echo '
'; + echo '
'; + + $url=$conf->global->MAIN_AUTHENTICATION_OPENID_URL; + if (! empty($url)) print ''.$langs->trans("LoginUsingOpenID").''; + else + { + $langs->load("errors"); + print ''.$langs->trans("ErrorOpenIDSetupNotComplete",'MAIN_AUTHENTICATION_OPENID_URL').''; + } + + echo '
'; +} + + ?> diff --git a/htdocs/langs/en_US/errors.lang b/htdocs/langs/en_US/errors.lang index 7c182f17360..05d26e281ea 100644 --- a/htdocs/langs/en_US/errors.lang +++ b/htdocs/langs/en_US/errors.lang @@ -126,6 +126,7 @@ ErrorFailedToAddContact=Failed to add contact ErrorDateMustBeBeforeToday=The date can not be greater than today ErrorPaymentModeDefinedToWithoutSetup=A payment mode was set to type %s but setup of module Invoice was not completed to define information to show for this payment mode. ErrorPHPNeedModule=Error, your PHP must have module %s installed to use this feature. +ErrorOpenIDSetupNotComplete=You setup Dolibarr config file to allow OpenID authentication, but URL of OpenID service is not defined into constant %s # Warnings WarningMandatorySetupNotComplete=Mandatory setup parameters are not yet defined diff --git a/htdocs/langs/en_US/users.lang b/htdocs/langs/en_US/users.lang index e8abd699b9c..b029eb61de9 100644 --- a/htdocs/langs/en_US/users.lang +++ b/htdocs/langs/en_US/users.lang @@ -117,4 +117,5 @@ DontDowngradeSuperAdmin=Only a superadmin can downgrade a superadmin HierarchicalResponsible=Hierarchical responsible HierarchicView=Hierarchical view UseTypeFieldToChange=Use field Type to change - +OpenIDURL=OpenID URL +LoginUsingOpenID=Login using OpenID \ No newline at end of file diff --git a/htdocs/langs/fr_FR/errors.lang b/htdocs/langs/fr_FR/errors.lang index d133620fb16..37a04be796d 100644 --- a/htdocs/langs/fr_FR/errors.lang +++ b/htdocs/langs/fr_FR/errors.lang @@ -126,6 +126,7 @@ ErrorFailedToAddContact=Echec à l'ajout du contact ErrorDateMustBeBeforeToday=La date ne peut pas être supérieure à aujourd'hui ErrorPaymentModeDefinedToWithoutSetup=Un mode de paiement a été défini de type %s mais la configuration du module Facture n'a pas été complété pour définir les informations afficher pour ce mode de paiment. ErrorPHPNeedModule=Erreur, votre PHP doit avoir le module %s installé pour utiliser cette fonctionnalité. +ErrorOpenIDSetupNotComplete=Vous avez configuré Dolibarr pour accepter l'authentication OpenID, mais l'URL du service OpenID n'est pas défini dans la constante %s # Warnings WarningMandatorySetupNotComplete=Les informations de configuration obligatoire doivent être renseignées diff --git a/htdocs/langs/fr_FR/users.lang b/htdocs/langs/fr_FR/users.lang index 46873f4e331..c3ffb33b24a 100644 --- a/htdocs/langs/fr_FR/users.lang +++ b/htdocs/langs/fr_FR/users.lang @@ -117,3 +117,5 @@ DontDowngradeSuperAdmin=Seul un superadministrateur peut rétrograder un superad HierarchicalResponsible=Responsable hiérarchique HierarchicView=Vue hiérarchique UseTypeFieldToChange=Modifier le champ Type pour changer +OpenIDURL=URL de connexion OpenID +LoginUsingOpenID=Se logguer via OpenID \ No newline at end of file diff --git a/htdocs/main.inc.php b/htdocs/main.inc.php index 5deef696da0..b128b48b6b5 100644 --- a/htdocs/main.inc.php +++ b/htdocs/main.inc.php @@ -435,7 +435,7 @@ if (! defined('NOLOGIN')) $login = checkLoginPassEntity($usertotest,$passwordtotest,$entitytotest,$authmode); if ($login) { - $dol_authmode=$conf->authmode; // This properties is defined only when logged to say what mode was successfully used + $dol_authmode=$conf->authmode; // This properties is defined only when logged, to say what mode was successfully used $dol_tz=$_POST["tz"]; $dol_tz_string=$_POST["tz_string"]; $dol_dst=0; diff --git a/htdocs/user/fiche.php b/htdocs/user/fiche.php index a66d6108d5f..caa0837f701 100644 --- a/htdocs/user/fiche.php +++ b/htdocs/user/fiche.php @@ -1049,7 +1049,7 @@ else print ''; print ''."\n"; - if (isset($conf->authmode) && preg_match('/myopenid/',$conf->authmode)) $rowspan++; + if (isset($conf->file->main_authentication) && preg_match('/openid/',$conf->file->main_authentication) && ! empty($conf->global->MAIN_OPENIDURL_PERUSER)) $rowspan++; if (! empty($conf->societe->enabled)) $rowspan++; if (! empty($conf->adherent->enabled)) $rowspan++; @@ -1188,7 +1188,7 @@ else } print ''; print "\n"; - + // Accountancy code if (! empty($conf->global->USER_ENABLE_ACCOUNTANCY_CODE)) // For the moment field is not used so must not appeared. { @@ -1196,7 +1196,7 @@ else print ''.$langs->trans("AccountancyCode").''; print ''.$object->accountancy_code.''; } - + // Status print ''.$langs->trans("Status").''; print ''; @@ -1212,10 +1212,9 @@ else print ''.dol_print_date($object->datepreviouslogin,"dayhour").''; print "\n"; - - if (isset($conf->authmode) && preg_match('/myopenid/',$conf->authmode)) + if (isset($conf->file->main_authentication) && preg_match('/openid/',$conf->file->main_authentication) && ! empty($conf->global->MAIN_OPENIDURL_PERUSER)) { - print ''.$langs->trans("url_openid").''; + print ''.$langs->trans("OpenIDURL").''; print ''.$object->openid.''; print "\n"; } @@ -1512,7 +1511,10 @@ else */ if ($action == 'edit' && ($canedituser || $caneditfield || $caneditpassword || ($user->id == $object->id))) { - $rowspan=14; + $rowspan=15; + if (isset($conf->file->main_authentication) && preg_match('/openid/',$conf->file->main_authentication) && ! empty($conf->global->MAIN_OPENIDURL_PERUSER)) $rowspan++; + if (! empty($conf->societe->enabled)) $rowspan++; + if (! empty($conf->adherent->enabled)) $rowspan++; print '
'; print ''; @@ -1520,9 +1522,6 @@ else print ''; print ''; - if (! empty($conf->societe->enabled)) $rowspan++; - if (! empty($conf->adherent->enabled)) $rowspan++; - print ''; print ''; - // openid - if (isset($conf->authmode) && preg_match('/myopenid/',$conf->authmode)) + // OpenID url + if (isset($conf->file->main_authentication) && preg_match('/openid/',$conf->file->main_authentication) && ! empty($conf->global->MAIN_OPENIDURL_PERUSER)) { - print "".''; + print "".''; print ''; print "\n"; - + // Accountancy code print ""; print '';
'.$langs->trans("Ref").''; print $object->id; @@ -1791,17 +1790,17 @@ else } print '
'.$langs->trans("url_openid").'
'.$langs->trans("OpenIDURL").''; - if ($caneditfield && !$object->ldap_sid) + if ($caneditfield) { - print ''; + print ''; } else - { + { print ''; print $object->openid; } @@ -1824,7 +1823,7 @@ else } print '
'.$langs->trans("AccountancyCode").'