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 * Copyright (C) 2024 MDW <mdeweerd@users.noreply.github.com>
4 *
5 * This program is free software; you can redistribute it and/or modify
6 * it under the terms of the GNU General Public License as published by
7 * the Free Software Foundation; either version 3 of the License, or
8 * (at your option) any later version.
9 *
10 * This program is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 * GNU General Public License for more details.
14 *
15 * You should have received a copy of the GNU General Public License
16 * along with this program. If not, see <https://www.gnu.org/licenses/>.
17 */
18
29{
33 public $openid_url_identity;
37 public $URLs = array();
41 public $error = array();
45 public $fields = array(
46 'required' => array(),
47 'optional' => array(),
48 );
49
53 public function __construct()
54 {
55 if (!function_exists('curl_exec')) {
56 die('Error: Class SimpleOpenID requires curl extension to work');
57 }
58 }
59
60 // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
67 public function SetOpenIDServer($a)
68 {
69 // phpcs:enable
70 $this->URLs['openid_server'] = $a;
71 }
72
73 // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
80 public function SetTrustRoot($a)
81 {
82 // phpcs:enable
83 $this->URLs['trust_root'] = $a;
84 }
85
86 // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
93 public function SetCancelURL($a)
94 {
95 // phpcs:enable
96 $this->URLs['cancel'] = $a;
97 }
98
99 // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
106 public function SetApprovedURL($a)
107 {
108 // phpcs:enable
109 $this->URLs['approved'] = $a;
110 }
111
112 // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
119 public function SetRequiredFields($a)
120 {
121 // phpcs:enable
122 if (is_array($a)) {
123 $this->fields['required'] = $a;
124 } else {
125 $this->fields['required'][] = $a;
126 }
127 }
128
129 // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
136 public function SetOptionalFields($a)
137 {
138 // phpcs:enable
139 if (is_array($a)) {
140 $this->fields['optional'] = $a;
141 } else {
142 $this->fields['optional'][] = $a;
143 }
144 }
145
146 // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
153 public function SetIdentity($a)
154 {
155 // phpcs:enable
156 // Set Identity URL
157 if ((stripos($a, 'http://') === false)
158 && (stripos($a, 'https://') === false)) {
159 $a = 'http://'.$a;
160 }
161 /*
162 $u = parse_url(trim($a));
163 if (!isset($u['path'])){
164 $u['path'] = '/';
165 }else if(substr($u['path'],-1,1) == '/'){
166 $u['path'] = substr($u['path'], 0, strlen($u['path'])-1);
167 }
168 if (isset($u['query'])){ // If there is a query string, then use identity as is
169 $identity = $a;
170 }else{
171 $identity = $u['scheme'] . '://' . $u['host'] . $u['path'];
172 }
173 */
174 $this->openid_url_identity = $a;
175 }
176
177 // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
183 public function GetIdentity()
184 {
185 // phpcs:enable
186 // Get Identity
187 return $this->openid_url_identity;
188 }
189
190 // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
196 public function GetError()
197 {
198 // phpcs:enable
199 $e = $this->error;
200 return array('code' => $e[0], 'description' => $e[1]);
201 }
202
203 // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
211 public function ErrorStore($code, $desc = null)
212 {
213 // phpcs:enable
214 $errs = array();
215 $errs['OPENID_NOSERVERSFOUND'] = 'Cannot find OpenID Server TAG on Identity page.';
216 if ($desc == null) {
217 $desc = $errs[$code];
218 }
219 $this->error = array($code, $desc);
220 }
221
222 // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
228 public function IsError()
229 {
230 // phpcs:enable
231 if (count($this->error) > 0) {
232 return true;
233 } else {
234 return false;
235 }
236 }
237
244 public function splitResponse($response)
245 {
246 $r = array();
247 $response = explode("\n", $response);
248 foreach ($response as $line) {
249 $line = trim($line);
250 if ($line != "") {
251 list($key, $value) = explode(":", $line, 2);
252 $r[trim($key)] = trim($value);
253 }
254 }
255 return $r;
256 }
257
258 // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
265 public function OpenID_Standarize($openid_identity = null)
266 {
267 // phpcs:enable
268 if ($openid_identity === null) {
269 $openid_identity = $this->openid_url_identity;
270 }
271
272 $u = parse_url(strtolower(trim($openid_identity)));
273
274 if (!isset($u['path']) || ($u['path'] == '/')) {
275 $u['path'] = '';
276 }
277 if (substr($u['path'], -1, 1) == '/') {
278 $u['path'] = substr($u['path'], 0, strlen($u['path']) - 1);
279 }
280 if (isset($u['query'])) { // If there is a query string, then use identity as is
281 return $u['host'].$u['path'].'?'.$u['query'];
282 } else {
283 return $u['host'].$u['path'];
284 }
285 }
286
293 public function array2url($arr)
294 {
295 // converts associated array to URL Query String
296 if (!is_array($arr)) {
297 return false;
298 }
299 $query = '';
300 foreach ($arr as $key => $value) {
301 $query .= $key."=".$value."&";
302 }
303 return $query;
304 }
305
306 // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
315 public function FSOCK_Request($url, $method = "GET", $params = "")
316 {
317 // phpcs:enable
318 $fp = fsockopen("ssl://www.myopenid.com", 443, $errno, $errstr, 3); // Connection timeout is 3 seconds
319 if (!$fp) {
320 $this->ErrorStore('OPENID_SOCKETERROR', $errstr);
321 return false;
322 } else {
323 $request = $method." /server HTTP/1.0\r\n";
324 $request .= "User-Agent: Dolibarr\r\n";
325 $request .= "Connection: close\r\n\r\n";
326 fwrite($fp, $request);
327 stream_set_timeout($fp, 4); // Connection response timeout is 4 seconds
328 $res = fread($fp, 2000);
329 $info = stream_get_meta_data($fp);
330 fclose($fp);
331
332 if ($info['timed_out']) {
333 $this->ErrorStore('OPENID_SOCKETTIMEOUT');
334 } else {
335 return $res;
336 }
337 }
338 }
339
340 // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
347 public function HTML2OpenIDServer($content)
348 {
349 // phpcs:enable
350 $get = array();
351
352 $matches1 = array();
353 $matches2 = array();
354
355 // Get details of their OpenID server and (optional) delegate
356 preg_match_all('/<link[^>]*rel=[\'"]openid.server[\'"][^>]*href=[\'"]([^\'"]+)[\'"][^>]*\/?>/i', $content, $matches1);
357 preg_match_all('/<link[^>]*href=\'"([^\'"]+)[\'"][^>]*rel=[\'"]openid.server[\'"][^>]*\/?>/i', $content, $matches2);
358 $servers = array_merge($matches1[1], $matches2[1]);
359
360 preg_match_all('/<link[^>]*rel=[\'"]openid.delegate[\'"][^>]*href=[\'"]([^\'"]+)[\'"][^>]*\/?>/i', $content, $matches1);
361
362 preg_match_all('/<link[^>]*href=[\'"]([^\'"]+)[\'"][^>]*rel=[\'"]openid.delegate[\'"][^>]*\/?>/i', $content, $matches2);
363
364 $delegates = array_merge($matches1[1], $matches2[1]);
365
366 $ret = array($servers, $delegates);
367 return $ret;
368 }
369
370
371 // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
378 public function GetOpenIDServer($url = '')
379 {
380 // phpcs:enable
381 global $conf;
382
383 include_once DOL_DOCUMENT_ROOT.'/core/lib/geturl.lib.php';
384 if (empty($url)) {
385 $url = getDolGlobalString('MAIN_AUTHENTICATION_OPENID_URL');
386 }
387
388 $response = getURLContent($url, 'GET', '', 1, array(), array('http', 'https'));
389
390 list($servers, $delegates) = $this->HTML2OpenIDServer($response);
391 if (count($servers) == 0) {
392 $this->ErrorStore('OPENID_NOSERVERSFOUND');
393 return false;
394 }
395 if (isset($delegates[0])
396 && ($delegates[0] != "")) {
397 $this->SetIdentity($delegates[0]);
398 }
399 $this->SetOpenIDServer($servers[0]);
400 return $servers[0];
401 }
402
403 // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
409 public function GetRedirectURL()
410 {
411 // phpcs:enable
412 $params = array();
413 $params['openid.return_to'] = urlencode($this->URLs['approved']);
414 $params['openid.mode'] = 'checkid_setup';
415 $params['openid.identity'] = urlencode($this->openid_url_identity);
416 $params['openid.trust_root'] = urlencode($this->URLs['trust_root']);
417
418 if (isset($this->fields['required'])
419 && (count($this->fields['required']) > 0)) {
420 $params['openid.sreg.required'] = implode(',', $this->fields['required']);
421 }
422 if (isset($this->fields['optional'])
423 && (count($this->fields['optional']) > 0)) {
424 $params['openid.sreg.optional'] = implode(',', $this->fields['optional']);
425 }
426 return $this->URLs['openid_server']."?".$this->array2url($params);
427 }
428
429 // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
435 public function Redirect()
436 {
437 // phpcs:enable
438 $redirect_to = $this->GetRedirectURL();
439 if (headers_sent()) { // Use JavaScript to redirect if content has been previously sent (not recommended, but safe)
440 echo '<script nonce="'.getNonce().'" type="text/javascript">window.location=\'';
441 echo $redirect_to;
442 echo '\';</script>';
443 } else { // Default Header Redirect
444 header('Location: '.$redirect_to);
445 }
446 }
447
453 public function validateWithServer()
454 {
455 $params = array(
456 'openid.assoc_handle' => urlencode(GETPOST('openid_assoc_handle')),
457 'openid.signed' => urlencode(GETPOST('openid_signed')),
458 'openid.sig' => urlencode(GETPOST('openid_sig'))
459 );
460 // Send only required parameters to confirm validity
461 $arr_signed = explode(",", str_replace('sreg.', 'sreg_', GETPOST('openid_signed')));
462 $num = count($arr_signed);
463 for ($i = 0; $i < $num; $i++) {
464 $s = str_replace('sreg_', 'sreg.', $arr_signed[$i]);
465 $c = GETPOST('openid_'.$arr_signed[$i]);
466 // if ($c != ""){
467 $params['openid.'.$s] = urlencode($c);
468 // }
469 }
470 $params['openid.mode'] = "check_authentication";
471
472 $openid_server = $this->GetOpenIDServer();
473 if ($openid_server == false) {
474 return false;
475 }
476
477 if (is_array($params)) {
478 $params = $this->array2url($params);
479 }
480
481 $result = getURLContent($openid_server, 'POST', $params);
482
483 $response = $result['content'];
484
485 $data = $this->splitResponse($response);
486 if ($data['is_valid'] == "true") {
487 return true;
488 } else {
489 return false;
490 }
491 }
492
493
494
495
502 public function sendDiscoveryRequestToGetXRDS($url = '')
503 {
504 global $conf;
505
506 include_once DOL_DOCUMENT_ROOT.'/core/lib/geturl.lib.php';
507 if (empty($url)) {
508 $url = getDolGlobalString('MAIN_AUTHENTICATION_OPENID_URL');
509 }
510
511 dol_syslog(get_class($this).'::sendDiscoveryRequestToGetXRDS get XRDS');
512
513 $addheaders = array('Accept: application/xrds+xml');
514 $response = getURLContent($url, 'GET', '', 1, $addheaders, array('http', 'https'), 0);
515 /* response should like this:
516 <?xml version="1.0" encoding="UTF-8"?>
517 <xrds:XRDS xmlns:xrds="xri://$xrds" xmlns="xri://$xrd*($v*2.0)">
518 <XRD>
519 <Service priority="0">
520 <Type>http://specs.openid.net/auth/2.0/server</Type>
521 <Type>http://openid.net/srv/ax/1.0</Type>
522 ...
523 <URI>https://www.google.com/accounts/o8/ud</URI>
524 </Service>
525 </XRD>
526 </xrds:XRDS>
527 */
528 $content = $response['content'];
529
530 $server = '';
531 if (preg_match('/'.preg_quote('<URI>', '/').'(.*)'.preg_quote('</URI>', '/').'/is', $content, $reg)) {
532 $server = $reg[1];
533 }
534
535 if (empty($server)) {
536 $this->ErrorStore('OPENID_NOSERVERSFOUND');
537 return false;
538 } else {
539 dol_syslog(get_class($this).'::sendDiscoveryRequestToGetXRDS found endpoint = '.$server);
540 $this->SetOpenIDServer($server);
541 return $server;
542 }
543 }
544}
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 a 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).