Jake Vanderwerf
2026-05-11 ac444cba221832c012c0435fdc8339fe9f37febb
inc/rest/PermissionHandler.php
@@ -330,6 +330,9 @@
      }
      if (!wp_verify_nonce($nonce, $action)) {
         error_log('[PermissionHandler] Validating nonce....');
         error_log('Nonce: '.print_r($nonce, true));
         error_log('Action: '.print_r($action, true));
         return new WP_Error(
            'invalid_nonce',
            'Invalid or expired security token',
@@ -345,36 +348,17 @@
    */
   public static function verifyActionNonce(WP_REST_Request $request, string $actionPrefix, string $header = 'X-Action-Nonce'): bool|WP_Error
   {
      $userId = $request->get_param('user') ?: get_current_user_id();
      $userId = absint($request->get_param('user'));
      if ($userId === 0) {
         return false;
      }
      $action = $actionPrefix . $userId;
      return self::verifyNonce($request, $action, $header);
   }
   /**
    * Combined permission check: user match + rate limit
    */
   public static function userMatchWithRateLimit(WP_REST_Request $request): bool|WP_Error
   {
      static $rateLimiter = null;
      if ($rateLimiter === null) {
         $rateLimiter = new RateLimiter();
      }
      // Check rate limit first
      if (!$rateLimiter->checkLimit($request)) {
         return new WP_Error(
            'rate_limit',
            'Too many requests. Please wait before trying again.',
            ['status' => 429]
         );
      }
      return self::userMatch($request);
   }
   /**
    * Create a custom permission callback combining multiple checks
    *
    * Usage:
@@ -390,9 +374,11 @@
               $check === 'admin' => self::isAdmin($request),
               $check === 'verified' => self::isVerified($request),
               $check === 'user' => self::userMatch($request),
               $check === 'nonce' => self::verifyNonce($request),
               is_array($check) && isset($check['role']) => self::hasRole($request, $check['role']),
               is_array($check) && isset($check['roles']) => self::hasAnyRole($request, $check['roles']),
               is_array($check) && isset($check['capability']) => self::hasCapability($request, $check['capability']),
               is_array($check) && isset($check['actionNonce']) => self::verifyActionNonce($request, $check['actionNonce']),
               is_callable($check) => $check($request),
               default => true,
            };
@@ -424,18 +410,19 @@
               $check === 'admin' => self::isAdmin($request),
               $check === 'verified' => self::isVerified($request),
               $check === 'user' => self::userMatch($request),
               $check === 'nonce' => self::verifyNonce($request),
               is_array($check) && isset($check['role']) => self::hasRole($request, $check['role']),
               is_array($check) && isset($check['roles']) => self::hasAnyRole($request, $check['roles']),
               is_array($check) && isset($check['capability']) => self::hasCapability($request, $check['capability']),
               is_array($check) && isset($check['actionNonce']) => self::verifyActionNonce($request, $check['actionNonce']),
               is_callable($check) => $check($request),
               default => false,
            };
            // If it's a successful check (true), pass
            if ($result === true) {
               return true;
            }
            // Track last error for reporting
            if (is_wp_error($result)) {
               $lastError = $result;
            }