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