WordPress.org

Make WordPress Core

Ticket #20276: 20276-potential-sessions-api.php

File 20276-potential-sessions-api.php, 6.2 KB (added by mdawaffe, 6 years ago)
Line 
1<?php
2
3abstract class WP_Session_Tokens {
4        protected $user_id;
5
6        protected function __construct( $user_id ) {
7                $this->user_id = $user_id;
8        }
9
10        final public static function get_instance( $user_id ) {
11                $manager = apply_filters( 'session_token_manager', 'WP_Session_Tokens' ); 
12                return new $manager( $user_id );
13        }
14
15        final private function hash( $token ) {
16                return hash( 'sha256', $token );
17        }
18
19        final public function verify_token( $token ) {
20                $verifier = $this->hash( $token );
21                return !! $this->get_session( $verifier );
22        }
23
24        final public function create_token( $expiration ) {
25                $session = apply_filters( 'attach_session_information', array(), $this->user_id );
26                $session['expiration'] = $expiration;
27
28                $token = wp_generate_password( 43, false, false );
29
30                $this->update_token( $token, $session );
31
32                return $token;
33        }
34
35        final public function update_token( $token ) {
36                $verifier = $this->hash( $token );
37                $this->update_session( $verifier, $session );
38        }
39
40        final public function destroy_token( $token ) {
41                $verifier = $this->hash( $token );
42                $this->update_session( $verifier, null );
43        }
44
45        final public function destroy_other_tokens( $token_to_keep ) {
46                $verifier = $this->hash( $token );
47                $session = $this->get_session( $verifier );
48                if ( $session ) {
49                        $this->keep_only_this_session( $verifier, $session );
50                } else {
51                        $this->destroy_all_tokens();
52                }
53        }
54
55        final protected function is_still_valid( $session ) {
56                return $session['expiration'] >= time();
57        }
58
59        final public function destroy_all_tokens() {
60                $this->destroy_all_sessions();
61        }
62
63        final public static function destroy_all_tokens_for_all_users() {
64                $manager = apply_filters( 'session_token_manager', 'WP_Session_Tokens' );
65                $manager::drop_sessions();
66        }
67
68        abstract public function get_sessions();
69
70        abstract protected function get_session( $verifier );
71
72        abstract protected function update_session( $verifier, $session = null );
73
74        abstract protected function keep_only_this_session( $verifier, $session );
75
76        abstract protected function destroy_all_sessions();
77
78        abstract public static function drop_sessions();
79}
80
81class WP_User_Meta_Session_Tokens extends WP_Session_Tokens {
82        public function get_sessions() {
83                $sessions = get_user_meta( $this->user_id, 'session_tokens', true );
84
85                if ( ! is_array( $sessions ) ) {
86                        return array();
87                }
88
89                $sessions = array_map( array( $this, 'prepare_session' ), $sessions );
90                return array_filter( array( $this, 'is_still_valid' ), $sessions );
91        }
92
93        protected function prepare_session( $session ) {
94                if ( is_int( $session ) ) {
95                        return array( 'expiration' => $session );
96                }
97
98                return $session;
99        }
100
101        protected function get_session( $verifier ) {
102                $sessions = $this->get_sessions();
103
104                if ( isset( $sessions[$verifier] ) ) {
105                        return $session;
106                }
107
108                return null;
109        }
110
111        protected function update_session( $verifier, $session = null ) {
112                $sessions = $this->get_sessions();
113
114                if ( $session ) {
115                        $sessions[$verifier] = $session;
116                } else {
117                        unset( $sessions[$verifier] );
118                }
119
120                $this->update_sessions( $sessions );
121        }
122
123        protected function update_sessions( $sessions ) {
124                if ( ! has_filter( 'attach_session_information' ) ) {
125                        $sessions = wp_list_pluck( $sessions, 'expiration' );
126                }
127
128                if ( $sessions ) {
129                        update_user_meta( $this->user_id, 'session_tokens', $sessions );
130                } else {
131                        delete_user_meta( $this->user_id, 'session_tokens' );
132                }
133        }
134
135        protected function keep_only_this_session( $verifier, $session ) {
136                $this->update_sessions( array( $verifier => $session ) );
137        }
138
139        protected function destroy_all_sessions() {
140                $this->update_sessions( array() );
141        }
142
143        public static function drop_sessions() {
144                delete_metadata( 'user', false, 'session_tokens', false, true );
145        }
146}
147
148/*
149CREATE TABLE `wp_sessions` (
150        `id` bigint(20 NOT NULL AUTO_INCREMENT,
151        `user_id` bigint(20) NOT NULL,
152        `verifier` char(64) NOT NULL,
153        `expiration` int(10) NOT NULL,
154        `ua` varchar(400) NOT NULL,
155        `ip` varchar(45) NOT NULL,
156        `start` int(10) NOT NULL,
157        PRIMARY KEY (`id`),
158        UNIQUE KEY `user_id` (`user_id`, `verifier`)
159)
160*/
161class Global_Table_Session_Tokens extends WP_Session_Tokens {
162        public function get_sessions() {
163                global $wpdb;
164
165                $sessions = $wpdb->get_results( $wpdb->prepare(
166                        "SELECT * FROM `$wpdb->sessions` WHERE `user_id` = %d AND `expiration` >= %d",
167                        $this->user_id,
168                        time()
169                ), ARRAY_A );
170
171                if ( ! is_array( $sessions ) ) {
172                        return array();
173                }
174
175                $verifiers = wp_list_pluck( $sessions, 'verifier' );
176
177                return array_combine( $verifiers, array_map( array( $this, 'prepare_session' ), $sessions ) );
178        }
179
180        protected function prepare_session( $session ) {
181                unset( $session['user_id'], $session['verifier'] );
182                $session['expiration'] = (int) $session['expiration'];
183                return $session;
184        }
185
186        protected function get_session( $verifier ) {
187                global $wpdb;
188
189                $session = $wpdb->get_results( $wpdb->prepare(
190                        "SELECT * FROM `$wpdb->sessions` WHERE `user_id` = %d AND `expiration` >= %d AND `verifier` = %s",
191                        $this->user_id,
192                        time(),
193                        $verifier
194                ), ARRAY_A );
195
196                if ( ! $session ) {
197                        return null;
198                }
199
200                return $this->prepare_session( $session );
201        }
202
203        protected function update_session( $verifier, $session = null ) {
204                global $wpdb;
205
206                if ( $session ) {
207                        $wpdb->query( $wpdb->prepare(
208                                "INSERT INTO `$wpdb->sessions` ( `user_id`, `verifier`, `expiration`, `ua`, `ip`, `start` ) VALUES ( %d, %s, %d, %s, %s, %d ) " .
209                                "ON DUPLICATE KEY UPDATE `ua` = VALUES( `ua` ), `ip` = VALUES( `ip` ), `expiration` = VALUES( `expiration` ), `start` = VALUES( `start` )",
210
211                                $this->user_id,
212                                $verifier,
213                                $session['expiration'],
214                                $session['ua'],
215                                $session['ip'],
216                                $session['start']
217                        ) );
218                } else {
219                        $wpdb->query( $wpdb->prepare(
220                                "DELETE FROM `$wpdb->sessions` WHERE `user_id` = %d AND `verifier` = %s",
221                                $this->user_id,
222                                $verifier
223                        ) );
224                }
225        }
226
227        protected function keep_only_this_session( $verifier, $session ) {
228                global $wpdb;
229
230                $wpdb->query( $wpdb->prepare(
231                        "DELETE FROM `$wpdb->sessions` WHERE `user_id` = %d AND `verifier` != %s",
232                        $this->user_id,
233                        $verifier
234                ) );
235        }
236
237        protected function destroy_all_sessions() {
238                global $wpdb;
239
240                $wpdb->query( $wpdb->prepare(
241                        "DELETE FROM `$wpdb->sessions` WHERE `user_id` = %d",
242                        $this->user_id
243                ) );
244        }
245
246        public static function drop_sessions() {
247                global $wpdb;
248
249                $wpdb->query( "TRUNCATE TABLE `$wpdb->sessions`" );
250        }
251}