PluginProbe
Search Atlas SEO – OTTO AI SEO Automation for WordPress / 2.7.1
Search Atlas SEO – OTTO AI SEO Automation for WordPress v2.7.1
2.7.1 2.7.2 2.7.0 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 All 141 releases
← All changes | includes/class-metasync-auth-manager.php +63 -15 2.6.16 → 2.7.1 View file →
@@ -100,8 +100,12 @@
100 100 }
101 101
102 102 // Check temporary access (transient)
103 103 if ($this->has_transient_access()) {
104 + // Sliding window: renew the access window itself, not just the
105 + // decorative activity timestamp, so an admin actively using the
106 + // tab is not cut off exactly at grant-time + timeout.
107 + set_transient($this->get_transient_key(), 'granted', $this->transient_timeout);
104 108 // Refresh transient activity
105 109 $this->update_activity();
106 110 return true;
107 111 }
@@ -163,19 +167,19 @@
163 167 * Grant temporary access (transient)
164 168 *
165 169 * @return bool True on success, false on failure
166 170 */
167 - public function grant_transient_access() {
171 + public function grant_transient_access($epoch = null) {
168 172 $user_id = $this->get_user_id();
169 173 if (!$user_id) {
170 174 return false;
171 175 }
172 176
173 - $transient_key = $this->get_transient_key();
177 + $transient_key = $this->get_transient_key($epoch);
174 178 $result = set_transient($transient_key, 'granted', $this->transient_timeout);
175 179
176 180 // Set activity timestamp
177 - $this->update_activity();
181 + $this->update_activity($epoch);
178 182
179 183 return $result;
180 184 }
181 185
@@ -235,15 +239,15 @@
235 239 * Update last activity timestamp
236 240 *
237 241 * @return bool True on success, false on failure
238 242 */
239 - private function update_activity() {
243 + private function update_activity($epoch = null) {
240 244 $user_id = $this->get_user_id();
241 245 if (!$user_id) {
242 246 return false;
243 247 }
244 248
245 - $activity_key = $this->get_activity_transient_key();
249 + $activity_key = $this->get_activity_transient_key($epoch);
246 250 return set_transient($activity_key, time(), $this->transient_timeout);
247 251 }
248 252
249 253 /**
@@ -281,14 +285,15 @@
281 285 * Get transient key for this user and context
282 286 *
283 287 * @return string|false Returns transient key or false if no user ID available
284 288 */
285 - private function get_transient_key() {
289 + private function get_transient_key($epoch = null) {
286 290 $user_id = $this->get_user_id();
287 291 if (!$user_id) {
288 292 return false;
289 293 }
290 - return 'metasync_auth_' . $this->context . '_' . $user_id;
294 + $epoch = $epoch === null ? self::get_revocation_epoch($this->context) : (int) $epoch;
295 + return 'metasync_auth_' . $this->context . '_' . $epoch . '_' . $user_id;
291 296 }
292 297
293 298 /**
294 299 * Get activity transient key for this user and context
@@ -294,16 +299,29 @@
294 299 * Get activity transient key for this user and context
295 300 *
296 301 * @return string|false Returns transient key or false if no user ID available
297 302 */
298 - private function get_activity_transient_key() {
303 + private function get_activity_transient_key($epoch = null) {
299 304 $user_id = $this->get_user_id();
300 305 if (!$user_id) {
301 306 return false;
302 307 }
303 - return 'metasync_activity_' . $this->context . '_' . $user_id;
308 + $epoch = $epoch === null ? self::get_revocation_epoch($this->context) : (int) $epoch;
309 + return 'metasync_activity_' . $this->context . '_' . $epoch . '_' . $user_id;
304 310 }
305 311
312 + private static function get_revocation_epoch($context) {
313 + $epoch = (int) get_option('metasync_auth_revoke_epoch_' . sanitize_key($context), 1);
314 + return $epoch > 0 ? $epoch : 1;
315 + }
316 +
317 + private function revoke_epoch_transient_access($epoch) {
318 + $user_id = $this->get_user_id();
319 + if (!$user_id) return;
320 + delete_transient('metasync_auth_' . $this->context . '_' . $epoch . '_' . $user_id);
321 + delete_transient('metasync_activity_' . $this->context . '_' . $epoch . '_' . $user_id);
322 + }
323 +
306 324 /**
307 325 * Get authentication status information
308 326 *
309 327 * @return array Status information
@@ -340,14 +358,22 @@
340 358 * @param bool $persistent Whether to grant persistent access
341 359 * @return bool True if password is valid and access granted, false otherwise
342 360 */
343 361 public function verify_and_grant($password, $valid_passwords, $persistent = false) {
362 + if ($this->context === 'whitelabel' && $persistent) {
363 + return false;
364 + }
365 + $epoch_before = $this->context === 'whitelabel' ? self::get_revocation_epoch($this->context) : 0;
344 366 if (!is_array($valid_passwords)) {
345 367 $valid_passwords = array($valid_passwords);
346 368 }
347 369
348 - // Remove empty passwords
349 - $valid_passwords = array_filter($valid_passwords);
370 + // Remove unset/empty passwords, but keep the string '0' — a
371 + // truthiness filter would silently drop a configured password of
372 + // "0" and make it impossible to authenticate with.
373 + $valid_passwords = array_filter($valid_passwords, function ($p) {
374 + return null !== $p && '' !== $p;
375 + });
350 376
351 377 if (empty($valid_passwords)) {
352 378 return false;
353 379 }
@@ -355,13 +381,17 @@
355 381 // Check if password matches any valid password
356 382 $password_valid = in_array($password, $valid_passwords, true);
357 383
358 384 if ($password_valid) {
359 - if ($persistent) {
360 - return $this->grant_persistent_access();
361 - } else {
362 - return $this->grant_transient_access();
385 + if ($this->context === 'whitelabel' && $epoch_before !== self::get_revocation_epoch($this->context)) {
386 + return false;
363 387 }
388 + $granted = $persistent ? $this->grant_persistent_access() : $this->grant_transient_access($this->context === 'whitelabel' ? $epoch_before : null);
389 + if ($granted && $this->context === 'whitelabel' && $epoch_before !== self::get_revocation_epoch($this->context)) {
390 + $this->revoke_epoch_transient_access($epoch_before);
391 + return false;
392 + }
393 + return $granted;
364 394 }
365 395
366 396 return false;
367 397 }
@@ -387,8 +417,26 @@
387 417 */
388 418 public static function revoke_user_access($user_id, $context = 'default') {
389 419 $user_meta_key = 'metasync_' . sanitize_key($context) . '_access';
390 420 return delete_user_meta($user_id, $user_meta_key);
421 + }
422 +
423 + /** Revoke persistent and transient grants for every user on this site. */
424 + public static function revoke_all_access($context = 'default') {
425 + $context = sanitize_key($context);
426 + $meta_key = 'metasync_' . $context . '_access';
427 + $users = function_exists('get_users') ? get_users(array('fields' => 'ID', 'meta_key' => $meta_key)) : array();
428 + $success = true;
429 + foreach ($users as $user_id) {
430 + if (!delete_user_meta($user_id, $meta_key)) {
431 + $success = false;
432 + }
433 + }
434 + // Bump the namespace so transient-only grants from every user are invalidated.
435 + if (!update_option('metasync_auth_revoke_epoch_' . $context, self::get_revocation_epoch($context) + 1, false)) {
436 + $success = false;
437 + }
438 + return $success;
391 439 }
392 440
393 441 /**
394 442 * Static helper: Check if user has access