Advertisement
Guest User

Untitled

a guest
Mar 29th, 2017
76
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
text 32.15 KB | None | 0 0
  1. <?php
  2. class LightOpenID
  3. {
  4. public $returnUrl
  5. , $required = array()
  6. , $optional = array()
  7. , $verify_peer = null
  8. , $capath = null
  9. , $cainfo = null
  10. , $data;
  11. private $identity, $claimed_id;
  12. protected $server, $version, $trustRoot, $aliases, $identifier_select = false
  13. , $ax = false, $sreg = false, $setup_url = null, $headers = array();
  14. static protected $ax_to_sreg = array(
  15. 'namePerson/friendly' => 'nickname',
  16. 'contact/email' => 'email',
  17. 'namePerson' => 'fullname',
  18. 'birthDate' => 'dob',
  19. 'person/gender' => 'gender',
  20. 'contact/postalCode/home' => 'postcode',
  21. 'contact/country/home' => 'country',
  22. 'pref/language' => 'language',
  23. 'pref/timezone' => 'timezone',
  24. );
  25.  
  26. function __construct($host)
  27. {
  28. $this->trustRoot = (strpos($host, '://') ? $host : 'http://csgospeedpot.pl' . $host);
  29. if ((!empty($_SERVER['HTTPS']) && $_SERVER['HTTPS'] != 'off')
  30. || (isset($_SERVER['HTTP_X_FORWARDED_PROTO'])
  31. && $_SERVER['HTTP_X_FORWARDED_PROTO'] == 'https')
  32. ) {
  33. $this->trustRoot = (strpos($host, '://') ? $host : 'https://' . $host);
  34. }
  35.  
  36. if(($host_end = strpos($this->trustRoot, '/', 8)) !== false) {
  37. $this->trustRoot = substr($this->trustRoot, 0, $host_end);
  38. }
  39.  
  40. $uri = rtrim(preg_replace('#((?<=\?)|&)openid\.[^&]+#', '', $_SERVER['REQUEST_URI']), '?');
  41. $this->returnUrl = $this->trustRoot . $uri;
  42.  
  43. $this->data = ($_SERVER['REQUEST_METHOD'] === 'POST') ? $_POST : $_GET;
  44.  
  45. if(!function_exists('curl_init') && !in_array('https', stream_get_wrappers())) {
  46. throw new ErrorException('You must have either https wrappers or curl enabled.');
  47. }
  48. }
  49.  
  50. function __set($name, $value)
  51. {
  52. switch ($name) {
  53. case 'identity':
  54. if (strlen($value = trim((String) $value))) {
  55. if (preg_match('#^xri:/*#i', $value, $m)) {
  56. $value = substr($value, strlen($m[0]));
  57. } elseif (!preg_match('/^(?:[=@+\$!\(]|https?:)/i', $value)) {
  58. $value = "http://$value";
  59. }
  60. if (preg_match('#^https?://[^/]+$#i', $value, $m)) {
  61. $value .= '/';
  62. }
  63. }
  64. $this->$name = $this->claimed_id = $value;
  65. break;
  66. case 'trustRoot':
  67. case 'realm':
  68. $this->trustRoot = trim($value);
  69. }
  70. }
  71.  
  72. function __get($name)
  73. {
  74. switch ($name) {
  75. case 'identity':
  76. # We return claimed_id instead of identity,
  77. # because the developer should see the claimed identifier,
  78. # i.e. what he set as identity, not the op-local identifier (which is what we verify)
  79. return $this->claimed_id;
  80. case 'trustRoot':
  81. case 'realm':
  82. return $this->trustRoot;
  83. case 'mode':
  84. return empty($this->data['openid_mode']) ? null : $this->data['openid_mode'];
  85. }
  86. }
  87.  
  88. /**
  89. * Checks if the server specified in the url exists.
  90. *
  91. * @param $url url to check
  92. * @return true, if the server exists; false otherwise
  93. */
  94. function hostExists($url)
  95. {
  96. if (strpos($url, '/') === false) {
  97. $server = $url;
  98. } else {
  99. $server = @parse_url($url, PHP_URL_HOST);
  100. }
  101.  
  102. if (!$server) {
  103. return false;
  104. }
  105.  
  106. return !!gethostbynamel($server);
  107. }
  108.  
  109. protected function request_curl($url, $method='GET', $params=array(), $update_claimed_id)
  110. {
  111. $params = http_build_query($params, '', '&');
  112. $curl = curl_init($url . ($method == 'GET' && $params ? '?' . $params : ''));
  113. curl_setopt($curl, CURLOPT_FOLLOWLOCATION, true);
  114. curl_setopt($curl, CURLOPT_HEADER, false);
  115. curl_setopt($curl, CURLOPT_SSL_VERIFYPEER, false);
  116. curl_setopt($curl, CURLOPT_RETURNTRANSFER, true);
  117. curl_setopt($curl, CURLOPT_HTTPHEADER, array('Accept: application/xrds+xml, */*'));
  118.  
  119. if($this->verify_peer !== null) {
  120. curl_setopt($curl, CURLOPT_SSL_VERIFYPEER, $this->verify_peer);
  121. if($this->capath) {
  122. curl_setopt($curl, CURLOPT_CAPATH, $this->capath);
  123. }
  124.  
  125. if($this->cainfo) {
  126. curl_setopt($curl, CURLOPT_CAINFO, $this->cainfo);
  127. }
  128. }
  129.  
  130. if ($method == 'POST') {
  131. curl_setopt($curl, CURLOPT_POST, true);
  132. curl_setopt($curl, CURLOPT_POSTFIELDS, $params);
  133. } elseif ($method == 'HEAD') {
  134. curl_setopt($curl, CURLOPT_HEADER, true);
  135. curl_setopt($curl, CURLOPT_NOBODY, true);
  136. } else {
  137. curl_setopt($curl, CURLOPT_HEADER, true);
  138. curl_setopt($curl, CURLOPT_HTTPGET, true);
  139. }
  140. $response = curl_exec($curl);
  141.  
  142. if($method == 'HEAD' && curl_getinfo($curl, CURLINFO_HTTP_CODE) == 405) {
  143. curl_setopt($curl, CURLOPT_HTTPGET, true);
  144. $response = curl_exec($curl);
  145. $response = substr($response, 0, strpos($response, "\r\n\r\n"));
  146. }
  147.  
  148. if($method == 'HEAD' || $method == 'GET') {
  149. $header_response = $response;
  150.  
  151. # If it's a GET request, we want to only parse the header part.
  152. if($method == 'GET') {
  153. $header_response = substr($response, 0, strpos($response, "\r\n\r\n"));
  154. }
  155.  
  156. $headers = array();
  157. foreach(explode("\n", $header_response) as $header) {
  158. $pos = strpos($header,':');
  159. if ($pos !== false) {
  160. $name = strtolower(trim(substr($header, 0, $pos)));
  161. $headers[$name] = trim(substr($header, $pos+1));
  162. }
  163. }
  164.  
  165. if($update_claimed_id) {
  166. # Updating claimed_id in case of redirections.
  167. $effective_url = curl_getinfo($curl, CURLINFO_EFFECTIVE_URL);
  168. if($effective_url != $url) {
  169. $this->identity = $this->claimed_id = $effective_url;
  170. }
  171. }
  172.  
  173. if($method == 'HEAD') {
  174. return $headers;
  175. } else {
  176. $this->headers = $headers;
  177. }
  178. }
  179.  
  180. if (curl_errno($curl)) {
  181. throw new ErrorException(curl_error($curl), curl_errno($curl));
  182. }
  183.  
  184. return $response;
  185. }
  186.  
  187. protected function parse_header_array($array, $update_claimed_id)
  188. {
  189. $headers = array();
  190. foreach($array as $header) {
  191. $pos = strpos($header,':');
  192. if ($pos !== false) {
  193. $name = strtolower(trim(substr($header, 0, $pos)));
  194. $headers[$name] = trim(substr($header, $pos+1));
  195.  
  196. # Following possible redirections. The point is just to have
  197. # claimed_id change with them, because the redirections
  198. # are followed automatically.
  199. # We ignore redirections with relative paths.
  200. # If any known provider uses them, file a bug report.
  201. if($name == 'location' && $update_claimed_id) {
  202. if(strpos($headers[$name], 'http') === 0) {
  203. $this->identity = $this->claimed_id = $headers[$name];
  204. } elseif($headers[$name][0] == '/') {
  205. $parsed_url = parse_url($this->claimed_id);
  206. $this->identity =
  207. $this->claimed_id = $parsed_url['scheme'] . '://'
  208. . $parsed_url['host']
  209. . $headers[$name];
  210. }
  211. }
  212. }
  213. }
  214. return $headers;
  215. }
  216.  
  217. protected function request_streams($url, $method='GET', $params=array(), $update_claimed_id)
  218. {
  219. if(!$this->hostExists($url)) {
  220. throw new ErrorException("Could not connect to $url.", 404);
  221. }
  222.  
  223. $params = http_build_query($params, '', '&');
  224. switch($method) {
  225. case 'GET':
  226. $opts = array(
  227. 'http' => array(
  228. 'method' => 'GET',
  229. 'header' => 'Accept: application/xrds+xml, */*',
  230. 'ignore_errors' => true,
  231. ), 'ssl' => array(
  232. 'CN_match' => parse_url($url, PHP_URL_HOST),
  233. ),
  234. );
  235. $url = $url . ($params ? '?' . $params : '');
  236. break;
  237. case 'POST':
  238. $opts = array(
  239. 'http' => array(
  240. 'method' => 'POST',
  241. 'header' => 'Content-type: application/x-www-form-urlencoded',
  242. 'content' => $params,
  243. 'ignore_errors' => true,
  244. ), 'ssl' => array(
  245. 'CN_match' => parse_url($url, PHP_URL_HOST),
  246. ),
  247. );
  248. break;
  249. case 'HEAD':
  250. # We want to send a HEAD request,
  251. # but since get_headers doesn't accept $context parameter,
  252. # we have to change the defaults.
  253. $default = stream_context_get_options(stream_context_get_default());
  254. stream_context_get_default(
  255. array(
  256. 'http' => array(
  257. 'method' => 'HEAD',
  258. 'header' => 'Accept: application/xrds+xml, */*',
  259. 'ignore_errors' => true,
  260. ), 'ssl' => array(
  261. 'CN_match' => parse_url($url, PHP_URL_HOST),
  262. ),
  263. )
  264. );
  265.  
  266. $url = $url . ($params ? '?' . $params : '');
  267. $headers = get_headers ($url);
  268. if(!$headers) {
  269. return array();
  270. }
  271.  
  272. if(intval(substr($headers[0], strlen('HTTP/1.1 '))) == 405) {
  273. # The server doesn't support HEAD, so let's emulate it with
  274. # a GET.
  275. $args = func_get_args();
  276. $args[1] = 'GET';
  277. call_user_func_array(array($this, 'request_streams'), $args);
  278. return $this->headers;
  279. }
  280.  
  281. $headers = $this->parse_header_array($headers, $update_claimed_id);
  282.  
  283. # And restore them.
  284. stream_context_get_default($default);
  285. return $headers;
  286. }
  287.  
  288. if($this->verify_peer) {
  289. $opts['ssl'] += array(
  290. 'verify_peer' => true,
  291. 'capath' => $this->capath,
  292. 'cafile' => $this->cainfo,
  293. );
  294. }
  295.  
  296. $context = stream_context_create ($opts);
  297. $data = file_get_contents($url, false, $context);
  298. # This is a hack for providers who don't support HEAD requests.
  299. # It just creates the headers array for the last request in $this->headers.
  300. if(isset($http_response_header)) {
  301. $this->headers = $this->parse_header_array($http_response_header, $update_claimed_id);
  302. }
  303.  
  304. return $data;
  305. }
  306.  
  307. protected function request($url, $method='GET', $params=array(), $update_claimed_id=false)
  308. {
  309. if (function_exists('curl_init')
  310. && (!in_array('https', stream_get_wrappers()) || !ini_get('safe_mode') && !ini_get('open_basedir'))
  311. ) {
  312. return $this->request_curl($url, $method, $params, $update_claimed_id);
  313. }
  314. return $this->request_streams($url, $method, $params, $update_claimed_id);
  315. }
  316.  
  317. protected function build_url($url, $parts)
  318. {
  319. if (isset($url['query'], $parts['query'])) {
  320. $parts['query'] = $url['query'] . '&' . $parts['query'];
  321. }
  322.  
  323. $url = $parts + $url;
  324. $url = $url['scheme'] . '://'
  325. . (empty($url['username'])?''
  326. :(empty($url['password'])? "{$url['username']}@"
  327. :"{$url['username']}:{$url['password']}@"))
  328. . $url['host']
  329. . (empty($url['port'])?'':":{$url['port']}")
  330. . (empty($url['path'])?'':$url['path'])
  331. . (empty($url['query'])?'':"?{$url['query']}")
  332. . (empty($url['fragment'])?'':"#{$url['fragment']}");
  333. return $url;
  334. }
  335.  
  336. /**
  337. * Helper function used to scan for <meta>/<link> tags and extract information
  338. * from them
  339. */
  340. protected function htmlTag($content, $tag, $attrName, $attrValue, $valueName)
  341. {
  342. preg_match_all("#<{$tag}[^>]*$attrName=['\"].*?$attrValue.*?['\"][^>]*$valueName=['\"](.+?)['\"][^>]*/?>#i", $content, $matches1);
  343. preg_match_all("#<{$tag}[^>]*$valueName=['\"](.+?)['\"][^>]*$attrName=['\"].*?$attrValue.*?['\"][^>]*/?>#i", $content, $matches2);
  344.  
  345. $result = array_merge($matches1[1], $matches2[1]);
  346. return empty($result)?false:$result[0];
  347. }
  348.  
  349. /**
  350. * Performs Yadis and HTML discovery. Normally not used.
  351. * @param $url Identity URL.
  352. * @return String OP Endpoint (i.e. OpenID provider address).
  353. * @throws ErrorException
  354. */
  355. function discover($url)
  356. {
  357. if (!$url) throw new ErrorException('No identity supplied.');
  358. # Use xri.net proxy to resolve i-name identities
  359. if (!preg_match('#^https?:#', $url)) {
  360. $url = "https://xri.net/$url";
  361. }
  362.  
  363. # We save the original url in case of Yadis discovery failure.
  364. # It can happen when we'll be lead to an XRDS document
  365. # which does not have any OpenID2 services.
  366. $originalUrl = $url;
  367.  
  368. # A flag to disable yadis discovery in case of failure in headers.
  369. $yadis = true;
  370.  
  371. # We'll jump a maximum of 5 times, to avoid endless redirections.
  372. for ($i = 0; $i < 5; $i ++) {
  373. if ($yadis) {
  374. $headers = $this->request($url, 'HEAD', array(), true);
  375.  
  376. $next = false;
  377. if (isset($headers['x-xrds-location'])) {
  378. $url = $this->build_url(parse_url($url), parse_url(trim($headers['x-xrds-location'])));
  379. $next = true;
  380. }
  381.  
  382. if (isset($headers['content-type'])
  383. && (strpos($headers['content-type'], 'application/xrds+xml') !== false
  384. || strpos($headers['content-type'], 'text/xml') !== false)
  385. ) {
  386. # Apparently, some providers return XRDS documents as text/html.
  387. # While it is against the spec, allowing this here shouldn't break
  388. # compatibility with anything.
  389. # ---
  390. # Found an XRDS document, now let's find the server, and optionally delegate.
  391. $content = $this->request($url, 'GET');
  392.  
  393. preg_match_all('#<Service.*?>(.*?)</Service>#s', $content, $m);
  394. foreach($m[1] as $content) {
  395. $content = ' ' . $content; # The space is added, so that strpos doesn't return 0.
  396.  
  397. # OpenID 2
  398. $ns = preg_quote('http://specs.openid.net/auth/2.0/', '#');
  399. if(preg_match('#<Type>\s*'.$ns.'(server|signon)\s*</Type>#s', $content, $type)) {
  400. if ($type[1] == 'server') $this->identifier_select = true;
  401.  
  402. preg_match('#<URI.*?>(.*)</URI>#', $content, $server);
  403. preg_match('#<(Local|Canonical)ID>(.*)</\1ID>#', $content, $delegate);
  404. if (empty($server)) {
  405. return false;
  406. }
  407. # Does the server advertise support for either AX or SREG?
  408. $this->ax = (bool) strpos($content, '<Type>http://openid.net/srv/ax/1.0</Type>');
  409. $this->sreg = strpos($content, '<Type>http://openid.net/sreg/1.0</Type>')
  410. || strpos($content, '<Type>http://openid.net/extensions/sreg/1.1</Type>');
  411.  
  412. $server = $server[1];
  413. if (isset($delegate[2])) $this->identity = trim($delegate[2]);
  414. $this->version = 2;
  415.  
  416. $this->server = $server;
  417. return $server;
  418. }
  419.  
  420. # OpenID 1.1
  421. $ns = preg_quote('http://openid.net/signon/1.1', '#');
  422. if (preg_match('#<Type>\s*'.$ns.'\s*</Type>#s', $content)) {
  423.  
  424. preg_match('#<URI.*?>(.*)</URI>#', $content, $server);
  425. preg_match('#<.*?Delegate>(.*)</.*?Delegate>#', $content, $delegate);
  426. if (empty($server)) {
  427. return false;
  428. }
  429. # AX can be used only with OpenID 2.0, so checking only SREG
  430. $this->sreg = strpos($content, '<Type>http://openid.net/sreg/1.0</Type>')
  431. || strpos($content, '<Type>http://openid.net/extensions/sreg/1.1</Type>');
  432.  
  433. $server = $server[1];
  434. if (isset($delegate[1])) $this->identity = $delegate[1];
  435. $this->version = 1;
  436.  
  437. $this->server = $server;
  438. return $server;
  439. }
  440. }
  441.  
  442. $next = true;
  443. $yadis = false;
  444. $url = $originalUrl;
  445. $content = null;
  446. break;
  447. }
  448. if ($next) continue;
  449.  
  450. # There are no relevant information in headers, so we search the body.
  451. $content = $this->request($url, 'GET', array(), true);
  452.  
  453. if (isset($this->headers['x-xrds-location'])) {
  454. $url = $this->build_url(parse_url($url), parse_url(trim($this->headers['x-xrds-location'])));
  455. continue;
  456. }
  457.  
  458. $location = $this->htmlTag($content, 'meta', 'http-equiv', 'X-XRDS-Location', 'content');
  459. if ($location) {
  460. $url = $this->build_url(parse_url($url), parse_url($location));
  461. continue;
  462. }
  463. }
  464.  
  465. if (!$content) $content = $this->request($url, 'GET');
  466.  
  467. # At this point, the YADIS Discovery has failed, so we'll switch
  468. # to openid2 HTML discovery, then fallback to openid 1.1 discovery.
  469. $server = $this->htmlTag($content, 'link', 'rel', 'openid2.provider', 'href');
  470. $delegate = $this->htmlTag($content, 'link', 'rel', 'openid2.local_id', 'href');
  471. $this->version = 2;
  472.  
  473. if (!$server) {
  474. # The same with openid 1.1
  475. $server = $this->htmlTag($content, 'link', 'rel', 'openid.server', 'href');
  476. $delegate = $this->htmlTag($content, 'link', 'rel', 'openid.delegate', 'href');
  477. $this->version = 1;
  478. }
  479.  
  480. if ($server) {
  481. # We found an OpenID2 OP Endpoint
  482. if ($delegate) {
  483. # We have also found an OP-Local ID.
  484. $this->identity = $delegate;
  485. }
  486. $this->server = $server;
  487. return $server;
  488. }
  489.  
  490. throw new ErrorException("No OpenID Server found at $url", 404);
  491. }
  492. throw new ErrorException('Endless redirection!', 500);
  493. }
  494.  
  495. protected function sregParams()
  496. {
  497. $params = array();
  498. # We always use SREG 1.1, even if the server is advertising only support for 1.0.
  499. # That's because it's fully backwards compatibile with 1.0, and some providers
  500. # advertise 1.0 even if they accept only 1.1. One such provider is myopenid.com
  501. $params['openid.ns.sreg'] = 'http://openid.net/extensions/sreg/1.1';
  502. if ($this->required) {
  503. $params['openid.sreg.required'] = array();
  504. foreach ($this->required as $required) {
  505. if (!isset(self::$ax_to_sreg[$required])) continue;
  506. $params['openid.sreg.required'][] = self::$ax_to_sreg[$required];
  507. }
  508. $params['openid.sreg.required'] = implode(',', $params['openid.sreg.required']);
  509. }
  510.  
  511. if ($this->optional) {
  512. $params['openid.sreg.optional'] = array();
  513. foreach ($this->optional as $optional) {
  514. if (!isset(self::$ax_to_sreg[$optional])) continue;
  515. $params['openid.sreg.optional'][] = self::$ax_to_sreg[$optional];
  516. }
  517. $params['openid.sreg.optional'] = implode(',', $params['openid.sreg.optional']);
  518. }
  519. return $params;
  520. }
  521.  
  522. protected function axParams()
  523. {
  524. $params = array();
  525. if ($this->required || $this->optional) {
  526. $params['openid.ns.ax'] = 'http://openid.net/srv/ax/1.0';
  527. $params['openid.ax.mode'] = 'fetch_request';
  528. $this->aliases = array();
  529. $counts = array();
  530. $required = array();
  531. $optional = array();
  532. foreach (array('required','optional') as $type) {
  533. foreach ($this->$type as $alias => $field) {
  534. if (is_int($alias)) $alias = strtr($field, '/', '_');
  535. $this->aliases[$alias] = 'http://axschema.org/' . $field;
  536. if (empty($counts[$alias])) $counts[$alias] = 0;
  537. $counts[$alias] += 1;
  538. ${$type}[] = $alias;
  539. }
  540. }
  541. foreach ($this->aliases as $alias => $ns) {
  542. $params['openid.ax.type.' . $alias] = $ns;
  543. }
  544. foreach ($counts as $alias => $count) {
  545. if ($count == 1) continue;
  546. $params['openid.ax.count.' . $alias] = $count;
  547. }
  548.  
  549. # Don't send empty ax.requied and ax.if_available.
  550. # Google and possibly other providers refuse to support ax when one of these is empty.
  551. if($required) {
  552. $params['openid.ax.required'] = implode(',', $required);
  553. }
  554. if($optional) {
  555. $params['openid.ax.if_available'] = implode(',', $optional);
  556. }
  557. }
  558. return $params;
  559. }
  560.  
  561. protected function authUrl_v1($immediate)
  562. {
  563. $returnUrl = $this->returnUrl;
  564. # If we have an openid.delegate that is different from our claimed id,
  565. # we need to somehow preserve the claimed id between requests.
  566. # The simplest way is to just send it along with the return_to url.
  567. if($this->identity != $this->claimed_id) {
  568. $returnUrl .= (strpos($returnUrl, '?') ? '&' : '?') . 'openid.claimed_id=' . $this->claimed_id;
  569. }
  570.  
  571. $params = array(
  572. 'openid.return_to' => $returnUrl,
  573. 'openid.mode' => $immediate ? 'checkid_immediate' : 'checkid_setup',
  574. 'openid.identity' => $this->identity,
  575. 'openid.trust_root' => $this->trustRoot,
  576. ) + $this->sregParams();
  577.  
  578. return $this->build_url(parse_url($this->server)
  579. , array('query' => http_build_query($params, '', '&')));
  580. }
  581.  
  582. protected function authUrl_v2($immediate)
  583. {
  584. $params = array(
  585. 'openid.ns' => 'http://specs.openid.net/auth/2.0',
  586. 'openid.mode' => $immediate ? 'checkid_immediate' : 'checkid_setup',
  587. 'openid.return_to' => $this->returnUrl,
  588. 'openid.realm' => $this->trustRoot,
  589. );
  590. if ($this->ax) {
  591. $params += $this->axParams();
  592. }
  593. if ($this->sreg) {
  594. $params += $this->sregParams();
  595. }
  596. if (!$this->ax && !$this->sreg) {
  597. # If OP doesn't advertise either SREG, nor AX, let's send them both
  598. # in worst case we don't get anything in return.
  599. $params += $this->axParams() + $this->sregParams();
  600. }
  601.  
  602. if ($this->identifier_select) {
  603. $params['openid.identity'] = $params['openid.claimed_id']
  604. = 'http://specs.openid.net/auth/2.0/identifier_select';
  605. } else {
  606. $params['openid.identity'] = $this->identity;
  607. $params['openid.claimed_id'] = $this->claimed_id;
  608. }
  609.  
  610. return $this->build_url(parse_url($this->server)
  611. , array('query' => http_build_query($params, '', '&')));
  612. }
  613.  
  614. /**
  615. * Returns authentication url. Usually, you want to redirect your user to it.
  616. * @return String The authentication url.
  617. * @param String $select_identifier Whether to request OP to select identity for an user in OpenID 2. Does not affect OpenID 1.
  618. * @throws ErrorException
  619. */
  620. function authUrl($immediate = false)
  621. {
  622. if ($this->setup_url && !$immediate) return $this->setup_url;
  623. if (!$this->server) $this->discover($this->identity);
  624.  
  625. if ($this->version == 2) {
  626. return $this->authUrl_v2($immediate);
  627. }
  628. return $this->authUrl_v1($immediate);
  629. }
  630.  
  631. /**
  632. * Performs OpenID verification with the OP.
  633. * @return Bool Whether the verification was successful.
  634. * @throws ErrorException
  635. */
  636. function validate()
  637. {
  638. # If the request was using immediate mode, a failure may be reported
  639. # by presenting user_setup_url (for 1.1) or reporting
  640. # mode 'setup_needed' (for 2.0). Also catching all modes other than
  641. # id_res, in order to avoid throwing errors.
  642. if(isset($this->data['openid_user_setup_url'])) {
  643. $this->setup_url = $this->data['openid_user_setup_url'];
  644. return false;
  645. }
  646. if($this->mode != 'id_res') {
  647. return false;
  648. }
  649.  
  650. $this->claimed_id = isset($this->data['openid_claimed_id'])?$this->data['openid_claimed_id']:$this->data['openid_identity'];
  651. $params = array(
  652. 'openid.assoc_handle' => $this->data['openid_assoc_handle'],
  653. 'openid.signed' => $this->data['openid_signed'],
  654. 'openid.sig' => $this->data['openid_sig'],
  655. );
  656.  
  657. if (isset($this->data['openid_ns'])) {
  658. # We're dealing with an OpenID 2.0 server, so let's set an ns
  659. # Even though we should know location of the endpoint,
  660. # we still need to verify it by discovery, so $server is not set here
  661. $params['openid.ns'] = 'http://specs.openid.net/auth/2.0';
  662. } elseif (isset($this->data['openid_claimed_id'])
  663. && $this->data['openid_claimed_id'] != $this->data['openid_identity']
  664. ) {
  665. # If it's an OpenID 1 provider, and we've got claimed_id,
  666. # we have to append it to the returnUrl, like authUrl_v1 does.
  667. $this->returnUrl .= (strpos($this->returnUrl, '?') ? '&' : '?')
  668. . 'openid.claimed_id=' . $this->claimed_id;
  669. }
  670.  
  671. if ($this->data['openid_return_to'] != $this->returnUrl) {
  672. # The return_to url must match the url of current request.
  673. # I'm assuing that noone will set the returnUrl to something that doesn't make sense.
  674. return false;
  675. }
  676.  
  677. $server = $this->discover($this->claimed_id);
  678.  
  679. foreach (explode(',', $this->data['openid_signed']) as $item) {
  680. # Checking whether magic_quotes_gpc is turned on, because
  681. # the function may fail if it is. For example, when fetching
  682. # AX namePerson, it might containg an apostrophe, which will be escaped.
  683. # In such case, validation would fail, since we'd send different data than OP
  684. # wants to verify. stripslashes() should solve that problem, but we can't
  685. # use it when magic_quotes is off.
  686. $value = $this->data['openid_' . str_replace('.','_',$item)];
  687. $params['openid.' . $item] = function_exists('get_magic_quotes_gpc') && get_magic_quotes_gpc() ? stripslashes($value) : $value;
  688.  
  689. }
  690.  
  691. $params['openid.mode'] = 'check_authentication';
  692.  
  693. $response = $this->request($server, 'POST', $params);
  694.  
  695. return preg_match('/is_valid\s*:\s*true/i', $response);
  696. }
  697.  
  698. protected function getAxAttributes()
  699. {
  700. $alias = null;
  701. if (isset($this->data['openid_ns_ax'])
  702. && $this->data['openid_ns_ax'] != 'http://openid.net/srv/ax/1.0'
  703. ) { # It's the most likely case, so we'll check it before
  704. $alias = 'ax';
  705. } else {
  706. # 'ax' prefix is either undefined, or points to another extension,
  707. # so we search for another prefix
  708. foreach ($this->data as $key => $val) {
  709. if (substr($key, 0, strlen('openid_ns_')) == 'openid_ns_'
  710. && $val == 'http://openid.net/srv/ax/1.0'
  711. ) {
  712. $alias = substr($key, strlen('openid_ns_'));
  713. break;
  714. }
  715. }
  716. }
  717. if (!$alias) {
  718. # An alias for AX schema has not been found,
  719. # so there is no AX data in the OP's response
  720. return array();
  721. }
  722.  
  723. $attributes = array();
  724. foreach (explode(',', $this->data['openid_signed']) as $key) {
  725. $keyMatch = $alias . '.value.';
  726. if (substr($key, 0, strlen($keyMatch)) != $keyMatch) {
  727. continue;
  728. }
  729. $key = substr($key, strlen($keyMatch));
  730. if (!isset($this->data['openid_' . $alias . '_type_' . $key])) {
  731. # OP is breaking the spec by returning a field without
  732. # associated ns. This shouldn't happen, but it's better
  733. # to check, than cause an E_NOTICE.
  734. continue;
  735. }
  736. $value = $this->data['openid_' . $alias . '_value_' . $key];
  737. $key = substr($this->data['openid_' . $alias . '_type_' . $key],
  738. strlen('http://axschema.org/'));
  739.  
  740. $attributes[$key] = $value;
  741. }
  742. return $attributes;
  743. }
  744.  
  745. protected function getSregAttributes()
  746. {
  747. $attributes = array();
  748. $sreg_to_ax = array_flip(self::$ax_to_sreg);
  749. foreach (explode(',', $this->data['openid_signed']) as $key) {
  750. $keyMatch = 'sreg.';
  751. if (substr($key, 0, strlen($keyMatch)) != $keyMatch) {
  752. continue;
  753. }
  754. $key = substr($key, strlen($keyMatch));
  755. if (!isset($sreg_to_ax[$key])) {
  756. # The field name isn't part of the SREG spec, so we ignore it.
  757. continue;
  758. }
  759. $attributes[$sreg_to_ax[$key]] = $this->data['openid_sreg_' . $key];
  760. }
  761. return $attributes;
  762. }
  763.  
  764. /**
  765. * Gets AX/SREG attributes provided by OP. should be used only after successful validaton.
  766. * Note that it does not guarantee that any of the required/optional parameters will be present,
  767. * or that there will be no other attributes besides those specified.
  768. * In other words. OP may provide whatever information it wants to.
  769. * * SREG names will be mapped to AX names.
  770. * * @return Array Array of attributes with keys being the AX schema names, e.g. 'contact/email'
  771. * @see http://www.axschema.org/types/
  772. */
  773. function getAttributes()
  774. {
  775. if (isset($this->data['openid_ns'])
  776. && $this->data['openid_ns'] == 'http://specs.openid.net/auth/2.0'
  777. ) { # OpenID 2.0
  778. # We search for both AX and SREG attributes, with AX taking precedence.
  779. return $this->getAxAttributes() + $this->getSregAttributes();
  780. }
  781. return $this->getSregAttributes();
  782. }
  783. }
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement