PHP OpenID consumer
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

urlbuilder.inc.php 7.4KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258
  1. <?PHP
  2. /* Poidsy 0.5 - http://chris.smith.name/projects/poidsy
  3. * Copyright (c) 2008-2010 Chris Smith
  4. *
  5. * Permission is hereby granted, free of charge, to any person obtaining a copy
  6. * of this software and associated documentation files (the "Software"), to deal
  7. * in the Software without restriction, including without limitation the rights
  8. * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
  9. * copies of the Software, and to permit persons to whom the Software is
  10. * furnished to do so, subject to the following conditions:
  11. *
  12. * The above copyright notice and this permission notice shall be included in
  13. * all copies or substantial portions of the Software.
  14. *
  15. * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
  16. * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
  17. * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
  18. * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
  19. * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
  20. * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
  21. * SOFTWARE.
  22. */
  23. require_once(dirname(__FILE__) . '/keymanager.inc.php');
  24. class URLBuilder {
  25. const MIN_VERSION_FOR_NS = 2;
  26. private static $namespace = array(
  27. 1 => 'http://openid.net/signon/1.1',
  28. 2 => 'http://specs.openid.net/auth/2.0'
  29. );
  30. public static function addArguments($base, $arguments) {
  31. $first = true;
  32. $res = $base === false ? '' : $base;
  33. if ($base !== false && strrpos($base, '?', -2) === false) {
  34. if ($base[strlen($base) - 1] != '?') {
  35. $res .= '?';
  36. }
  37. } else if ($base !== false) {
  38. $res .= '&';
  39. }
  40. foreach ($arguments as $key => $value) {
  41. if ($first) {
  42. $first = false;
  43. } else {
  44. $res .= '&';
  45. }
  46. $res .= urlencode($key) . '=' . urlencode($value);
  47. }
  48. return $res;
  49. }
  50. public static function buildRequest($type, $base, $delegate, $identity, $returnURL, $handle, $version = 1) {
  51. $args = array(
  52. 'openid.mode' => 'checkid_' . $type,
  53. 'openid.identity' => $version == 1 ? $delegate : $identity,
  54. 'openid.claimed_id' => $delegate,
  55. ($version == 1 ? 'openid.trust_root' : 'openid.realm') => self::getTrustRoot($returnURL),
  56. 'openid.return_to' => self::addArguments($returnURL,
  57. array('openid.nonce' => $_SESSION['openid']['nonce']))
  58. );
  59. if ($version >= self::MIN_VERSION_FOR_NS) {
  60. $args['openid.ns'] = self::$namespace[$version];
  61. }
  62. if ($handle !== null) {
  63. $args['openid.assoc_handle'] = $handle;
  64. }
  65. self::addSRegArgs($args);
  66. return self::addArguments($base, $args);
  67. }
  68. public static function getTrustRoot($base = null, $curl = null) {
  69. $curr = $curl == null ? self::getCurrentURL() : $curl;
  70. if (defined('OPENID_TRUSTROOT')) {
  71. $root = OPENID_TRUSTROOT;
  72. } else {
  73. $root = $base == null ? $curr : $base;
  74. }
  75. // Note that this may end up going back to 'http:/' if
  76. // the domains don't match.
  77. while (substr($curr, 0, strlen($root)) != $root) {
  78. $root = dirname($root) . '/';
  79. }
  80. return $root;
  81. }
  82. private static function addSRegArgs(&$args) {
  83. if (defined('OPENID_SREG_REQUEST')) {
  84. $args['openid.sreg.required'] = OPENID_SREG_REQUEST;
  85. }
  86. if (defined('OPENID_SREG_OPTIONAL')) {
  87. $args['openid.sreg.optional'] = OPENID_SREG_OPTIONAL;
  88. }
  89. if (defined('OPENID_SREG_POLICY')) {
  90. $args['openid.sreg.policy_url'] = OPENID_SREG_POLICY;
  91. }
  92. }
  93. public static function buildAssociate($server, $version = 1, $assocType = null, $sessionType = null) {
  94. if ($assocType == null) { $assocType = 'HMAC-SHA1'; }
  95. if ($sessionType == null) { $sessionType = 'DH-SHA1'; }
  96. $args = array(
  97. 'openid.mode' => 'associate',
  98. 'openid.assoc_type' => $assocType,
  99. );
  100. if ($version >= self::MIN_VERSION_FOR_NS) {
  101. $args['openid.ns'] = self::$namespace[$version];
  102. }
  103. if (KeyManager::supportsDH()) {
  104. $args['openid.session_type'] = $sessionType;
  105. $args['openid.dh_modulus'] = KeyManager::getDhModulus();
  106. $args['openid.dh_gen'] = KeyManager::getDhGen();
  107. $args['openid.dh_consumer_public'] = KeyManager::getDhPublicKey($server);
  108. } else {
  109. $args['openid.session_type'] = '';
  110. }
  111. return self::addArguments(false, $args);
  112. }
  113. public static function buildAuth($params, $version = 1) {
  114. $args = array(
  115. 'openid.mode' => 'check_authentication'
  116. );
  117. if ($version >= self::MIN_VERSION_FOR_NS) {
  118. $args['openid.ns'] = self::$namespace[$version];
  119. }
  120. $toadd = array('assoc_handle', 'sig', 'signed');
  121. $toadd = array_merge($toadd, explode(',', $params['openid_signed']));
  122. foreach ($toadd as $arg) {
  123. if (!isset($args['openid.' . $arg])) {
  124. $args['openid.' . $arg] = $params['openid_' . str_replace('.', '_', $arg)];
  125. }
  126. }
  127. return self::addArguments(false, $args);
  128. }
  129. public static function isValidReturnToURL($url) {
  130. // 11.1: The URL scheme, authority, and path MUST be the same between the two URLs.
  131. // Any query parameters that are present in the "openid.return_to" URL MUST
  132. // also be present with the same values in the URL of the HTTP request the
  133. // RP received.
  134. return self::isSameURL(self::getCurrentURL(true), $url);
  135. }
  136. public static function isSameURL($url1, $url2) {
  137. $actual = parse_url($url1);
  138. $return = parse_url($url2);
  139. foreach (array('scheme', 'host', 'port', 'user', 'pass', 'path') as $part) {
  140. if ($part == 'port') {
  141. if (!isset($actual['port'])) { $actual['port'] = $actual['scheme'] == 'https' ? 443 : 80; }
  142. if (!isset($return['port'])) { $return['port'] = $return['scheme'] == 'https' ? 443 : 80; }
  143. }
  144. if (isset($actual[$part]) ^ isset($return[$part])) {
  145. // Present in one but not the other
  146. return false;
  147. } else if (isset($actual[$part]) && $actual[$part] != $return[$part]) {
  148. // Present in both but different
  149. return false;
  150. }
  151. }
  152. parse_str($actual['query'], $actualVars);
  153. parse_str($return['query'], $returnVars);
  154. foreach ($returnVars as $key => $value) {
  155. if (!isset($actualVars[$key]) || $actualVars[$key] != $value) {
  156. return false;
  157. }
  158. }
  159. return true;
  160. }
  161. public static function getCurrentURL($raw = false) {
  162. $res = 'http';
  163. if (isset($_SERVER['HTTPS'])) {
  164. $res = 'https';
  165. }
  166. $res .= '://' . $_SERVER['SERVER_NAME'];
  167. if ($_SERVER['SERVER_PORT'] != (isset($_SERVER['HTTPS']) ? 443 : 80)) {
  168. $res .= ':' . $_SERVER['SERVER_PORT'];
  169. }
  170. $url = $_SERVER['REQUEST_URI'];
  171. if (!$raw) {
  172. while (preg_match('/([\?&])openid[\._](.*?)=(.*?)(&|$)/', $url, $m)) {
  173. $url = str_replace($m[0], $m[1], $url);
  174. }
  175. }
  176. $url = preg_replace('/\??&*$/', '', $url);
  177. return $res . $url;
  178. }
  179. /**
  180. * Redirects the user back to their original page.
  181. */
  182. public static function redirect() {
  183. if (defined('OPENID_REDIRECTURL')) {
  184. $url = OPENID_REDIRECTURL;
  185. } else if (isset($_SESSION['openid']['redirect'])) {
  186. $url = $_SESSION['openid']['redirect'];
  187. } else {
  188. $url = self::getCurrentURL();
  189. }
  190. self::doRedirect($url);
  191. }
  192. /**
  193. * Redirects the user to the specified URL.
  194. *
  195. * @param $url The URL to redirect the user to
  196. */
  197. public static function doRedirect($url) {
  198. header('Location: ' . $url);
  199. echo '<html><head><title>Redirecting</title></head><body>';
  200. echo '<p>Redirecting to <a href="', htmlentities($url), '">';
  201. echo htmlentities($url), '</a></p></body></html>';
  202. exit();
  203. }
  204. }
  205. ?>