Browse Source

Work on openif authentication mode

Laurent Destailleur 12 years ago
parent
commit
491bd76c1b

+ 73 - 97
htdocs/core/login/functions_myopenid.php → htdocs/core/class/openid.class.php

@@ -1,6 +1,5 @@
 <?php
-/* Copyright (C) 2007-2008 Laurent Destailleur  <eldy@users.sourceforge.net>
- * Copyright (C) 2007-2009 Regis Houssin        <regis.houssin@capnetworks.com>
+/* Copyright (C) 2013 Laurent Destailleur  <eldy@users.sourceforge.net>
  *
  * 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:
+		<?xml version="1.0" encoding="UTF-8"?>
+		<xrds:XRDS xmlns:xrds="xri://$xrds" xmlns="xri://$xrd*($v*2.0)">
+		<XRD>
+		<Service priority="0">
+		<Type>http://specs.openid.net/auth/2.0/server</Type>
+		<Type>http://openid.net/srv/ax/1.0</Type>
+		...
+		<URI>https://www.google.com/accounts/o8/ud</URI>
+		</Service>
+		</XRD>
+		</xrds:XRDS>
+		*/
+		$content=$response['content'];
+
+        $server='';
+        if (preg_match('/'.preg_quote('<URI>','/').'(.*)'.preg_quote('</URI>','/').'/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;
+	    }
+    }
+
 }
 
 ?>

+ 33 - 14
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);
     }

+ 116 - 0
htdocs/core/login/functions_openid.php

@@ -0,0 +1,116 @@
+<?php
+/* Copyright (C) 2007-2013 Laurent Destailleur  <eldy@users.sourceforge.net>
+ * Copyright (C) 2007-2009 Regis Houssin        <regis.houssin@capnetworks.com>
+ *
+ * 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 <http://www.gnu.org/licenses/>.
+ */
+
+/**
+ *      \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;
+}
+
+?>

+ 21 - 0
htdocs/core/tpl/login.tpl.php

@@ -152,6 +152,27 @@ if ($forgetpasslink || $helpcenterlink)
 	}
 	echo '</div>';
 }
+
+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 '<br>';
+	echo '<div align="center" style="margin-top: 4px;">';
+
+	$url=$conf->global->MAIN_AUTHENTICATION_OPENID_URL;
+	if (! empty($url)) print '<a class="alogin" href="'.$url.'">'.$langs->trans("LoginUsingOpenID").'</a>';
+	else
+	{
+		$langs->load("errors");
+		print '<font class="warning">'.$langs->trans("ErrorOpenIDSetupNotComplete",'MAIN_AUTHENTICATION_OPENID_URL').'</font>';
+	}
+
+	echo '</div>';
+}
+
+
 ?>
 
 </div>

+ 1 - 0
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 <b>%s</b> 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

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

+ 1 - 0
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 <b>%s</b> 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

+ 2 - 0
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

+ 1 - 1
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;

+ 16 - 17
htdocs/user/fiche.php

@@ -1049,7 +1049,7 @@ else
             print '</td>';
             print '</tr>'."\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 '</td>';
             print "</tr>\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 '<tr><td valign="top">'.$langs->trans("AccountancyCode").'</td>';
             	print '<td>'.$object->accountancy_code.'</td>';
 			}
-            
+
             // Status
             print '<tr><td valign="top">'.$langs->trans("Status").'</td>';
             print '<td>';
@@ -1212,10 +1212,9 @@ else
             print '<td>'.dol_print_date($object->datepreviouslogin,"dayhour").'</td>';
             print "</tr>\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 '<tr><td valign="top">'.$langs->trans("url_openid").'</td>';
+                print '<tr><td valign="top">'.$langs->trans("OpenIDURL").'</td>';
                 print '<td>'.$object->openid.'</td>';
                 print "</tr>\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 '<form action="'.$_SERVER['PHP_SELF'].'?id='.$object->id.'" method="POST" name="updateuser" enctype="multipart/form-data">';
             print '<input type="hidden" name="token" value="'.$_SESSION['newtoken'].'">';
@@ -1520,9 +1522,6 @@ else
             print '<input type="hidden" name="entity" value="'.$object->entity.'">';
             print '<table width="100%" class="border">';
 
-            if (! empty($conf->societe->enabled)) $rowspan++;
-            if (! empty($conf->adherent->enabled)) $rowspan++;
-
             print '<tr><td width="25%" valign="top">'.$langs->trans("Ref").'</td>';
             print '<td colspan="2">';
             print $object->id;
@@ -1791,17 +1790,17 @@ else
             }
             print '</td></tr>';
 
-            // 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 "<tr>".'<td valign="top">'.$langs->trans("url_openid").'</td>';
+                print "<tr>".'<td valign="top">'.$langs->trans("OpenIDURL").'</td>';
                 print '<td>';
-                if ($caneditfield  && !$object->ldap_sid)
+                if ($caneditfield)
                 {
-                    print '<input size="40" type="text" name="openid" class="flat" value="'.$object->openid.'">';
+                    print '<input size="40" type="url" name="openid" class="flat" value="'.$object->openid.'">';
                 }
                 else
-                {
+              {
                     print '<input type="hidden" name="openid" value="'.$object->openid.'">';
                     print $object->openid;
                 }
@@ -1824,7 +1823,7 @@ else
             }
             print '</td>';
             print "</tr>\n";
-			
+
 			// Accountancy code
             print "<tr>";
             print '<td valign="top">'.$langs->trans("AccountancyCode").'</td>';