dolibarr 21.0.0-alpha
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 = array();
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.ScopeNotCamelCaps
215 public function IsError()
216 {
217 // phpcs:enable
218 if (count($this->error) > 0) {
219 return true;
220 } else {
221 return false;
222 }
223 }
224
231 public function splitResponse($response)
232 {
233 $r = array();
234 $response = explode("\n", $response);
235 foreach ($response as $line) {
236 $line = trim($line);
237 if ($line != "") {
238 list($key, $value) = explode(":", $line, 2);
239 $r[trim($key)] = trim($value);
240 }
241 }
242 return $r;
243 }
244
245 // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
252 public function OpenID_Standarize($openid_identity = null)
253 {
254 // phpcs:enable
255 if ($openid_identity === null) {
256 $openid_identity = $this->openid_url_identity;
257 }
258
259 $u = parse_url(strtolower(trim($openid_identity)));
260
261 if (!isset($u['path']) || ($u['path'] == '/')) {
262 $u['path'] = '';
263 }
264 if (substr($u['path'], -1, 1) == '/') {
265 $u['path'] = substr($u['path'], 0, strlen($u['path']) - 1);
266 }
267 if (isset($u['query'])) { // If there is a query string, then use identity as is
268 return $u['host'].$u['path'].'?'.$u['query'];
269 } else {
270 return $u['host'].$u['path'];
271 }
272 }
273
280 public function array2url($arr)
281 {
282 // converts associated array to URL Query String
283 if (!is_array($arr)) {
284 return false;
285 }
286 $query = '';
287 foreach ($arr as $key => $value) {
288 $query .= $key."=".$value."&";
289 }
290 return $query;
291 }
292
293 // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
302 public function FSOCK_Request($url, $method = "GET", $params = "")
303 {
304 // phpcs:enable
305 $fp = fsockopen("ssl://www.myopenid.com", 443, $errno, $errstr, 3); // Connection timeout is 3 seconds
306 if (!$fp) {
307 $this->ErrorStore('OPENID_SOCKETERROR', $errstr);
308 return false;
309 } else {
310 $request = $method." /server HTTP/1.0\r\n";
311 $request .= "User-Agent: Dolibarr\r\n";
312 $request .= "Connection: close\r\n\r\n";
313 fwrite($fp, $request);
314 stream_set_timeout($fp, 4); // Connection response timeout is 4 seconds
315 $res = fread($fp, 2000);
316 $info = stream_get_meta_data($fp);
317 fclose($fp);
318
319 if ($info['timed_out']) {
320 $this->ErrorStore('OPENID_SOCKETTIMEOUT');
321 } else {
322 return $res;
323 }
324 }
325 }
326
327 // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
334 public function HTML2OpenIDServer($content)
335 {
336 // phpcs:enable
337 $get = array();
338
339 $matches1 = array();
340 $matches2 = array();
341
342 // Get details of their OpenID server and (optional) delegate
343 preg_match_all('/<link[^>]*rel=[\'"]openid.server[\'"][^>]*href=[\'"]([^\'"]+)[\'"][^>]*\/?>/i', $content, $matches1);
344 preg_match_all('/<link[^>]*href=\'"([^\'"]+)[\'"][^>]*rel=[\'"]openid.server[\'"][^>]*\/?>/i', $content, $matches2);
345 $servers = array_merge($matches1[1], $matches2[1]);
346
347 preg_match_all('/<link[^>]*rel=[\'"]openid.delegate[\'"][^>]*href=[\'"]([^\'"]+)[\'"][^>]*\/?>/i', $content, $matches1);
348
349 preg_match_all('/<link[^>]*href=[\'"]([^\'"]+)[\'"][^>]*rel=[\'"]openid.delegate[\'"][^>]*\/?>/i', $content, $matches2);
350
351 $delegates = array_merge($matches1[1], $matches2[1]);
352
353 $ret = array($servers, $delegates);
354 return $ret;
355 }
356
357
358 // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
365 public function GetOpenIDServer($url = '')
366 {
367 // phpcs:enable
368 global $conf;
369
370 include_once DOL_DOCUMENT_ROOT.'/core/lib/geturl.lib.php';
371 if (empty($url)) {
372 $url = getDolGlobalString('MAIN_AUTHENTICATION_OPENID_URL');
373 }
374
375 $response = getURLContent($url, 'GET', '', 1, array(), array('http', 'https'));
376
377 list($servers, $delegates) = $this->HTML2OpenIDServer($response);
378 if (count($servers) == 0) {
379 $this->ErrorStore('OPENID_NOSERVERSFOUND');
380 return false;
381 }
382 if (isset($delegates[0])
383 && ($delegates[0] != "")) {
384 $this->SetIdentity($delegates[0]);
385 }
386 $this->SetOpenIDServer($servers[0]);
387 return $servers[0];
388 }
389
390 // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
396 public function GetRedirectURL()
397 {
398 // phpcs:enable
399 $params = array();
400 $params['openid.return_to'] = urlencode($this->URLs['approved']);
401 $params['openid.mode'] = 'checkid_setup';
402 $params['openid.identity'] = urlencode($this->openid_url_identity);
403 $params['openid.trust_root'] = urlencode($this->URLs['trust_root']);
404
405 if (isset($this->fields['required'])
406 && (count($this->fields['required']) > 0)) {
407 $params['openid.sreg.required'] = implode(',', $this->fields['required']);
408 }
409 if (isset($this->fields['optional'])
410 && (count($this->fields['optional']) > 0)) {
411 $params['openid.sreg.optional'] = implode(',', $this->fields['optional']);
412 }
413 return $this->URLs['openid_server']."?".$this->array2url($params);
414 }
415
416 // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
422 public function Redirect()
423 {
424 // phpcs:enable
425 $redirect_to = $this->GetRedirectURL();
426 if (headers_sent()) { // Use JavaScript to redirect if content has been previously sent (not recommended, but safe)
427 echo '<script nonce="'.getNonce().'" type="text/javascript">window.location=\'';
428 echo $redirect_to;
429 echo '\';</script>';
430 } else { // Default Header Redirect
431 header('Location: '.$redirect_to);
432 }
433 }
434
440 public function validateWithServer()
441 {
442 $params = array(
443 'openid.assoc_handle' => urlencode(GETPOST('openid_assoc_handle')),
444 'openid.signed' => urlencode(GETPOST('openid_signed')),
445 'openid.sig' => urlencode(GETPOST('openid_sig'))
446 );
447 // Send only required parameters to confirm validity
448 $arr_signed = explode(",", str_replace('sreg.', 'sreg_', GETPOST('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 = GETPOST('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 = getDolGlobalString('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.
getDolGlobalString($key, $default='')
Return dolibarr global constant string value.
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).