/wp-includes/session.php

  1. <?php 
  2. /** 
  3. * Abstract class for managing user session tokens. 
  4. * 
  5. * @since 4.0.0 
  6. */ 
  7. abstract class WP_Session_Tokens { 
  8.  
  9. /** 
  10. * User ID. 
  11. * 
  12. * @since 4.0.0 
  13. * @access protected 
  14. * @var int User ID. 
  15. */ 
  16. protected $user_id; 
  17.  
  18. /** 
  19. * Protected constructor. 
  20. * 
  21. * @since 4.0.0 
  22. * 
  23. * @param int $user_id User whose session to manage. 
  24. */ 
  25. protected function __construct( $user_id ) { 
  26. $this->user_id = $user_id; 
  27.  
  28. /** 
  29. * Get a session token manager instance for a user. 
  30. * 
  31. * This method contains a filter that allows a plugin to swap out 
  32. * the session manager for a subclass of WP_Session_Tokens. 
  33. * 
  34. * @since 4.0.0 
  35. * @access public 
  36. * @static 
  37. * 
  38. * @param int $user_id User whose session to manage. 
  39. */ 
  40. final public static function get_instance( $user_id ) { 
  41. /** 
  42. * Filters the session token manager used. 
  43. * 
  44. * @since 4.0.0 
  45. * 
  46. * @param string $session Name of class to use as the manager. 
  47. * Default 'WP_User_Meta_Session_Tokens'. 
  48. */ 
  49. $manager = apply_filters( 'session_token_manager', 'WP_User_Meta_Session_Tokens' ); 
  50. return new $manager( $user_id ); 
  51.  
  52. /** 
  53. * Hashes a session token for storage. 
  54. * 
  55. * @since 4.0.0 
  56. * @access private 
  57. * 
  58. * @param string $token Session token to hash. 
  59. * @return string A hash of the session token (a verifier). 
  60. */ 
  61. final private function hash_token( $token ) { 
  62. // If ext/hash is not present, use sha1() instead. 
  63. if ( function_exists( 'hash' ) ) { 
  64. return hash( 'sha256', $token ); 
  65. } else { 
  66. return sha1( $token ); 
  67.  
  68. /** 
  69. * Get a user's session. 
  70. * 
  71. * @since 4.0.0 
  72. * @access public 
  73. * 
  74. * @param string $token Session token 
  75. * @return array User session 
  76. */ 
  77. final public function get( $token ) { 
  78. $verifier = $this->hash_token( $token ); 
  79. return $this->get_session( $verifier ); 
  80.  
  81. /** 
  82. * Validate a user's session token as authentic. 
  83. * 
  84. * Checks that the given token is present and hasn't expired. 
  85. * 
  86. * @since 4.0.0 
  87. * @access public 
  88. * 
  89. * @param string $token Token to verify. 
  90. * @return bool Whether the token is valid for the user. 
  91. */ 
  92. final public function verify( $token ) { 
  93. $verifier = $this->hash_token( $token ); 
  94. return (bool) $this->get_session( $verifier ); 
  95.  
  96. /** 
  97. * Generate a session token and attach session information to it. 
  98. * 
  99. * A session token is a long, random string. It is used in a cookie 
  100. * link that cookie to an expiration time and to ensure the cookie 
  101. * becomes invalidated upon logout. 
  102. * 
  103. * This function generates a token and stores it with the associated 
  104. * expiration time (and potentially other session information via the 
  105. * {@see 'attach_session_information'} filter). 
  106. * 
  107. * @since 4.0.0 
  108. * @access public 
  109. * 
  110. * @param int $expiration Session expiration timestamp. 
  111. * @return string Session token. 
  112. */ 
  113. final public function create( $expiration ) { 
  114. /** 
  115. * Filters the information attached to the newly created session. 
  116. * 
  117. * Could be used in the future to attach information such as 
  118. * IP address or user agent to a session. 
  119. * 
  120. * @since 4.0.0 
  121. * 
  122. * @param array $session Array of extra data. 
  123. * @param int $user_id User ID. 
  124. */ 
  125. $session = apply_filters( 'attach_session_information', array(), $this->user_id ); 
  126. $session['expiration'] = $expiration; 
  127.  
  128. // IP address. 
  129. if ( !empty( $_SERVER['REMOTE_ADDR'] ) ) { 
  130. $session['ip'] = $_SERVER['REMOTE_ADDR']; 
  131.  
  132. // User-agent. 
  133. if ( ! empty( $_SERVER['HTTP_USER_AGENT'] ) ) { 
  134. $session['ua'] = wp_unslash( $_SERVER['HTTP_USER_AGENT'] ); 
  135.  
  136. // Timestamp 
  137. $session['login'] = time(); 
  138.  
  139. $token = wp_generate_password( 43, false, false ); 
  140.  
  141. $this->update( $token, $session ); 
  142.  
  143. return $token; 
  144.  
  145. /** 
  146. * Update a session token. 
  147. * 
  148. * @since 4.0.0 
  149. * @access public 
  150. * 
  151. * @param string $token Session token to update. 
  152. * @param array $session Session information. 
  153. */ 
  154. final public function update( $token, $session ) { 
  155. $verifier = $this->hash_token( $token ); 
  156. $this->update_session( $verifier, $session ); 
  157.  
  158. /** 
  159. * Destroy a session token. 
  160. * 
  161. * @since 4.0.0 
  162. * @access public 
  163. * 
  164. * @param string $token Session token to destroy. 
  165. */ 
  166. final public function destroy( $token ) { 
  167. $verifier = $this->hash_token( $token ); 
  168. $this->update_session( $verifier, null ); 
  169.  
  170. /** 
  171. * Destroy all session tokens for this user,  
  172. * except a single token, presumably the one in use. 
  173. * 
  174. * @since 4.0.0 
  175. * @access public 
  176. * 
  177. * @param string $token_to_keep Session token to keep. 
  178. */ 
  179. final public function destroy_others( $token_to_keep ) { 
  180. $verifier = $this->hash_token( $token_to_keep ); 
  181. $session = $this->get_session( $verifier ); 
  182. if ( $session ) { 
  183. $this->destroy_other_sessions( $verifier ); 
  184. } else { 
  185. $this->destroy_all_sessions(); 
  186.  
  187. /** 
  188. * Determine whether a session token is still valid,  
  189. * based on expiration. 
  190. * 
  191. * @since 4.0.0 
  192. * @access protected 
  193. * 
  194. * @param array $session Session to check. 
  195. * @return bool Whether session is valid. 
  196. */ 
  197. final protected function is_still_valid( $session ) { 
  198. return $session['expiration'] >= time(); 
  199.  
  200. /** 
  201. * Destroy all session tokens for a user. 
  202. * 
  203. * @since 4.0.0 
  204. * @access public 
  205. */ 
  206. final public function destroy_all() { 
  207. $this->destroy_all_sessions(); 
  208.  
  209. /** 
  210. * Destroy all session tokens for all users. 
  211. * 
  212. * @since 4.0.0 
  213. * @access public 
  214. * @static 
  215. */ 
  216. final public static function destroy_all_for_all_users() { 
  217. $manager = apply_filters( 'session_token_manager', 'WP_User_Meta_Session_Tokens' ); 
  218. call_user_func( array( $manager, 'drop_sessions' ) ); 
  219.  
  220. /** 
  221. * Retrieve all sessions of a user. 
  222. * 
  223. * @since 4.0.0 
  224. * @access public 
  225. * 
  226. * @return array Sessions of a user. 
  227. */ 
  228. final public function get_all() { 
  229. return array_values( $this->get_sessions() ); 
  230.  
  231. /** 
  232. * This method should retrieve all sessions of a user, keyed by verifier. 
  233. * 
  234. * @since 4.0.0 
  235. * @access protected 
  236. * 
  237. * @return array Sessions of a user, keyed by verifier. 
  238. */ 
  239. abstract protected function get_sessions(); 
  240.  
  241. /** 
  242. * This method should look up a session by its verifier (token hash). 
  243. * 
  244. * @since 4.0.0 
  245. * @access protected 
  246. * 
  247. * @param string $verifier Verifier of the session to retrieve. 
  248. * @return array|null The session, or null if it does not exist. 
  249. */ 
  250. abstract protected function get_session( $verifier ); 
  251.  
  252. /** 
  253. * This method should update a session by its verifier. 
  254. * 
  255. * Omitting the second argument should destroy the session. 
  256. * 
  257. * @since 4.0.0 
  258. * @access protected 
  259. * 
  260. * @param string $verifier Verifier of the session to update. 
  261. * @param array $session Optional. Session. Omitting this argument destroys the session. 
  262. */ 
  263. abstract protected function update_session( $verifier, $session = null ); 
  264.  
  265. /** 
  266. * This method should destroy all session tokens for this user,  
  267. * except a single session passed. 
  268. * 
  269. * @since 4.0.0 
  270. * @access protected 
  271. * 
  272. * @param string $verifier Verifier of the session to keep. 
  273. */ 
  274. abstract protected function destroy_other_sessions( $verifier ); 
  275.  
  276. /** 
  277. * This method should destroy all sessions for a user. 
  278. * 
  279. * @since 4.0.0 
  280. * @access protected 
  281. */ 
  282. abstract protected function destroy_all_sessions(); 
  283.  
  284. /** 
  285. * This static method should destroy all session tokens for all users. 
  286. * 
  287. * @since 4.0.0 
  288. * @access public 
  289. * @static 
  290. */ 
  291. public static function drop_sessions() {} 
  292.  
  293. /** 
  294. * Meta-based user sessions token manager. 
  295. * 
  296. * @since 4.0.0 
  297. */ 
  298. class WP_User_Meta_Session_Tokens extends WP_Session_Tokens { 
  299.  
  300. /** 
  301. * Get all sessions of a user. 
  302. * 
  303. * @since 4.0.0 
  304. * @access protected 
  305. * 
  306. * @return array Sessions of a user. 
  307. */ 
  308. protected function get_sessions() { 
  309. $sessions = get_user_meta( $this->user_id, 'session_tokens', true ); 
  310.  
  311. if ( ! is_array( $sessions ) ) { 
  312. return array(); 
  313.  
  314. $sessions = array_map( array( $this, 'prepare_session' ), $sessions ); 
  315. return array_filter( $sessions, array( $this, 'is_still_valid' ) ); 
  316.  
  317. /** 
  318. * Converts an expiration to an array of session information. 
  319. * 
  320. * @param mixed $session Session or expiration. 
  321. * @return array Session. 
  322. */ 
  323. protected function prepare_session( $session ) { 
  324. if ( is_int( $session ) ) { 
  325. return array( 'expiration' => $session ); 
  326.  
  327. return $session; 
  328.  
  329. /** 
  330. * Retrieve a session by its verifier (token hash). 
  331. * 
  332. * @since 4.0.0 
  333. * @access protected 
  334. * 
  335. * @param string $verifier Verifier of the session to retrieve. 
  336. * @return array|null The session, or null if it does not exist 
  337. */ 
  338. protected function get_session( $verifier ) { 
  339. $sessions = $this->get_sessions(); 
  340.  
  341. if ( isset( $sessions[ $verifier ] ) ) { 
  342. return $sessions[ $verifier ]; 
  343.  
  344. return null; 
  345.  
  346. /** 
  347. * Update a session by its verifier. 
  348. * 
  349. * @since 4.0.0 
  350. * @access protected 
  351. * 
  352. * @param string $verifier Verifier of the session to update. 
  353. * @param array $session Optional. Session. Omitting this argument destroys the session. 
  354. */ 
  355. protected function update_session( $verifier, $session = null ) { 
  356. $sessions = $this->get_sessions(); 
  357.  
  358. if ( $session ) { 
  359. $sessions[ $verifier ] = $session; 
  360. } else { 
  361. unset( $sessions[ $verifier ] ); 
  362.  
  363. $this->update_sessions( $sessions ); 
  364.  
  365. /** 
  366. * Update a user's sessions in the usermeta table. 
  367. * 
  368. * @since 4.0.0 
  369. * @access protected 
  370. * 
  371. * @param array $sessions Sessions. 
  372. */ 
  373. protected function update_sessions( $sessions ) { 
  374. if ( $sessions ) { 
  375. update_user_meta( $this->user_id, 'session_tokens', $sessions ); 
  376. } else { 
  377. delete_user_meta( $this->user_id, 'session_tokens' ); 
  378.  
  379. /** 
  380. * Destroy all session tokens for a user, except a single session passed. 
  381. * 
  382. * @since 4.0.0 
  383. * @access protected 
  384. * 
  385. * @param string $verifier Verifier of the session to keep. 
  386. */ 
  387. protected function destroy_other_sessions( $verifier ) { 
  388. $session = $this->get_session( $verifier ); 
  389. $this->update_sessions( array( $verifier => $session ) ); 
  390.  
  391. /** 
  392. * Destroy all session tokens for a user. 
  393. * 
  394. * @since 4.0.0 
  395. * @access protected 
  396. */ 
  397. protected function destroy_all_sessions() { 
  398. $this->update_sessions( array() ); 
  399.  
  400. /** 
  401. * Destroy all session tokens for all users. 
  402. * 
  403. * @since 4.0.0 
  404. * @access public 
  405. * @static 
  406. */ 
  407. public static function drop_sessions() { 
  408. delete_metadata( 'user', 0, 'session_tokens', false, true ); 
.