dolibarr  19.0.0-dev
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 <https://www.gnu.org/licenses/>.
16  */
17 
28 {
29  public $openid_url_identity;
30  public $URLs = array();
31  public $error = array();
32  public $fields = array(
33  'required' => array(),
34  'optional' => array(),
35  );
36 
40  public function __construct()
41  {
42  if (!function_exists('curl_exec')) {
43  die('Error: Class SimpleOpenID requires curl extension to work');
44  }
45  }
46 
47  // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
54  public function SetOpenIDServer($a)
55  {
56  // phpcs:enable
57  $this->URLs['openid_server'] = $a;
58  }
59 
60  // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
67  public function SetTrustRoot($a)
68  {
69  // phpcs:enable
70  $this->URLs['trust_root'] = $a;
71  }
72 
73  // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
80  public function SetCancelURL($a)
81  {
82  // phpcs:enable
83  $this->URLs['cancel'] = $a;
84  }
85 
86  // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
93  public function SetApprovedURL($a)
94  {
95  // phpcs:enable
96  $this->URLs['approved'] = $a;
97  }
98 
99  // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
106  public function SetRequiredFields($a)
107  {
108  // phpcs:enable
109  if (is_array($a)) {
110  $this->fields['required'] = $a;
111  } else {
112  $this->fields['required'][] = $a;
113  }
114  }
115 
116  // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
123  public function SetOptionalFields($a)
124  {
125  // phpcs:enable
126  if (is_array($a)) {
127  $this->fields['optional'] = $a;
128  } else {
129  $this->fields['optional'][] = $a;
130  }
131  }
132 
133  // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
140  public function SetIdentity($a)
141  {
142  // phpcs:enable
143  // Set Identity URL
144  if ((stripos($a, 'http://') === false)
145  && (stripos($a, 'https://') === false)) {
146  $a = 'http://'.$a;
147  }
148  /*
149  $u = parse_url(trim($a));
150  if (!isset($u['path'])){
151  $u['path'] = '/';
152  }else if(substr($u['path'],-1,1) == '/'){
153  $u['path'] = substr($u['path'], 0, strlen($u['path'])-1);
154  }
155  if (isset($u['query'])){ // If there is a query string, then use identity as is
156  $identity = $a;
157  }else{
158  $identity = $u['scheme'] . '://' . $u['host'] . $u['path'];
159  }
160  */
161  $this->openid_url_identity = $a;
162  }
163 
164  // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
170  public function GetIdentity()
171  {
172  // phpcs:enable
173  // Get Identity
174  return $this->openid_url_identity;
175  }
176 
177  // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
183  public function GetError()
184  {
185  // phpcs:enable
186  $e = $this->error;
187  return array('code'=>$e[0], 'description'=>$e[1]);
188  }
189 
190  // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
198  public function ErrorStore($code, $desc = null)
199  {
200  // phpcs:enable
201  $errs['OPENID_NOSERVERSFOUND'] = 'Cannot find OpenID Server TAG on Identity page.';
202  if ($desc == null) {
203  $desc = $errs[$code];
204  }
205  $this->error = array($code, $desc);
206  }
207 
208  // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
214  public function IsError()
215  {
216  // phpcs:enable
217  if (count($this->error) > 0) {
218  return true;
219  } else {
220  return false;
221  }
222  }
223 
230  public function splitResponse($response)
231  {
232  $r = array();
233  $response = explode("\n", $response);
234  foreach ($response as $line) {
235  $line = trim($line);
236  if ($line != "") {
237  list($key, $value) = explode(":", $line, 2);
238  $r[trim($key)] = trim($value);
239  }
240  }
241  return $r;
242  }
243 
244  // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
251  public function OpenID_Standarize($openid_identity = null)
252  {
253  // phpcs:enable
254  if ($openid_identity === null) {
255  $openid_identity = $this->openid_url_identity;
256  }
257 
258  $u = parse_url(strtolower(trim($openid_identity)));
259 
260  if (!isset($u['path']) || ($u['path'] == '/')) {
261  $u['path'] = '';
262  }
263  if (substr($u['path'], -1, 1) == '/') {
264  $u['path'] = substr($u['path'], 0, strlen($u['path']) - 1);
265  }
266  if (isset($u['query'])) { // If there is a query string, then use identity as is
267  return $u['host'].$u['path'].'?'.$u['query'];
268  } else {
269  return $u['host'].$u['path'];
270  }
271  }
272 
279  public function array2url($arr)
280  {
281  // converts associated array to URL Query String
282  if (!is_array($arr)) {
283  return false;
284  }
285  $query = '';
286  foreach ($arr as $key => $value) {
287  $query .= $key."=".$value."&";
288  }
289  return $query;
290  }
291 
292  // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
301  public function FSOCK_Request($url, $method = "GET", $params = "")
302  {
303  // phpcs:enable
304  $fp = fsockopen("ssl://www.myopenid.com", 443, $errno, $errstr, 3); // Connection timeout is 3 seconds
305  if (!$fp) {
306  $this->ErrorStore('OPENID_SOCKETERROR', $errstr);
307  return false;
308  } else {
309  $request = $method." /server HTTP/1.0\r\n";
310  $request .= "User-Agent: Dolibarr\r\n";
311  $request .= "Connection: close\r\n\r\n";
312  fwrite($fp, $request);
313  stream_set_timeout($fp, 4); // Connection response timeout is 4 seconds
314  $res = fread($fp, 2000);
315  $info = stream_get_meta_data($fp);
316  fclose($fp);
317 
318  if ($info['timed_out']) {
319  $this->ErrorStore('OPENID_SOCKETTIMEOUT');
320  } else {
321  return $res;
322  }
323  }
324  }
325 
326  // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
333  public function HTML2OpenIDServer($content)
334  {
335  // phpcs:enable
336  $get = array();
337 
338  $matches1 = array(); $matches2 = array();
339 
340  // Get details of their OpenID server and (optional) delegate
341  preg_match_all('/<link[^>]*rel=[\'"]openid.server[\'"][^>]*href=[\'"]([^\'"]+)[\'"][^>]*\/?>/i', $content, $matches1);
342  preg_match_all('/<link[^>]*href=\'"([^\'"]+)[\'"][^>]*rel=[\'"]openid.server[\'"][^>]*\/?>/i', $content, $matches2);
343  $servers = array_merge($matches1[1], $matches2[1]);
344 
345  preg_match_all('/<link[^>]*rel=[\'"]openid.delegate[\'"][^>]*href=[\'"]([^\'"]+)[\'"][^>]*\/?>/i', $content, $matches1);
346 
347  preg_match_all('/<link[^>]*href=[\'"]([^\'"]+)[\'"][^>]*rel=[\'"]openid.delegate[\'"][^>]*\/?>/i', $content, $matches2);
348 
349  $delegates = array_merge($matches1[1], $matches2[1]);
350 
351  $ret = array($servers, $delegates);
352  return $ret;
353  }
354 
355 
356  // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
363  public function GetOpenIDServer($url = '')
364  {
365  // phpcs:enable
366  global $conf;
367 
368  include_once DOL_DOCUMENT_ROOT.'/core/lib/geturl.lib.php';
369  if (empty($url)) {
370  $url = $conf->global->MAIN_AUTHENTICATION_OPENID_URL;
371  }
372 
373  $response = getURLContent($url, 'GET', '', 1, array(), array('http', 'https'));
374 
375  list($servers, $delegates) = $this->HTML2OpenIDServer($response);
376  if (count($servers) == 0) {
377  $this->ErrorStore('OPENID_NOSERVERSFOUND');
378  return false;
379  }
380  if (isset($delegates[0])
381  && ($delegates[0] != "")) {
382  $this->SetIdentity($delegates[0]);
383  }
384  $this->SetOpenIDServer($servers[0]);
385  return $servers[0];
386  }
387 
388  // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
394  public function GetRedirectURL()
395  {
396  // phpcs:enable
397  $params = array();
398  $params['openid.return_to'] = urlencode($this->URLs['approved']);
399  $params['openid.mode'] = 'checkid_setup';
400  $params['openid.identity'] = urlencode($this->openid_url_identity);
401  $params['openid.trust_root'] = urlencode($this->URLs['trust_root']);
402 
403  if (isset($this->fields['required'])
404  && (count($this->fields['required']) > 0)) {
405  $params['openid.sreg.required'] = implode(',', $this->fields['required']);
406  }
407  if (isset($this->fields['optional'])
408  && (count($this->fields['optional']) > 0)) {
409  $params['openid.sreg.optional'] = implode(',', $this->fields['optional']);
410  }
411  return $this->URLs['openid_server']."?".$this->array2url($params);
412  }
413 
414  // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
420  public function Redirect()
421  {
422  // phpcs:enable
423  $redirect_to = $this->GetRedirectURL();
424  if (headers_sent()) { // Use JavaScript to redirect if content has been previously sent (not recommended, but safe)
425  echo '<script nonce="'.getNonce().'" type="text/javascript">window.location=\'';
426  echo $redirect_to;
427  echo '\';</script>';
428  } else { // Default Header Redirect
429  header('Location: '.$redirect_to);
430  }
431  }
432 
433  // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
439  public function ValidateWithServer()
440  {
441  // phpcs:enable
442  $params = array(
443  'openid.assoc_handle' => urlencode($_GET['openid_assoc_handle']),
444  'openid.signed' => urlencode($_GET['openid_signed']),
445  'openid.sig' => urlencode($_GET['openid_sig'])
446  );
447  // Send only required parameters to confirm validity
448  $arr_signed = explode(",", str_replace('sreg.', 'sreg_', $_GET['openid_signed']));
449  $num = count($arr_signed);
450  for ($i = 0; $i < $num; $i++) {
451  $s = str_replace('sreg_', 'sreg.', $arr_signed[$i]);
452  $c = $_GET['openid_'.$arr_signed[$i]];
453  // if ($c != ""){
454  $params['openid.'.$s] = urlencode($c);
455  // }
456  }
457  $params['openid.mode'] = "check_authentication";
458 
459  $openid_server = $this->GetOpenIDServer();
460  if ($openid_server == false) {
461  return false;
462  }
463 
464  if (is_array($params)) {
465  $params = $this->array2url($params);
466  }
467 
468  $result = getURLContent($openid_server, 'POST', $params);
469 
470  $response = $result['content'];
471 
472  $data = $this->splitResponse($response);
473  if ($data['is_valid'] == "true") {
474  return true;
475  } else {
476  return false;
477  }
478  }
479 
480 
481 
482 
489  public function sendDiscoveryRequestToGetXRDS($url = '')
490  {
491  global $conf;
492 
493  include_once DOL_DOCUMENT_ROOT.'/core/lib/geturl.lib.php';
494  if (empty($url)) {
495  $url = $conf->global->MAIN_AUTHENTICATION_OPENID_URL;
496  }
497 
498  dol_syslog(get_class($this).'::sendDiscoveryRequestToGetXRDS get XRDS');
499 
500  $addheaders = array('Accept: application/xrds+xml');
501  $response = getURLContent($url, 'GET', '', 1, $addheaders, array('http', 'https'), 0);
502  /* response should like this:
503  <?xml version="1.0" encoding="UTF-8"?>
504  <xrds:XRDS xmlns:xrds="xri://$xrds" xmlns="xri://$xrd*($v*2.0)">
505  <XRD>
506  <Service priority="0">
507  <Type>http://specs.openid.net/auth/2.0/server</Type>
508  <Type>http://openid.net/srv/ax/1.0</Type>
509  ...
510  <URI>https://www.google.com/accounts/o8/ud</URI>
511  </Service>
512  </XRD>
513  </xrds:XRDS>
514  */
515  $content = $response['content'];
516 
517  $server = '';
518  if (preg_match('/'.preg_quote('<URI>', '/').'(.*)'.preg_quote('</URI>', '/').'/is', $content, $reg)) {
519  $server = $reg[1];
520  }
521 
522  if (empty($server)) {
523  $this->ErrorStore('OPENID_NOSERVERSFOUND');
524  return false;
525  } else {
526  dol_syslog(get_class($this).'::sendDiscoveryRequestToGetXRDS found endpoint = '.$server);
527  $this->SetOpenIDServer($server);
528  return $server;
529  }
530  }
531 }
Class to manage OpenID.
GetRedirectURL()
GetRedirectURL.
Redirect()
Redirect.
GetOpenIDServer($url='')
Get openid server.
SetApprovedURL($a)
SetApprovedURL.
__construct()
Constructor.
OpenID_Standarize($openid_identity=null)
OpenID_Standarize.
SetOptionalFields($a)
SetOptionalFields.
HTML2OpenIDServer($content)
HTML2OpenIDServer.
splitResponse($response)
splitResponse
SetCancelURL($a)
SetOpenIDServer.
GetError()
SetOpenIDServer.
SetTrustRoot($a)
SetOpenIDServer.
array2url($arr)
array2url
IsError()
IsError.
SetIdentity($a)
SetIdentity.
FSOCK_Request($url, $method="GET", $params="")
FSOCK_Request.
ErrorStore($code, $desc=null)
ErrorStore.
SetRequiredFields($a)
SetRequiredFields.
SetOpenIDServer($a)
SetOpenIDServer.
GetIdentity()
GetIdentity.
sendDiscoveryRequestToGetXRDS($url='')
Get XRDS response and set possible servers.
getURLContent($url, $postorget='GET', $param='', $followlocation=1, $addheaders=array(), $allowedschemes=array('http', 'https'), $localurl=0, $ssl_verifypeer=-1)
Function to get a content from an URL (use proxy if proxy defined).
Definition: geturl.lib.php:41