PluginProbe ʕ •ᴥ•ʔ
JetBackup – Backup, Restore & Migrate / trunk
JetBackup – Backup, Restore & Migrate vtrunk
3.1.22.3 1.4.3 1.4.4 1.4.5 1.4.6 1.4.7 1.4.8 1.4.8.1 1.4.9 1.5.0 1.5.1 1.5.1.1 1.5.2 1.5.3 1.5.4 1.5.5 1.5.6 1.5.7 1.5.8 1.6.0 1.6.10 1.6.11 1.6.12 1.6.13 1.6.15 1.6.5.1 1.6.8.8 1.6.9 1.6.9.1 2.0.3 2.0.4 2.0.5 2.0.6 2.0.7.5 2.0.8.7 2.0.9.11 2.0.9.14 2.0.9.15 2.0.9.6 2.0.9.7 2.0.9.9 3.1.10.7 3.1.11.1 3.1.12.3 3.1.13.4 3.1.14.17 3.1.15.4 3.1.16.1 3.1.17.5 3.1.18.10 3.1.18.8 3.1.18.9 3.1.19.8 3.1.20.3 3.1.21.3 3.1.7.9 3.1.9.2 trunk 1.1.90 1.1.91 1.2.0 1.2.5 1.2.6 1.2.7 1.2.8 1.2.9 1.3.0 1.3.1 1.3.2 1.3.3 1.3.4 1.3.6 1.3.7 1.3.8 1.3.9 1.4.0 1.4.1 1.4.2
backup / src / JetBackup / Wordpress / Abilities.php
backup / src / JetBackup / Wordpress Last commit date
.htaccess 1 year ago Abilities.php 1 day ago Blog.php 1 year ago Helper.php 5 months ago Init.php 5 months ago Installer.php 7 months ago MySQL.php 1 year ago UI.php 4 months ago Update.php 1 year ago Wordpress.php 1 day ago index.html 1 year ago web.config 1 year ago
Abilities.php
209 lines
1 <?php
2
3 namespace JetBackup\Wordpress;
4
5 use JetBackup\Ajax\iAjax;
6 use JetBackup\Exception\JBException;
7 use JetBackup\Factory;
8 use JetBackup\JetBackup;
9
10 if (!defined( '__JETBACKUP__')) die('Direct access is not allowed');
11
12 class Abilities {
13
14 const CAPABILITY = 'manage_options';
15
16 const CATEGORY = 'jetbackup';
17 const CATEGORY_LABEL = 'JetBackup';
18 const CATEGORY_DESCRIPTION = 'JetBackup backup, restore and status capabilities.';
19
20 const INPUT_NONE = 0;
21 const INPUT_PAGINATION = 1;
22 const INPUT_ID = 2;
23
24 const KEY_SKIP = 'skip';
25 const KEY_LIMIT = 'limit';
26 const LIMIT_DEFAULT = 50;
27 const LIMIT_MAX = 100;
28
29 const ARG_LABEL = 'label';
30 const ARG_DESCRIPTION = 'description';
31 const ARG_CATEGORY = 'category';
32 const ARG_INPUT_SCHEMA = 'input_schema';
33 const ARG_OUTPUT_SCHEMA = 'output_schema';
34 const ARG_EXECUTE_CALLBACK = 'execute_callback';
35 const ARG_PERMISSION_CALLBACK = 'permission_callback';
36 const ARG_META = 'meta';
37 const ARG_SHOW_IN_REST = 'show_in_rest';
38 const ARG_MCP = 'mcp';
39 const ARG_PUBLIC = 'public';
40
41 const SCHEMA_TYPE = 'type';
42 const SCHEMA_PROPERTIES = 'properties';
43 const SCHEMA_ADDITIONAL_PROPERTIES = 'additionalProperties';
44 const SCHEMA_DEFAULT = 'default';
45 const SCHEMA_MINIMUM = 'minimum';
46 const SCHEMA_MAXIMUM = 'maximum';
47 const SCHEMA_TYPE_OBJECT = 'object';
48 const SCHEMA_TYPE_INTEGER = 'integer';
49
50 const RESPONSE_MESSAGE = 'message';
51 const RESPONSE_DATA = 'data';
52
53 const ERROR_INVALID = 'jetbackup_invalid_ability';
54 const ERROR_FAILED = 'jetbackup_ability_failed';
55 const MESSAGE_FAILED = 'JetBackup ability execution failed';
56
57 const SANITIZE_OUTPUT = [
58 'getSystemInfo' => ['secured_dir', 'data_dir', 'wordpress_path', 'jetbackup_data_dir'],
59 ];
60
61 const ABILITIES = [
62 'listBackups' => ['List Backups', 'List available backup snapshots.', self::INPUT_PAGINATION],
63 'listBackupJobs' => ['List Backup Jobs', 'List configured backup jobs.', self::INPUT_PAGINATION],
64 'listDestinations' => ['List Destinations', 'List configured backup destinations.', self::INPUT_PAGINATION],
65 'listSchedules' => ['List Schedules', 'List configured backup schedules.', self::INPUT_PAGINATION],
66 'listQueueItems' => ['List Queue Items', 'List queue items.', self::INPUT_PAGINATION],
67 'getBackup' => ['Get Backup', 'Get a single backup snapshot by id.', self::INPUT_ID],
68 'getBackupJob' => ['Get Backup Job', 'Get a single backup job by id.', self::INPUT_ID],
69 'getQueueItem' => ['Get Queue Item', 'Get a single queue item by id.', self::INPUT_ID],
70 'getDashboard' => ['Get Dashboard', 'Get the dashboard summary.', self::INPUT_NONE],
71 'getSystemInfo' => ['Get System Info', 'Get system information.', self::INPUT_NONE],
72 ];
73
74 private function __construct() {}
75
76 public static function registerCategories(): void {
77
78 try {
79 if (!Factory::getSettingsSecurity()->isAbilitiesEnabled()) return;
80 } catch (\Throwable $e) {
81 return;
82 }
83
84 Wordpress::registerAbilityCategory(self::CATEGORY, [
85 self::ARG_LABEL => self::CATEGORY_LABEL,
86 self::ARG_DESCRIPTION => self::CATEGORY_DESCRIPTION,
87 ]);
88 }
89
90 public static function register(): void {
91
92 try {
93 if (!Factory::getSettingsSecurity()->isAbilitiesEnabled()) return;
94 } catch (\Throwable $e) {
95 return;
96 }
97
98 foreach (self::ABILITIES as $action => $ability) {
99
100 [$label, $description, $input] = $ability;
101
102 Wordpress::registerAbility(self::_name($action), [
103 self::ARG_LABEL => $label,
104 self::ARG_DESCRIPTION => $description,
105 self::ARG_CATEGORY => self::CATEGORY,
106 self::ARG_INPUT_SCHEMA => self::_inputSchema($input),
107 self::ARG_OUTPUT_SCHEMA => [self::SCHEMA_TYPE => self::SCHEMA_TYPE_OBJECT],
108 self::ARG_EXECUTE_CALLBACK => static function ($input=[]) use ($action) { return self::_execute($action, is_array($input) ? $input : []); },
109 self::ARG_PERMISSION_CALLBACK => [self::class, 'permission'],
110 self::ARG_META => [self::ARG_SHOW_IN_REST => true, self::ARG_MCP => [self::ARG_PUBLIC => true]],
111 ]);
112 }
113 }
114
115 private static function _name(string $action): string {
116 return self::CATEGORY . '/' . strtolower(preg_replace('/([a-z0-9])([A-Z])/', '$1-$2', $action));
117 }
118
119 public static function permission(): bool {
120 try {
121 if (!function_exists('current_user_can') || !function_exists('is_user_logged_in')) return false;
122 if (!is_user_logged_in()) return false;
123 if (!current_user_can(self::CAPABILITY)) return false;
124 if (Helper::isMultisite() && !Helper::isNetworkAdminUser()) return false;
125
126 $security = Factory::getSettingsSecurity();
127 if (!$security->isAbilitiesEnabled()) return false;
128 if ($security->isMFAEnabled() && !UI::validateMFA() && !$security->isMFAAllowAbilities()) return false;
129
130 return true;
131
132 } catch (\Throwable $e) {
133 if (Wordpress::isDebugModeEnabled()) error_log('[JetBackup] ability permission check failed: ' . $e->getMessage());
134 return false;
135 }
136 }
137
138 private static function _inputSchema(int $input): array {
139
140 $properties = [];
141
142 if ($input === self::INPUT_PAGINATION) $properties = [
143 self::KEY_SKIP => [self::SCHEMA_TYPE => self::SCHEMA_TYPE_INTEGER, self::SCHEMA_MINIMUM => 0],
144 self::KEY_LIMIT => [
145 self::SCHEMA_TYPE => self::SCHEMA_TYPE_INTEGER,
146 self::SCHEMA_MINIMUM => 1,
147 self::SCHEMA_MAXIMUM => self::LIMIT_MAX,
148 self::SCHEMA_DEFAULT => self::LIMIT_DEFAULT,
149 ],
150 ];
151 else if ($input === self::INPUT_ID) $properties = [
152 JetBackup::ID_FIELD => [self::SCHEMA_TYPE => self::SCHEMA_TYPE_INTEGER, self::SCHEMA_MINIMUM => 1],
153 ];
154
155 return [
156 self::SCHEMA_TYPE => self::SCHEMA_TYPE_OBJECT,
157 self::SCHEMA_PROPERTIES => $properties,
158 self::SCHEMA_ADDITIONAL_PROPERTIES => false,
159 self::SCHEMA_DEFAULT => (object) [],
160 ];
161 }
162
163 private static function _execute(string $action, array $input) {
164
165 $class = "\\JetBackup\\Ajax\\Calls\\" . ucfirst($action);
166
167 if (!class_exists($class)) return new \WP_Error(self::ERROR_INVALID, 'Unknown JetBackup ability', ['status' => 404]);
168
169 try {
170 /** @var iAjax $call */
171 $call = new $class();
172 $call->setData(self::_boundInput($action, $input));
173 $call->execute();
174
175 $data = $call->getResponseData();
176 if (isset(self::SANITIZE_OUTPUT[$action])) $data = self::_sanitize($data, self::SANITIZE_OUTPUT[$action]);
177
178 return [
179 self::RESPONSE_MESSAGE => $call->getResponseMessage(),
180 self::RESPONSE_DATA => $data,
181 ];
182
183 } catch (JBException $e) {
184 if (Wordpress::isDebugModeEnabled()) error_log('[JetBackup] ability failed (' . $action . '): ' . $e->getMessage());
185 return new \WP_Error(self::ERROR_FAILED, self::MESSAGE_FAILED, ['status' => 400]);
186 } catch (\Throwable $e) {
187 if (Wordpress::isDebugModeEnabled()) error_log('[JetBackup] ability error (' . $action . '): ' . $e->getMessage());
188 return new \WP_Error(self::ERROR_FAILED, self::MESSAGE_FAILED, ['status' => 500]);
189 }
190 }
191
192 private static function _boundInput(string $action, array $input): array {
193 if ((self::ABILITIES[$action][2] ?? self::INPUT_NONE) !== self::INPUT_PAGINATION) return $input;
194 $limit = isset($input[self::KEY_LIMIT]) ? (int) $input[self::KEY_LIMIT] : self::LIMIT_DEFAULT;
195 if ($limit < 1) $limit = self::LIMIT_DEFAULT;
196 if ($limit > self::LIMIT_MAX) $limit = self::LIMIT_MAX;
197 $input[self::KEY_LIMIT] = $limit;
198 return $input;
199 }
200
201 private static function _sanitize(array $data, array $keys): array {
202 foreach ($data as $key => $value) {
203 if (in_array($key, $keys, true)) { unset($data[$key]); continue; }
204 if (is_array($value)) $data[$key] = self::_sanitize($value, $keys);
205 }
206 return $data;
207 }
208 }
209