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