PageRenderTime 52ms CodeModel.GetById 23ms RepoModel.GetById 1ms app.codeStats 0ms

/lib/simplesamlphp-1.10.0/lib/SimpleSAML/Auth/State.php

https://bitbucket.org/sahkoinenaanestys/sahkoinenaanestys
PHP | 349 lines | 131 code | 77 blank | 141 comment | 18 complexity | b74b64784a5742cae3498d7918826bd8 MD5 | raw file
  1. <?php
  2. /**
  3. * This is a helper class for saving and loading state information.
  4. *
  5. * The state must be an associative array. This class will add additional keys to this
  6. * array. These keys will always start with 'SimpleSAML_Auth_State.'.
  7. *
  8. * It is also possible to add a restart URL to the state. If state information is lost, for
  9. * example because it timed out, or the user loaded a bookmarked page, the loadState function
  10. * will redirect to this URL. To use this, set $state[SimpleSAML_Auth_State::RESTART] to this
  11. * URL.
  12. *
  13. * Both the saveState and the loadState function takes in a $stage parameter. This parameter is
  14. * a security feature, and is used to prevent the user from taking a state saved one place and
  15. * using it as input a different place.
  16. *
  17. * The $stage parameter must be a unique string. To maintain uniqueness, it must be on the form
  18. * "<classname>.<identifier>" or "<module>:<identifier>".
  19. *
  20. * There is also support for passing exceptions through the state.
  21. * By defining an exception handler when creating the state array, users of the state
  22. * array can call throwException with the state and the exception. This exception will
  23. * be passed to the handler defined by the EXCEPTION_HANDLER_URL or EXCEPTION_HANDLER_FUNC
  24. * elements of the state array.
  25. *
  26. * @author Olav Morken, UNINETT AS.
  27. * @package simpleSAMLphp
  28. * @version $Id$
  29. */
  30. class SimpleSAML_Auth_State {
  31. /**
  32. * The index in the state array which contains the identifier.
  33. */
  34. const ID = 'SimpleSAML_Auth_State.id';
  35. /**
  36. * The index in the cloned state array which contains the identifier of the
  37. * original state.
  38. */
  39. const CLONE_ORIGINAL_ID = 'SimpleSAML_Auth_State.cloneOriginalId';
  40. /**
  41. * The index in the state array which contains the current stage.
  42. */
  43. const STAGE = 'SimpleSAML_Auth_State.stage';
  44. /**
  45. * The index in the state array which contains the restart URL.
  46. */
  47. const RESTART = 'SimpleSAML_Auth_State.restartURL';
  48. /**
  49. * The index in the state array which contains the exception handler URL.
  50. */
  51. const EXCEPTION_HANDLER_URL = 'SimpleSAML_Auth_State.exceptionURL';
  52. /**
  53. * The index in the state array which contains the exception handler function.
  54. */
  55. const EXCEPTION_HANDLER_FUNC = 'SimpleSAML_Auth_State.exceptionFunc';
  56. /**
  57. * The index in the state array which contains the exception data.
  58. */
  59. const EXCEPTION_DATA = 'SimpleSAML_Auth_State.exceptionData';
  60. /**
  61. * The stage of a state with an exception.
  62. */
  63. const EXCEPTION_STAGE = 'SimpleSAML_Auth_State.exceptionStage';
  64. /**
  65. * The URL parameter which contains the exception state id.
  66. */
  67. const EXCEPTION_PARAM = 'SimpleSAML_Auth_State_exceptionId';
  68. /**
  69. * State timeout.
  70. */
  71. private static $stateTimeout = NULL;
  72. /**
  73. * Retrieve the ID of a state array.
  74. *
  75. * Note that this function will not save the state.
  76. *
  77. * @param array &$state The state array.
  78. * @param bool $rawId Return a raw ID, without a restart URL. Defaults to FALSE.
  79. * @return string Identifier which can be used to retrieve the state later.
  80. */
  81. public static function getStateId(&$state, $rawId = FALSE) {
  82. assert('is_array($state)');
  83. assert('is_bool($rawId)');
  84. if (!array_key_exists(self::ID, $state)) {
  85. $state[self::ID] = SimpleSAML_Utilities::generateID();
  86. }
  87. $id = $state[self::ID];
  88. if ($rawId || !array_key_exists(self::RESTART, $state)) {
  89. /* Either raw ID or no restart URL. In any case, return the raw ID. */
  90. return $id;
  91. }
  92. /* We have a restart URL. Return the ID with that URL. */
  93. return $id . ':' . $state[self::RESTART];
  94. }
  95. /**
  96. * Retrieve state timeout.
  97. *
  98. * @return integer State timeout.
  99. */
  100. private static function getStateTimeout() {
  101. if (self::$stateTimeout === NULL) {
  102. $globalConfig = SimpleSAML_Configuration::getInstance();
  103. self::$stateTimeout = $globalConfig->getInteger('session.state.timeout', 60*60);
  104. }
  105. return self::$stateTimeout;
  106. }
  107. /**
  108. * Save the state.
  109. *
  110. * This function saves the state, and returns an id which can be used to
  111. * retrieve it later. It will also update the $state array with the identifier.
  112. *
  113. * @param array &$state The login request state.
  114. * @param string $stage The current stage in the login process.
  115. * @param bool $rawId Return a raw ID, without a restart URL.
  116. * @return string Identifier which can be used to retrieve the state later.
  117. */
  118. public static function saveState(&$state, $stage, $rawId = FALSE) {
  119. assert('is_array($state)');
  120. assert('is_string($stage)');
  121. assert('is_bool($rawId)');
  122. $return = self::getStateId($state, $rawId);
  123. $id = $state[self::ID];
  124. /* Save stage. */
  125. $state[self::STAGE] = $stage;
  126. /* Save state. */
  127. $serializedState = serialize($state);
  128. $session = SimpleSAML_Session::getInstance();
  129. $session->setData('SimpleSAML_Auth_State', $id, $serializedState, self::getStateTimeout());
  130. SimpleSAML_Logger::debug('Saved state: ' . var_export($return, TRUE));
  131. return $return;
  132. }
  133. /**
  134. * Clone the state.
  135. *
  136. * This function clones and returns the new cloned state.
  137. *
  138. * @param array $state The original request state.
  139. * @return array Cloned state data.
  140. */
  141. public static function cloneState(array $state) {
  142. $clonedState = $state;
  143. if (array_key_exists(self::ID, $state)) {
  144. $clonedState[self::CLONE_ORIGINAL_ID] = $state[self::ID];
  145. unset($clonedState[self::ID]);
  146. SimpleSAML_Logger::debug('Cloned state: ' . var_export($state[self::ID], TRUE));
  147. } else {
  148. SimpleSAML_Logger::debug('Cloned state with undefined id.');
  149. }
  150. return $clonedState;
  151. }
  152. /**
  153. * Retrieve saved state.
  154. *
  155. * This function retrieves saved state information. If the state information has been lost,
  156. * it will attempt to restart the request by calling the restart URL which is embedded in the
  157. * state information. If there is no restart information available, an exception will be thrown.
  158. *
  159. * @param string $id State identifier (with embedded restart information).
  160. * @param string $stage The stage the state should have been saved in.
  161. * @param bool $allowMissing Whether to allow the state to be missing.
  162. * @return array|NULL State information, or NULL if the state is missing and $allowMissing is TRUE.
  163. */
  164. public static function loadState($id, $stage, $allowMissing = FALSE) {
  165. assert('is_string($id)');
  166. assert('is_string($stage)');
  167. assert('is_bool($allowMissing)');
  168. SimpleSAML_Logger::debug('Loading state: ' . var_export($id, TRUE));
  169. $tmp = explode(':', $id, 2);
  170. $id = $tmp[0];
  171. if (count($tmp) === 2) {
  172. $restartURL = $tmp[1];
  173. } else {
  174. $restartURL = NULL;
  175. }
  176. $session = SimpleSAML_Session::getInstance();
  177. $state = $session->getData('SimpleSAML_Auth_State', $id);
  178. if ($state === NULL) {
  179. /* Could not find saved data. */
  180. if ($allowMissing) {
  181. return NULL;
  182. }
  183. if ($restartURL === NULL) {
  184. throw new SimpleSAML_Error_NoState();
  185. }
  186. SimpleSAML_Utilities::redirect($restartURL);
  187. }
  188. $state = unserialize($state);
  189. assert('is_array($state)');
  190. assert('array_key_exists(self::ID, $state)');
  191. assert('array_key_exists(self::STAGE, $state)');
  192. /* Verify stage. */
  193. if ($state[self::STAGE] !== $stage) {
  194. /* This could be a user trying to bypass security, but most likely it is just
  195. * someone using the back-button in the browser. We try to restart the
  196. * request if that is possible. If not, show an error.
  197. */
  198. $msg = 'Wrong stage in state. Was \'' . $state[self::STAGE] .
  199. '\', shoud be \'' . $stage . '\'.';
  200. SimpleSAML_Logger::warning($msg);
  201. if ($restartURL === NULL) {
  202. throw new Exception($msg);
  203. }
  204. SimpleSAML_Utilities::redirect($restartURL);
  205. }
  206. return $state;
  207. }
  208. /**
  209. * Delete state.
  210. *
  211. * This function deletes the given state to prevent the user from reusing it later.
  212. *
  213. * @param array &$state The state which should be deleted.
  214. */
  215. public static function deleteState(&$state) {
  216. assert('is_array($state)');
  217. if (!array_key_exists(self::ID, $state)) {
  218. /* This state hasn't been saved. */
  219. return;
  220. }
  221. SimpleSAML_Logger::debug('Deleting state: ' . var_export($state[self::ID], TRUE));
  222. $session = SimpleSAML_Session::getInstance();
  223. $session->deleteData('SimpleSAML_Auth_State', $state[self::ID]);
  224. }
  225. /**
  226. * Throw exception to the state exception handler.
  227. *
  228. * @param array $state The state array.
  229. * @param SimpleSAML_Error_Exception $exception The exception.
  230. */
  231. public static function throwException($state, SimpleSAML_Error_Exception $exception) {
  232. assert('is_array($state)');
  233. if (array_key_exists(self::EXCEPTION_HANDLER_URL, $state)) {
  234. /* Save the exception. */
  235. $state[self::EXCEPTION_DATA] = $exception;
  236. $id = self::saveState($state, self::EXCEPTION_STAGE);
  237. /* Redirect to the exception handler. */
  238. SimpleSAML_Utilities::redirect($state[self::EXCEPTION_HANDLER_URL], array(self::EXCEPTION_PARAM => $id));
  239. } elseif (array_key_exists(self::EXCEPTION_HANDLER_FUNC, $state)) {
  240. /* Call the exception handler. */
  241. $func = $state[self::EXCEPTION_HANDLER_FUNC];
  242. assert('is_callable($func)');
  243. call_user_func($func, $exception, $state);
  244. assert(FALSE);
  245. } else {
  246. /*
  247. * No exception handler is defined for the current state.
  248. */
  249. throw $exception;
  250. }
  251. }
  252. /**
  253. * Retrieve an exception state.
  254. *
  255. * @param string|NULL $id The exception id. Can be NULL, in which case it will be retrieved from the request.
  256. * @return array|NULL The state array with the exception, or NULL if no exception was thrown.
  257. */
  258. public static function loadExceptionState($id = NULL) {
  259. assert('is_string($id) || is_null($id)');
  260. if ($id === NULL) {
  261. if (!array_key_exists(self::EXCEPTION_PARAM, $_REQUEST)) {
  262. /* No exception. */
  263. return NULL;
  264. }
  265. $id = $_REQUEST[self::EXCEPTION_PARAM];
  266. }
  267. $state = self::loadState($id, self::EXCEPTION_STAGE);
  268. assert('array_key_exists(self::EXCEPTION_DATA, $state)');
  269. return $state;
  270. }
  271. }
  272. ?>