dolibarr  9.0.0
openid.class.php
Go to the documentation of this file.
1 <?php
2 /* Copyright (C) 2013 Laurent Destailleur <eldy@users.sourceforge.net>
3  *
4  * This program is free software; you can redistribute it and/or modify
5  * it under the terms of the GNU General Public License as published by
6  * the Free Software Foundation; either version 3 of the License, or
7  * (at your option) any later version.
8  *
9  * This program is distributed in the hope that it will be useful,
10  * but WITHOUT ANY WARRANTY; without even the implied warranty of
11  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12  * GNU General Public License for more details.
13  *
14  * You should have received a copy of the GNU General Public License
15  * along with this program. If not, see <http://www.gnu.org/licenses/>.
16  */
17 
28 {
29  var $openid_url_identity;
30  var $URLs = array();
31  var $error = array();
32  var $fields = array(
33  'required' => array(),
34  'optional' => array(),
35  );
36 
40  function __construct()
41  {
42  if (!function_exists('curl_exec'))
43  {
44  die('Error: Class SimpleOpenID requires curl extension to work');
45  }
46  }
47 
48  // phpcs:disable PEAR.NamingConventions.ValidFunctionName.NotCamelCaps
55  function SetOpenIDServer($a)
56  {
57  // phpcs:enable
58  $this->URLs['openid_server'] = $a;
59  }
60 
61  // phpcs:disable PEAR.NamingConventions.ValidFunctionName.NotCamelCaps
68  function SetTrustRoot($a)
69  {
70  // phpcs:enable
71  $this->URLs['trust_root'] = $a;
72  }
73 
74  // phpcs:disable PEAR.NamingConventions.ValidFunctionName.NotCamelCaps
81  function SetCancelURL($a)
82  {
83  // phpcs:enable
84  $this->URLs['cancel'] = $a;
85  }
86 
87  // phpcs:disable PEAR.NamingConventions.ValidFunctionName.NotCamelCaps
94  function SetApprovedURL($a)
95  {
96  // phpcs:enable
97  $this->URLs['approved'] = $a;
98  }
99 
100  // phpcs:disable PEAR.NamingConventions.ValidFunctionName.NotCamelCaps
107  function SetRequiredFields($a)
108  {
109  // phpcs:enable
110  if (is_array($a)) {
111  $this->fields['required'] = $a;
112  } else {
113  $this->fields['required'][] = $a;
114  }
115  }
116 
117  // phpcs:disable PEAR.NamingConventions.ValidFunctionName.NotCamelCaps
124  function SetOptionalFields($a)
125  {
126  // phpcs:enable
127  if (is_array($a)) {
128  $this->fields['optional'] = $a;
129  } else {
130  $this->fields['optional'][] = $a;
131  }
132  }
133 
134  // phpcs:disable PEAR.NamingConventions.ValidFunctionName.NotCamelCaps
141  function SetIdentity($a)
142  {
143  // phpcs:enable
144  // Set Identity URL
145  if ((stripos($a, 'http://') === false)
146  && (stripos($a, 'https://') === false)) {
147  $a = 'http://'.$a;
148  }
149  /*
150  $u = parse_url(trim($a));
151  if (!isset($u['path'])){
152  $u['path'] = '/';
153  }else if(substr($u['path'],-1,1) == '/'){
154  $u['path'] = substr($u['path'], 0, strlen($u['path'])-1);
155  }
156  if (isset($u['query'])){ // If there is a query string, then use identity as is
157  $identity = $a;
158  }else{
159  $identity = $u['scheme'] . '://' . $u['host'] . $u['path'];
160  }
161  //*/
162  $this->openid_url_identity = $a;
163  }
164 
165  // phpcs:disable PEAR.NamingConventions.ValidFunctionName.NotCamelCaps
171  function GetIdentity()
172  {
173  // phpcs:enable
174  // Get Identity
175  return $this->openid_url_identity;
176  }
177 
178  // phpcs:disable PEAR.NamingConventions.ValidFunctionName.NotCamelCaps
184  function GetError()
185  {
186  // phpcs:enable
187  $e = $this->error;
188  return array('code'=>$e[0],'description'=>$e[1]);
189  }
190 
191  // phpcs:disable PEAR.NamingConventions.ValidFunctionName.NotCamelCaps
199  function ErrorStore($code, $desc = null)
200  {
201  // phpcs:enable
202  $errs['OPENID_NOSERVERSFOUND'] = 'Cannot find OpenID Server TAG on Identity page.';
203  if ($desc == null){
204  $desc = $errs[$code];
205  }
206  $this->error = array($code,$desc);
207  }
208 
209  // phpcs:disable PEAR.NamingConventions.ValidFunctionName.NotCamelCaps
215  function IsError()
216  {
217  // phpcs:enable
218  if (count($this->error) > 0)
219  {
220  return true;
221  }
222  else
223  {
224  return false;
225  }
226  }
227 
234  function splitResponse($response)
235  {
236  $r = array();
237  $response = explode("\n", $response);
238  foreach($response as $line) {
239  $line = trim($line);
240  if ($line != "") {
241  list($key, $value) = explode(":", $line, 2);
242  $r[trim($key)] = trim($value);
243  }
244  }
245  return $r;
246  }
247 
248  // phpcs:disable PEAR.NamingConventions.ValidFunctionName.NotCamelCaps
255  function OpenID_Standarize($openid_identity = null)
256  {
257  // phpcs:enable
258  if ($openid_identity === null)
259  $openid_identity = $this->openid_url_identity;
260 
261  $u = parse_url(strtolower(trim($openid_identity)));
262 
263  if (!isset($u['path']) || ($u['path'] == '/')) {
264  $u['path'] = '';
265  }
266  if(substr($u['path'],-1,1) == '/'){
267  $u['path'] = substr($u['path'], 0, strlen($u['path'])-1);
268  }
269  if (isset($u['query'])){ // If there is a query string, then use identity as is
270  return $u['host'] . $u['path'] . '?' . $u['query'];
271  }else{
272  return $u['host'] . $u['path'];
273  }
274  }
275 
282  function array2url($arr)
283  {
284  // converts associated array to URL Query String
285  if (!is_array($arr)){
286  return false;
287  }
288  $query = '';
289  foreach($arr as $key => $value){
290  $query .= $key . "=" . $value . "&";
291  }
292  return $query;
293  }
294 
295  // phpcs:disable PEAR.NamingConventions.ValidFunctionName.NotCamelCaps
304  function FSOCK_Request($url, $method="GET", $params = "")
305  {
306  // phpcs:enable
307  $fp = fsockopen("ssl://www.myopenid.com", 443, $errno, $errstr, 3); // Connection timeout is 3 seconds
308  if (!$fp) {
309  $this->ErrorStore('OPENID_SOCKETERROR', $errstr);
310  return false;
311  } else {
312  $request = $method . " /server HTTP/1.0\r\n";
313  $request .= "User-Agent: Dolibarr\r\n";
314  $request .= "Connection: close\r\n\r\n";
315  fwrite($fp, $request);
316  stream_set_timeout($fp, 4); // Connection response timeout is 4 seconds
317  $res = fread($fp, 2000);
318  $info = stream_get_meta_data($fp);
319  fclose($fp);
320 
321  if ($info['timed_out']) {
322  $this->ErrorStore('OPENID_SOCKETTIMEOUT');
323  } else {
324  return $res;
325  }
326  }
327  }
328 
329  // phpcs:disable PEAR.NamingConventions.ValidFunctionName.NotCamelCaps
338  function CURL_Request($url, $method="GET", $params = "")
339  {
340  // phpcs:enable
341  // Remember, SSL MUST BE SUPPORTED
342  if (is_array($params)) $params = $this->array2url($params);
343 
344  $curl = curl_init($url . ($method == "GET" && $params != "" ? "?" . $params : ""));
345  @curl_setopt($curl, CURLOPT_FOLLOWLOCATION, true);
346  curl_setopt($curl, CURLOPT_HEADER, false);
347  curl_setopt($curl, CURLOPT_SSL_VERIFYPEER, false);
348  curl_setopt($curl, CURLOPT_HTTPGET, ($method == "GET"));
349  curl_setopt($curl, CURLOPT_POST, ($method == "POST"));
350  if ($method == "POST") curl_setopt($curl, CURLOPT_POSTFIELDS, $params);
351  curl_setopt($curl, CURLOPT_RETURNTRANSFER, true);
352  $response = curl_exec($curl);
353 
354  if (curl_errno($curl) == 0){
355  $response;
356  }else{
357  $this->ErrorStore('OPENID_CURL', curl_error($curl));
358  }
359  return $response;
360  }
361 
362  // phpcs:disable PEAR.NamingConventions.ValidFunctionName.NotCamelCaps
369  function HTML2OpenIDServer($content)
370  {
371  // phpcs:enable
372  $get = array();
373 
374  // Get details of their OpenID server and (optional) delegate
375  preg_match_all('/<link[^>]*rel=[\'"]openid.server[\'"][^>]*href=[\'"]([^\'"]+)[\'"][^>]*\/?>/i', $content, $matches1);
376  preg_match_all('/<link[^>]*href=\'"([^\'"]+)[\'"][^>]*rel=[\'"]openid.server[\'"][^>]*\/?>/i', $content, $matches2);
377  $servers = array_merge($matches1[1], $matches2[1]);
378 
379  preg_match_all('/<link[^>]*rel=[\'"]openid.delegate[\'"][^>]*href=[\'"]([^\'"]+)[\'"][^>]*\/?>/i', $content, $matches1);
380 
381  preg_match_all('/<link[^>]*href=[\'"]([^\'"]+)[\'"][^>]*rel=[\'"]openid.delegate[\'"][^>]*\/?>/i', $content, $matches2);
382 
383  $delegates = array_merge($matches1[1], $matches2[1]);
384 
385  $ret = array($servers, $delegates);
386  return $ret;
387  }
388 
389 
390  // phpcs:disable PEAR.NamingConventions.ValidFunctionName.NotCamelCaps
397  function GetOpenIDServer($url='')
398  {
399  // phpcs:enable
400  global $conf;
401 
402  include_once DOL_DOCUMENT_ROOT.'/core/lib/geturl.lib.php';
403  if (empty($url)) $url=$conf->global->MAIN_AUTHENTICATION_OPENID_URL;
404 
405  $response = getURLContent($url);
406 
407  list($servers, $delegates) = $this->HTML2OpenIDServer($response);
408  if (count($servers) == 0) {
409  $this->ErrorStore('OPENID_NOSERVERSFOUND');
410  return false;
411  }
412  if (isset($delegates[0])
413  && ($delegates[0] != "")) {
414  $this->SetIdentity($delegates[0]);
415  }
416  $this->SetOpenIDServer($servers[0]);
417  return $servers[0];
418  }
419 
420  // phpcs:disable PEAR.NamingConventions.ValidFunctionName.NotCamelCaps
426  function GetRedirectURL()
427  {
428  // phpcs:enable
429  $params = array();
430  $params['openid.return_to'] = urlencode($this->URLs['approved']);
431  $params['openid.mode'] = 'checkid_setup';
432  $params['openid.identity'] = urlencode($this->openid_url_identity);
433  $params['openid.trust_root'] = urlencode($this->URLs['trust_root']);
434 
435  if (isset($this->fields['required'])
436  && (count($this->fields['required']) > 0)) {
437  $params['openid.sreg.required'] = implode(',',$this->fields['required']);
438  }
439  if (isset($this->fields['optional'])
440  && (count($this->fields['optional']) > 0)) {
441  $params['openid.sreg.optional'] = implode(',',$this->fields['optional']);
442  }
443  return $this->URLs['openid_server'] . "?". $this->array2url($params);
444  }
445 
446  // phpcs:disable PEAR.NamingConventions.ValidFunctionName.NotCamelCaps
452  function Redirect()
453  {
454  // phpcs:enable
455  $redirect_to = $this->GetRedirectURL();
456  if (headers_sent())
457  { // Use JavaScript to redirect if content has been previously sent (not recommended, but safe)
458  echo '<script language="JavaScript" type="text/javascript">window.location=\'';
459  echo $redirect_to;
460  echo '\';</script>';
461  }
462  else
463  { // Default Header Redirect
464  header('Location: ' . $redirect_to);
465  }
466  }
467 
468  // phpcs:disable PEAR.NamingConventions.ValidFunctionName.NotCamelCaps
474  function ValidateWithServer()
475  {
476  // phpcs:enable
477  $params = array(
478  'openid.assoc_handle' => urlencode($_GET['openid_assoc_handle']),
479  'openid.signed' => urlencode($_GET['openid_signed']),
480  'openid.sig' => urlencode($_GET['openid_sig'])
481  );
482  // Send only required parameters to confirm validity
483  $arr_signed = explode(",",str_replace('sreg.','sreg_',$_GET['openid_signed']));
484  $num = count($arr_signed);
485  for ($i = 0; $i < $num; $i++)
486  {
487  $s = str_replace('sreg_','sreg.', $arr_signed[$i]);
488  $c = $_GET['openid_' . $arr_signed[$i]];
489  // if ($c != ""){
490  $params['openid.' . $s] = urlencode($c);
491  // }
492  }
493  $params['openid.mode'] = "check_authentication";
494 
495  $openid_server = $this->GetOpenIDServer();
496  if ($openid_server == false)
497  {
498  return false;
499  }
500  $response = $this->CURL_Request($openid_server,'POST',$params);
501  $data = $this->splitResponse($response);
502  if ($data['is_valid'] == "true")
503  {
504  return true;
505  }
506  else
507  {
508  return false;
509  }
510  }
511 
512 
513 
514 
521  function sendDiscoveryRequestToGetXRDS($url='')
522  {
523  global $conf;
524 
525  include_once DOL_DOCUMENT_ROOT.'/core/lib/geturl.lib.php';
526  if (empty($url)) $url=$conf->global->MAIN_AUTHENTICATION_OPENID_URL;
527 
528  dol_syslog(get_class($this).'::sendDiscoveryRequestToGetXRDS get XRDS');
529 
530  $addheaders=array('Accept: application/xrds+xml');
531  $response = getURLContent($url, 'GET', '', 1, $addheaders);
532  /* response should like this:
533  <?xml version="1.0" encoding="UTF-8"?>
534  <xrds:XRDS xmlns:xrds="xri://$xrds" xmlns="xri://$xrd*($v*2.0)">
535  <XRD>
536  <Service priority="0">
537  <Type>http://specs.openid.net/auth/2.0/server</Type>
538  <Type>http://openid.net/srv/ax/1.0</Type>
539  ...
540  <URI>https://www.google.com/accounts/o8/ud</URI>
541  </Service>
542  </XRD>
543  </xrds:XRDS>
544  */
545  $content=$response['content'];
546 
547  $server='';
548  if (preg_match('/'.preg_quote('<URI>','/').'(.*)'.preg_quote('</URI>','/').'/is', $content, $reg))
549  {
550  $server=$reg[1];
551  }
552 
553  if (empty($server))
554  {
555  $this->ErrorStore('OPENID_NOSERVERSFOUND');
556  return false;
557  }
558  else
559  {
560  dol_syslog(get_class($this).'::sendDiscoveryRequestToGetXRDS found endpoint = '.$server);
561  $this->SetOpenIDServer($server);
562  return $server;
563  }
564  }
565 }
SetOptionalFields($a)
SetOptionalFields.
SetApprovedURL($a)
SetApprovedURL.
SetOpenIDServer($a)
SetOpenIDServer.
GetIdentity()
GetIdentity.
sendDiscoveryRequestToGetXRDS($url='')
Get XRDS response and set possible servers.
HTML2OpenIDServer($content)
HTML2OpenIDServer.
array2url($arr)
array2url
SetIdentity($a)
SetIdentity.
GetRedirectURL()
GetRedirectURL.
IsError()
IsError.
ErrorStore($code, $desc=null)
ErrorStore.
getURLContent($url, $postorget='GET', $param='', $followlocation=1, $addheaders=array())
Function get content from an URL (use proxy if proxy defined)
Definition: geturl.lib.php:34
SetRequiredFields($a)
SetRequiredFields.
SetCancelURL($a)
SetOpenIDServer.
SetTrustRoot($a)
SetOpenIDServer.
CURL_Request($url, $method="GET", $params="")
CURL_Request.
OpenID_Standarize($openid_identity=null)
OpenID_Standarize.
FSOCK_Request($url, $method="GET", $params="")
FSOCK_Request.
GetOpenIDServer($url='')
Get openid server.
splitResponse($response)
splitResponse
GetError()
SetOpenIDServer.
__construct()
Constructor.
Redirect()
Redirect.
Class to manage OpenID.