PluginProbe
Search Atlas SEO – OTTO AI SEO Automation for WordPress / trunk
Search Atlas SEO – OTTO AI SEO Automation for WordPress vtrunk
2.6.26 2.6.25 2.6.24 2.6.23 2.6.22 2.6.21 2.6.20 2.6.19 2.6.18 2.6.17 2.6.16 2.6.15 2.6.14 2.6.13 2.6.12 2.6.11 2.6.10 2.6.9 2.6.8 2.6.7 2.6.6 2.6.5 2.6.4 2.6.3 2.5.23 All 138 releases
metasync / includes / class-metasync-auth-manager.php

class-metasync-auth-manager.php in Search Atlas SEO – OTTO AI SEO Automation for WordPress trunk, at includes/class-metasync-auth-manager.php

445 lines 13.3 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 <?php
2 /**
3 * Authentication Manager for MetaSync Plugin
4 *
5 * Provides secure, transient-based authentication for protected areas
6 * Replaces session-based authentication with WordPress-native solutions
7 *
8 * Best Practices:
9 * - Uses WordPress Transients (works with object caching, Redis, Memcached)
10 * - User Meta for persistent access
11 * - No PHP sessions (works on all hosting environments)
12 * - OOP design for reusability
13 * - Supports multiple authentication contexts
14 *
15 * @package MetaSync
16 * @subpackage MetaSync/includes
17 * @since 2.5.12
18 */
19
20 // Prevent direct access
21 if (!defined('ABSPATH')) {
22 exit;
23 }
24
25 class Metasync_Auth_Manager {
26
27 /**
28 * Authentication context identifier
29 *
30 * @var string
31 */
32 private $context;
33
34 /**
35 * Transient timeout in seconds (default: 30 minutes)
36 *
37 * @var int
38 */
39 private $transient_timeout = 1800;
40
41 /**
42 * User meta key for persistent access
43 *
44 * @var string
45 */
46 private $user_meta_key;
47
48 /**
49 * Current user ID
50 *
51 * @var int|null
52 */
53 private $user_id;
54
55 /**
56 * Constructor
57 *
58 * @param string $context Authentication context (e.g., 'debug', 'whitelabel')
59 * @param int $timeout Transient timeout in seconds (default: 1800 = 30 minutes)
60 */
61 public function __construct($context = 'default', $timeout = 1800) {
62 $this->context = sanitize_key($context);
63 $this->transient_timeout = absint($timeout);
64 $this->user_meta_key = 'metasync_' . $this->context . '_access';
65
66 // Don't call wp_get_current_user() in constructor - it might not be available yet
67 // Will be set lazily when needed via get_user_id()
68 $this->user_id = null;
69 }
70
71 /**
72 * Get current user ID (lazy loaded)
73 *
74 * @return int|null User ID or null if not available
75 */
76 private function get_user_id() {
77 if ($this->user_id === null && function_exists('wp_get_current_user')) {
78 $current_user = wp_get_current_user();
79 $this->user_id = $current_user && $current_user->ID ? $current_user->ID : 0;
80 }
81 return $this->user_id > 0 ? $this->user_id : null;
82 }
83
84 /**
85 * Check if user has access (checks both persistent and transient)
86 *
87 * @return bool True if user has access, false otherwise
88 */
89 public function has_access() {
90 $user_id = $this->get_user_id();
91 if (!$user_id) {
92 return false;
93 }
94
95 // Check persistent access (user meta)
96 if ($this->has_persistent_access()) {
97 // Refresh transient activity
98 $this->update_activity();
99 return true;
100 }
101
102 // Check temporary access (transient)
103 if ($this->has_transient_access()) {
104 // Refresh transient activity
105 $this->update_activity();
106 return true;
107 }
108
109 return false;
110 }
111
112 /**
113 * Check if user has persistent access via user meta
114 *
115 * @return bool
116 */
117 public function has_persistent_access() {
118 $user_id = $this->get_user_id();
119 if (!$user_id) {
120 return false;
121 }
122
123 $access = get_user_meta($user_id, $this->user_meta_key, true);
124 return $access === 'granted';
125 }
126
127 /**
128 * Check if user has temporary access via transient
129 *
130 * @return bool
131 */
132 public function has_transient_access() {
133 $user_id = $this->get_user_id();
134 if (!$user_id) {
135 return false;
136 }
137
138 $transient_key = $this->get_transient_key();
139 $access = get_transient($transient_key);
140 return $access === 'granted';
141 }
142
143 /**
144 * Grant persistent access (user meta)
145 *
146 * @return bool True on success, false on failure
147 */
148 public function grant_persistent_access() {
149 $user_id = $this->get_user_id();
150 if (!$user_id) {
151 return false;
152 }
153
154 $result = update_user_meta($user_id, $this->user_meta_key, 'granted');
155
156 // Also set transient for immediate access
157 $this->grant_transient_access();
158
159 return $result !== false;
160 }
161
162 /**
163 * Grant temporary access (transient)
164 *
165 * @return bool True on success, false on failure
166 */
167 public function grant_transient_access($epoch = null) {
168 $user_id = $this->get_user_id();
169 if (!$user_id) {
170 return false;
171 }
172
173 $transient_key = $this->get_transient_key($epoch);
174 $result = set_transient($transient_key, 'granted', $this->transient_timeout);
175
176 // Set activity timestamp
177 $this->update_activity($epoch);
178
179 return $result;
180 }
181
182 /**
183 * Revoke all access (both persistent and transient)
184 *
185 * @return bool True on success, false on failure
186 */
187 public function revoke_access() {
188 $user_id = $this->get_user_id();
189 if (!$user_id) {
190 return false;
191 }
192
193 // Delete user meta
194 delete_user_meta($user_id, $this->user_meta_key);
195
196 // Delete transients
197 delete_transient($this->get_transient_key());
198 delete_transient($this->get_activity_transient_key());
199
200 return true;
201 }
202
203 /**
204 * Revoke only persistent access (keeps transient until expiration)
205 *
206 * @return bool True on success, false on failure
207 */
208 public function revoke_persistent_access() {
209 $user_id = $this->get_user_id();
210 if (!$user_id) {
211 return false;
212 }
213
214 return delete_user_meta($user_id, $this->user_meta_key);
215 }
216
217 /**
218 * Revoke only transient access (keeps persistent if granted)
219 *
220 * @return bool True on success, false on failure
221 */
222 public function revoke_transient_access() {
223 $user_id = $this->get_user_id();
224 if (!$user_id) {
225 return false;
226 }
227
228 delete_transient($this->get_transient_key());
229 delete_transient($this->get_activity_transient_key());
230
231 return true;
232 }
233
234 /**
235 * Update last activity timestamp
236 *
237 * @return bool True on success, false on failure
238 */
239 private function update_activity($epoch = null) {
240 $user_id = $this->get_user_id();
241 if (!$user_id) {
242 return false;
243 }
244
245 $activity_key = $this->get_activity_transient_key($epoch);
246 return set_transient($activity_key, time(), $this->transient_timeout);
247 }
248
249 /**
250 * Get last activity timestamp
251 *
252 * @return int|false Timestamp or false if not found
253 */
254 public function get_last_activity() {
255 $user_id = $this->get_user_id();
256 if (!$user_id) {
257 return false;
258 }
259
260 $activity_key = $this->get_activity_transient_key();
261 return get_transient($activity_key);
262 }
263
264 /**
265 * Check if access has timed out due to inactivity
266 *
267 * @return bool True if timed out, false otherwise
268 */
269 public function is_timed_out() {
270 $last_activity = $this->get_last_activity();
271
272 if ($last_activity === false) {
273 return true;
274 }
275
276 $inactive_time = time() - $last_activity;
277 return $inactive_time > $this->transient_timeout;
278 }
279
280 /**
281 * Get transient key for this user and context
282 *
283 * @return string|false Returns transient key or false if no user ID available
284 */
285 private function get_transient_key($epoch = null) {
286 $user_id = $this->get_user_id();
287 if (!$user_id) {
288 return false;
289 }
290 $epoch = $epoch === null ? self::get_revocation_epoch($this->context) : (int) $epoch;
291 return 'metasync_auth_' . $this->context . '_' . $epoch . '_' . $user_id;
292 }
293
294 /**
295 * Get activity transient key for this user and context
296 *
297 * @return string|false Returns transient key or false if no user ID available
298 */
299 private function get_activity_transient_key($epoch = null) {
300 $user_id = $this->get_user_id();
301 if (!$user_id) {
302 return false;
303 }
304 $epoch = $epoch === null ? self::get_revocation_epoch($this->context) : (int) $epoch;
305 return 'metasync_activity_' . $this->context . '_' . $epoch . '_' . $user_id;
306 }
307
308 private static function get_revocation_epoch($context) {
309 $epoch = (int) get_option('metasync_auth_revoke_epoch_' . sanitize_key($context), 1);
310 return $epoch > 0 ? $epoch : 1;
311 }
312
313 private function revoke_epoch_transient_access($epoch) {
314 $user_id = $this->get_user_id();
315 if (!$user_id) return;
316 delete_transient('metasync_auth_' . $this->context . '_' . $epoch . '_' . $user_id);
317 delete_transient('metasync_activity_' . $this->context . '_' . $epoch . '_' . $user_id);
318 }
319
320 /**
321 * Get authentication status information
322 *
323 * @return array Status information
324 */
325 public function get_status() {
326 $user_id = $this->get_user_id();
327 if (!$user_id) {
328 return array(
329 'authenticated' => false,
330 'user_id' => null,
331 'context' => $this->context,
332 'error' => 'No user logged in'
333 );
334 }
335
336 return array(
337 'authenticated' => $this->has_access(),
338 'user_id' => $user_id,
339 'context' => $this->context,
340 'persistent_access' => $this->has_persistent_access(),
341 'transient_access' => $this->has_transient_access(),
342 'last_activity' => $this->get_last_activity(),
343 'is_timed_out' => $this->is_timed_out(),
344 'transient_timeout' => $this->transient_timeout,
345 'user_meta_key' => $this->user_meta_key
346 );
347 }
348
349 /**
350 * Verify password and grant access
351 *
352 * @param string $password Password to verify
353 * @param string|array $valid_passwords Valid password(s) to check against
354 * @param bool $persistent Whether to grant persistent access
355 * @return bool True if password is valid and access granted, false otherwise
356 */
357 public function verify_and_grant($password, $valid_passwords, $persistent = false) {
358 if ($this->context === 'whitelabel' && $persistent) {
359 return false;
360 }
361 $epoch_before = $this->context === 'whitelabel' ? self::get_revocation_epoch($this->context) : 0;
362 if (!is_array($valid_passwords)) {
363 $valid_passwords = array($valid_passwords);
364 }
365
366 // Remove empty passwords
367 $valid_passwords = array_filter($valid_passwords);
368
369 if (empty($valid_passwords)) {
370 return false;
371 }
372
373 // Check if password matches any valid password
374 $password_valid = in_array($password, $valid_passwords, true);
375
376 if ($password_valid) {
377 if ($this->context === 'whitelabel' && $epoch_before !== self::get_revocation_epoch($this->context)) {
378 return false;
379 }
380 $granted = $persistent ? $this->grant_persistent_access() : $this->grant_transient_access($this->context === 'whitelabel' ? $epoch_before : null);
381 if ($granted && $this->context === 'whitelabel' && $epoch_before !== self::get_revocation_epoch($this->context)) {
382 $this->revoke_epoch_transient_access($epoch_before);
383 return false;
384 }
385 return $granted;
386 }
387
388 return false;
389 }
390
391 /**
392 * Static helper: Grant persistent access to a user
393 *
394 * @param int $user_id User ID
395 * @param string $context Authentication context
396 * @return bool True on success, false on failure
397 */
398 public static function grant_user_access($user_id, $context = 'default') {
399 $user_meta_key = 'metasync_' . sanitize_key($context) . '_access';
400 return update_user_meta($user_id, $user_meta_key, 'granted') !== false;
401 }
402
403 /**
404 * Static helper: Revoke persistent access from a user
405 *
406 * @param int $user_id User ID
407 * @param string $context Authentication context
408 * @return bool True on success, false on failure
409 */
410 public static function revoke_user_access($user_id, $context = 'default') {
411 $user_meta_key = 'metasync_' . sanitize_key($context) . '_access';
412 return delete_user_meta($user_id, $user_meta_key);
413 }
414
415 /** Revoke persistent and transient grants for every user on this site. */
416 public static function revoke_all_access($context = 'default') {
417 $context = sanitize_key($context);
418 $meta_key = 'metasync_' . $context . '_access';
419 $users = function_exists('get_users') ? get_users(array('fields' => 'ID', 'meta_key' => $meta_key)) : array();
420 $success = true;
421 foreach ($users as $user_id) {
422 if (!delete_user_meta($user_id, $meta_key)) {
423 $success = false;
424 }
425 }
426 // Bump the namespace so transient-only grants from every user are invalidated.
427 if (!update_option('metasync_auth_revoke_epoch_' . $context, self::get_revocation_epoch($context) + 1, false)) {
428 $success = false;
429 }
430 return $success;
431 }
432
433 /**
434 * Static helper: Check if user has access
435 *
436 * @param int $user_id User ID
437 * @param string $context Authentication context
438 * @return bool True if user has access, false otherwise
439 */
440 public static function user_has_access($user_id, $context = 'default') {
441 $auth = new self($context);
442 return $auth->has_access();
443 }
444 }
445