PluginProbe
FluentAuth – The Ultimate Authorization & Security Plugin for WordPress / trunk
FluentAuth – The Ultimate Authorization & Security Plugin for WordPress vtrunk
trunk 1.0.0 1.0.1 1.0.2 1.0.3 1.0.4 1.0.5 1.0.6 1.0.7 1.0.8 1.1.0 2.0.0 2.0.1 2.0.2 2.0.3 2.1.0 2.1.1 2.1.2
fluent-security / app / Http / Controllers / SettingsController.php

SettingsController.php in FluentAuth – The Ultimate Authorization & Security Plugin for WordPress trunk, at app/Http/Controllers/SettingsController.php

575 lines 20.3 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 <?php
2
3 namespace FluentAuth\App\Http\Controllers;
4
5 use FluentAuth\App\Helpers\Arr;
6 use FluentAuth\App\Helpers\Helper;
7 use FluentAuth\App\Hooks\Handlers\ServerModeHandler;
8
9 class SettingsController
10 {
11 public static function getSettings(\WP_REST_Request $request)
12 {
13 return [
14 'settings' => Helper::getAuthSettings(),
15 'user_roles' => Helper::getUserRoles(),
16 'low_level_roles' => Helper::getLowLevelRoles()
17 ];
18 }
19
20 public static function updateSettings(\WP_REST_Request $request)
21 {
22 $settings = self::validateSettings($request->get_param('settings'));
23 if (is_wp_error($settings)) {
24 return $settings;
25 }
26
27 update_option('__fls_auth_settings', $settings, false);
28
29 return [
30 'settings' => $settings,
31 'message' => __('Settings has been updated', 'fluent-security')
32 ];
33 }
34
35 private static function validateSettings($settings)
36 {
37 $oldSettings = Helper::getAuthSettings();
38 if (isset($settings['require_configuration'])) {
39 unset($settings['require_configuration']);
40 }
41
42 $settings = Arr::only($settings, array_keys($oldSettings));
43
44 $numericTypes = [
45 'auto_delete_logs_day',
46 'login_try_limit',
47 'login_try_timing'
48 ];
49
50 foreach ($settings as $settingKey => $setting) {
51 if (in_array($settingKey, $numericTypes)) {
52 $settings[$settingKey] = (int)$setting;
53 } else {
54 if (is_array($setting)) {
55 $settings[$settingKey] = map_deep($setting, 'sanitize_text_field');
56 } else {
57 $settings[$settingKey] = sanitize_text_field($setting);
58 }
59 }
60 }
61
62 $errors = [];
63
64 if ($settings['enable_auth_logs'] == 'yes') {
65 if (!$settings['login_try_limit']) {
66 $errors['login_try_limit'] = [
67 'required' => 'Login try limit is required'
68 ];
69 }
70 if (!$settings['login_try_timing']) {
71 $errors['login_try_timing'] = [
72 'required' => 'Login Timing is required'
73 ];
74 }
75 }
76
77 if ($settings['email2fa'] == 'yes' && empty($settings['email2fa_roles'])) {
78 $errors['email2fa_roles'] = [
79 'required' => 'Two-Factor Authentication roles is required'
80 ];
81 }
82
83 if ($errors) {
84 return new \WP_Error('validation_error', 'Form Validation failed', $errors);
85 }
86
87 return $settings;
88
89 }
90
91
92 public static function getAuthFormSettings(\WP_REST_Request $request)
93 {
94
95 $settings = Helper::getAuthFormsSettings();
96
97 return [
98 'settings' => $settings,
99 'roles' => Helper::getUserRoles(true),
100 'user_capabilities' => Helper::getWpPermissions(true)
101 ];
102 }
103
104 public static function saveAuthFormSettings(\WP_REST_Request $request)
105 {
106 $oldSettings = Helper::getAuthFormsSettings();
107 $settings = (array)$request->get_param('settings');
108
109 if (!$settings) {
110 $settings = (array)$request->get_param('redirect_settings');
111
112 $oldSettings['login_redirects'] = sanitize_text_field($settings['login_redirects']);
113
114 if (!empty($settings['default_login_redirect'])) {
115 $oldSettings['default_login_redirect'] = sanitize_url($settings['default_login_redirect']);
116 }
117
118 if (!empty($settings['default_logout_redirect'])) {
119 $oldSettings['default_logout_redirect'] = sanitize_url($settings['default_logout_redirect']);
120 }
121
122 $redirectRules = Arr::get($settings, 'redirect_rules', []);
123
124 $sanitizedRules = [];
125
126 if ($redirectRules) {
127 foreach ($redirectRules as $redirectIndex => $redirect) {
128 $item = [
129 'login' => '',
130 'logout' => ''
131 ];
132 if (!empty($redirect['login'])) {
133 $item['login'] = sanitize_url($redirect['login']);
134 }
135 if (!empty($redirect['logout'])) {
136 $item['logout'] = sanitize_url($redirect['logout']);
137 }
138 $conditions = $redirect['conditions'];
139 foreach ($conditions as $index => $condition) {
140 $conditions[$index] = map_deep($condition, 'sanitize_text_field');
141 }
142
143 $item['conditions'] = $conditions;
144
145 $sanitizedRules[] = $item;
146 }
147 }
148
149 $oldSettings['redirect_rules'] = $sanitizedRules;
150
151 } else {
152 $oldSettings['enabled'] = sanitize_text_field($settings['enabled']);
153 }
154
155 update_option('__fls_auth_forms_settings', $oldSettings, false);
156
157 return [
158 'message' => __('Settings has been updated', 'fluent-security'),
159 'settings' => $oldSettings
160 ];
161 }
162
163 public static function getAuthCustomizerSetting(\WP_REST_Request $request)
164 {
165 return [
166 'settings' => Helper::getAuthCustomizerSettings(),
167 'login_form_html' => ''
168 ];
169 }
170
171 public static function saveAuthCustomizerSetting(\WP_REST_Request $request)
172 {
173 $settings = (array)$request->get_param('settings');
174 $settings = Helper::formatAuthCustomizerSettings($settings);
175 update_option('__fls_auth_customizer_settings', $settings, false);
176
177 return [
178 'message' => __('Settings has been updated', 'fluent-security'),
179 'settings' => $settings
180 ];
181 }
182
183 public static function uploadImage(\WP_REST_Request $request)
184 {
185 $file = $_FILES['file'];
186 if (empty($file)) {
187 return new \WP_Error('invalid_file', __('Invalid file', 'fluent-security'));
188 }
189
190 // wp_check_filetype_and_ext() will look at both the file extension and the file's actual contents.
191 $checked = wp_check_filetype_and_ext(
192 $file['tmp_name'],
193 $file['name'],
194 null // we’ll supply our own list of allowed types below
195 );
196 $ext = $checked['ext'];
197 $type = $checked['type'];
198
199
200 $allowed_mimes = [
201 'jpg|jpeg|jpe' => 'image/jpeg',
202 'png' => 'image/png',
203 'gif' => 'image/gif',
204 'webp' => 'image/webp',
205 'bmp' => 'image/bmp',
206 ];
207
208 if (!in_array($type, $allowed_mimes, true)) {
209 return new \WP_Error(
210 'invalid_file_type',
211 __('Sorry, you can only upload JPG, PNG, GIF, WebP or BMP files.', 'fluent-security')
212 );
213 }
214
215 if (!function_exists('wp_handle_upload')) {
216 require_once ABSPATH . 'wp-admin/includes/file.php';
217 }
218
219 $upload_overrides = [
220 'test_form' => false,
221 // Pass the allowed list here so WP also enforces it
222 'mimes' => $allowed_mimes,
223 ];
224 $movefile = wp_handle_upload($file, $upload_overrides);
225
226 if (isset($movefile['error'])) {
227 return new \WP_Error('upload_error', $movefile['error']);
228 }
229
230 return [
231 'media' => $movefile,
232 ];
233 }
234
235
236 public static function saveChildSite(\WP_REST_Request $request)
237 {
238 if (!(new ServerModeHandler())->isEnabled()) {
239 return new \WP_Error('invalid_request', __('This feature is only available in the server mode.', 'fluent-security'));
240 }
241 $willRemove = $request->get_param('will_remove');
242 if ($willRemove == 'yes') {
243 $url = $request->get_param('site_url');
244
245 $prevSettings = get_option('__fls_child_sites', []);
246
247 $prevSettings = array_filter($prevSettings, function ($site) use ($url) {
248 return $site['site_url'] !== $url;
249 });
250
251 update_option('__fls_child_sites', $prevSettings, false);
252
253 return [
254 'message' => __('Site has been removed successfully', 'fluent-security')
255 ];
256 }
257
258 $siteConfig = trim($request->get_param('site_config'));
259
260 if (empty($siteConfig)) {
261 return new \WP_Error('invalid_request', __('Invalid request', 'fluent-security'));
262 }
263
264 $siteConfig = json_decode($siteConfig, true);
265
266 if (empty($siteConfig['site_url']) || empty($siteConfig['callback_url'])) {
267 return new \WP_Error('invalid_request', __('Invalid request', 'fluent-security'));
268 }
269
270 // validate the urls
271 if (!filter_var($siteConfig['site_url'], FILTER_VALIDATE_URL)) {
272 return new \WP_Error('invalid_request', __('Invalid site URL', 'fluent-security'));
273 }
274 if (!filter_var($siteConfig['callback_url'], FILTER_VALIDATE_URL)) {
275 return new \WP_Error('invalid_request', __('Invalid callback URL', 'fluent-security'));
276 }
277
278 $fomattedData = [
279 'site_url' => sanitize_url($siteConfig['site_url']),
280 'callback_url' => sanitize_url($siteConfig['callback_url']),
281 'title' => sanitize_text_field($siteConfig['site_title']),
282 'status' => 'yes'
283 ];
284
285 if (empty($fomattedData['title'])) {
286 $fomattedData['title'] = parse_url($fomattedData['site_url'], PHP_URL_HOST);
287 }
288
289 $previousSites = get_option('__fls_child_sites', []);
290
291 $siteId = strtolower(wp_generate_password(4, false));
292
293 while (isset($previousSites[$siteId])) {
294 $siteId = strtolower(wp_generate_password(4, false));
295 }
296
297 $fomattedData['site_id'] = $siteId;
298
299 // check if the site already exists
300 $existingSite = array_filter($previousSites, function ($site) use ($fomattedData) {
301 return $site['site_url'] === $fomattedData['site_url'];
302 });
303
304 if (!empty($existingSite)) {
305 return new \WP_Error('invalid_request', __('This site already exists.', 'fluent-security'));
306 }
307
308 $fomattedData['secret_key'] = wp_generate_password(32, false);
309 $previousSites[$fomattedData['site_id']] = $fomattedData;
310
311 update_option('__fls_child_sites', $previousSites, false);
312
313 $serverConfig = json_encode([
314 'server_token' => $fomattedData['secret_key'],
315 'callback' => rest_url('fluent-auth/child-sites/validate-token'),
316 'server_url' => site_url(),
317 'site_id' => $fomattedData['site_id']
318 ], JSON_UNESCAPED_SLASHES);
319
320 return [
321 'message' => __('Site has been added successfully', 'fluent-security'),
322 'server_token' => $serverConfig
323 ];
324 }
325
326 public static function getChildSites(\WP_REST_Request $request)
327 {
328
329 if (!(new ServerModeHandler())->isEnabled()) {
330 return new \WP_Error('invalid_request', __('This feature is only available in the server mode.', 'fluent-security'));
331 }
332
333 $sites = get_option('__fls_child_sites', []);
334
335 $formattedSites = [];
336 foreach ($sites as $site) {
337 $formattedSites[] = [
338 'title' => $site['title'],
339 'url' => $site['site_url'],
340 'site_id' => $site['site_id'],
341 ];
342 }
343
344 return [
345 'sites' => $formattedSites,
346 ];
347 }
348
349 public static function validateChildSiteToken(\WP_REST_Request $request)
350 {
351
352 $data = [
353 'user_token' => $request->get_param('user_token'),
354 'server_token' => $request->get_param('server_token'),
355 'site_id' => $request->get_param('site_id'),
356 ];
357
358 if (!is_string($data['user_token']) || !is_string($data['server_token']) || !is_string($data['site_id'])) {
359 return new \WP_Error('invalid_request', __('Invalid request', 'fluent-security'));
360 }
361
362 if (empty($data['user_token']) || empty($data['server_token']) || empty($data['site_id'])) {
363 return new \WP_Error('invalid_request', __('Invalid request', 'fluent-security'));
364 }
365
366 $sites = get_option('__fls_child_sites', []);
367 $site = Arr::get($sites, $data['site_id'], null);
368 if (empty($site)) {
369 return new \WP_Error('invalid_request', __('Invalid Site ID', 'fluent-security'));
370 }
371
372 if (!hash_equals($site['secret_key'], $data['server_token'])) {
373 return new \WP_Error('invalid_request', __('Invalid server token', 'fluent-security'));
374 }
375
376 $userToken = explode('___', $data['user_token']);
377
378 $userId = Arr::get($userToken, '1', null);
379
380 if (!$userId) {
381 return new \WP_Error('invalid_request', __('Invalid user token', 'fluent-security'));
382 }
383
384 $user = get_user_by('ID', $userId);
385 $userMeta = get_user_meta($userId, '__flsc_temp_token', true);
386
387 if (empty($user) || empty($userMeta) || !hash_equals($userMeta, $data['user_token'])) {
388 return new \WP_Error('invalid_request', __('Invalid user token', 'fluent-security'));
389 }
390
391 update_user_meta($userId, '__flsc_temp_token', '');
392
393 // now we will prepare the data for the user
394 $data = apply_filters('fluent_auth/remote_auth_response_data', [
395 'remote_user_id' => $user->ID,
396 'user_login' => $user->user_login,
397 'user_email' => $user->user_email,
398 'user_nicename' => $user->user_nicename,
399 'user_url' => $user->user_url,
400 'nickname' => $user->nickname,
401 'locale' => $user->locale,
402 'display_name' => $user->display_name,
403 'user_registered' => $user->user_registered,
404 'roles' => array_values($user->roles),
405 'first_name' => $user->first_name,
406 'last_name' => $user->last_name,
407 'description' => $user->description,
408 ], $user, $site);
409
410 return [
411 'user_data' => $data,
412 ];
413 }
414
415 public function installPlugin(\WP_REST_Request $request)
416 {
417 $plugin = $request->get_param('plugin');
418
419 if (!$plugin) {
420 return new \WP_Error('invalid_request', __('Invalid request', 'fluent-security'));
421 }
422
423 if (!current_user_can('install_plugins')) {
424 return new \WP_Error('permission_denied', __('You do not have permission to install plugins', 'fluent-security'), 403);
425 }
426
427 if (defined('FLUENTMAIL_PLUGIN_FILE')) {
428 return new \WP_Error('already_installed', __('FluentSMTP is already installed as part of Fluent Mail plugin.', 'fluent-security'));
429 }
430
431 $plugin_id = 'fluent-smtp';
432 $plugin = [
433 'name' => 'FluentSMTP',
434 'repo-slug' => 'fluent-smtp',
435 'file' => 'fluent-smtp.php',
436 ];
437
438 $this->backgroundInstaller($plugin, $plugin_id);
439
440 if (!defined('FLUENTMAIL_PLUGIN_FILE')) {
441 return new \WP_Error('installation_failed', __('Plugin installation failed. Please try again.', 'fluent-security'));
442 }
443
444 return [
445 'message' => __('FluentSMTP has been installed successfully', 'fluent-security'),
446 'settings_url' => admin_url('options-general.php?page=fluent-mail#/'),
447 ];
448 }
449
450
451 private function backgroundInstaller($plugin_to_install, $plugin_id)
452 {
453 if (!empty($plugin_to_install['repo-slug'])) {
454 require_once ABSPATH . 'wp-admin/includes/file.php';
455 require_once ABSPATH . 'wp-admin/includes/plugin-install.php';
456 require_once ABSPATH . 'wp-admin/includes/class-wp-upgrader.php';
457 require_once ABSPATH . 'wp-admin/includes/plugin.php';
458
459 WP_Filesystem();
460
461 $skin = new \Automatic_Upgrader_Skin();
462 $upgrader = new \WP_Upgrader($skin);
463 $installed_plugins = array_reduce(array_keys(\get_plugins()), array($this, 'associate_plugin_file'), array());
464 $plugin_slug = $plugin_to_install['repo-slug'];
465 $plugin_file = isset($plugin_to_install['file']) ? $plugin_to_install['file'] : $plugin_slug . '.php';
466 $installed = false;
467 $activate = false;
468
469 // See if the plugin is installed already.
470 if (isset($installed_plugins[$plugin_file])) {
471 $installed = true;
472 $activate = !is_plugin_active($installed_plugins[$plugin_file]);
473 }
474
475 // Install this thing!
476 if (!$installed) {
477 // Suppress feedback.
478 ob_start();
479
480 try {
481 $plugin_information = plugins_api(
482 'plugin_information',
483 array(
484 'slug' => $plugin_slug,
485 'fields' => array(
486 'short_description' => false,
487 'sections' => false,
488 'requires' => false,
489 'rating' => false,
490 'ratings' => false,
491 'downloaded' => false,
492 'last_updated' => false,
493 'added' => false,
494 'tags' => false,
495 'homepage' => false,
496 'donate_link' => false,
497 'author_profile' => false,
498 'author' => false,
499 ),
500 )
501 );
502
503 if (is_wp_error($plugin_information)) {
504 throw new \Exception($plugin_information->get_error_message());
505 }
506
507 if (!is_object($plugin_information) || empty($plugin_information->download_link)) {
508 throw new \Exception(__('Could not retrieve plugin download link.', 'fluent-security'));
509 }
510
511 $package = $plugin_information->download_link;
512 $download = $upgrader->download_package($package);
513
514 if (is_wp_error($download)) {
515 throw new \Exception($download->get_error_message());
516 }
517
518 $working_dir = $upgrader->unpack_package($download, true);
519
520 if (is_wp_error($working_dir)) {
521 throw new \Exception($working_dir->get_error_message());
522 }
523
524 $result = $upgrader->install_package(
525 array(
526 'source' => $working_dir,
527 'destination' => WP_PLUGIN_DIR,
528 'clear_destination' => false,
529 'abort_if_destination_exists' => false,
530 'clear_working' => true,
531 'hook_extra' => array(
532 'type' => 'plugin',
533 'action' => 'install',
534 ),
535 )
536 );
537
538 if (is_wp_error($result)) {
539 throw new \Exception($result->get_error_message());
540 }
541
542 $activate = true;
543
544 } catch (\Exception $e) {
545 }
546
547 // Discard feedback.
548 ob_end_clean();
549 }
550
551 wp_clean_plugins_cache();
552
553 // Activate this thing.
554 if ($activate) {
555 try {
556 $result = activate_plugin($installed ? $installed_plugins[$plugin_file] : $plugin_slug . '/' . $plugin_file);
557
558 if (is_wp_error($result)) {
559 throw new \Exception($result->get_error_message());
560 }
561 } catch (\Exception $e) {
562 }
563 }
564 }
565 }
566
567 private function associate_plugin_file($plugins, $key)
568 {
569 $path = explode('/', $key);
570 $filename = end($path);
571 $plugins[$filename] = $key;
572 return $plugins;
573 }
574 }
575