PluginProbe
ThinkRank AI SEO – AI SEO Plugin for WordPress: Schema, XML Sitemaps, Meta Tags, Search Console & Local SEO / 2.1.0
ThinkRank AI SEO – AI SEO Plugin for WordPress: Schema, XML Sitemaps, Meta Tags, Search Console & Local SEO v2.1.0
2.7.0 2.6.0 2.5.0 2.4.0 2.3.0 2.2.0 2.1.1 2.1.0 2.0.2 2.0.1 2.0.0 1.32.0 1.31.0 1.30.0 1.29.0 1.28.0 1.27.0 1.26.0 1.25.0 trunk 1.0.0 1.0.1 1.0.2 1.1.0 1.10.0 All 48 releases
thinkrank / includes / core / class-role-manager.php

class-role-manager.php in ThinkRank AI SEO – AI SEO Plugin for WordPress: Schema, XML Sitemaps, Meta Tags, Search Console & Local SEO 2.1.0, at includes/core/class-role-manager.php

92 lines 3.1 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 <?php
2
3 declare(strict_types=1);
4
5 namespace ThinkRank\Core;
6
7 use WP_Error;
8
9 // Prevent direct access
10 if (!defined('ABSPATH')) {
11 exit;
12 }
13
14 /**
15 * Role Manager
16 *
17 * Wires up ThinkRank's role-based access control:
18 * - keeps the administrator role's capabilities in sync;
19 * - centrally guards the whole `/thinkrank/v1/` REST namespace via a single
20 * `rest_pre_dispatch` filter (route prefix → capability), so no per-endpoint
21 * permission callback needs to change.
22 *
23 * Menu-access gating (the admin page capability) and the SPA nav filtering are
24 * handled by the Admin manager and the React app respectively, both reading
25 * from {@see Capability_Manager}.
26 *
27 * @since 1.12.0
28 */
29 class Role_Manager {
30
31 /**
32 * Initialize hooks.
33 *
34 * @return void
35 */
36 public function init(): void {
37 add_action('init', [Capability_Manager::class, 'ensure']);
38 add_filter('rest_pre_dispatch', [$this, 'gate_rest'], 10, 3);
39 }
40
41 /**
42 * Central capability gate for all ThinkRank REST routes.
43 *
44 * @param mixed $result Existing short-circuit result (or null).
45 * @param \WP_REST_Server $server REST server.
46 * @param \WP_REST_Request $request The request.
47 * @return mixed Null/array to proceed, or WP_Error to block.
48 */
49 public function gate_rest($result, $server, $request) {
50 // Respect an earlier short-circuit.
51 if (null !== $result) {
52 return $result;
53 }
54
55 $route = (string) $request->get_route();
56 // Gate both the free (/thinkrank/v1/) and Pro (/thinkrank-pro/v1/)
57 // namespaces so the Role Manager governs Pro sections too — otherwise the
58 // whole Pro namespace bypasses the capability gate.
59 if (strpos($route, '/thinkrank/v1/') !== 0 && strpos($route, '/thinkrank-pro/v1/') !== 0) {
60 return $result;
61 }
62
63 // MCP + OAuth routes authenticate INSIDE their handlers (Bearer token /
64 // OAuth access token — server-to-server calls with no logged-in user),
65 // so the namespace-wide capability gate must not touch them. The MCP
66 // management routes (/mcp/connection, /mcp/connect, …) stay gated. See
67 // ThinkRank\Mcp\Mcp_Manager.
68 if ('/thinkrank/v1/mcp' === $route || strpos($route, '/thinkrank/v1/mcp/oauth/') === 0) {
69 return $result;
70 }
71
72 if (!Capability_Manager::current_user_can(Capability_Manager::ACCESS)) {
73 return new WP_Error(
74 'thinkrank_forbidden',
75 __('You do not have permission to access ThinkRank.', 'thinkrank'),
76 ['status' => rest_authorization_required_code()]
77 );
78 }
79
80 $capability = Capability_Manager::capability_for_route($route);
81 if ($capability !== Capability_Manager::ACCESS && !Capability_Manager::current_user_can($capability)) {
82 return new WP_Error(
83 'thinkrank_forbidden_section',
84 __('You do not have permission to access this ThinkRank section.', 'thinkrank'),
85 ['status' => rest_authorization_required_code()]
86 );
87 }
88
89 return $result;
90 }
91 }
92