PluginProbe ʕ •ᴥ•ʔ
WP 2FA – Two-factor authentication for WordPress / 1.7.0
WP 2FA – Two-factor authentication for WordPress v1.7.0
4.1.0 4.0.0 1.7.1 2.0.0 2.0.1 2.1.0 2.2.0 2.2.1 2.3.0 2.4.0 2.4.1 2.4.2 2.5.0 2.6.0 2.6.1 2.6.2 2.6.3 2.6.4 2.7.0 2.8.0 2.9.0 2.9.1 2.9.2 2.9.3 3.0.0 3.0.1 3.1.0 3.1.1 3.1.1.2 trunk 1.2.0 1.3.0 1.4.0 1.4.1 1.4.2 1.5.0 1.5.1 1.5.2 1.6.0 1.6.1 1.6.2 1.7.0
wp-2fa / includes / classes / Admin / SettingsPage.php
wp-2fa / includes / classes / Admin Last commit date
Controllers 5 years ago Views 5 years ago SettingsPage.php 5 years ago SetupWizard.php 5 years ago User.php 5 years ago UserListing.php 5 years ago UserNotices.php 5 years ago UserProfile.php 5 years ago UserRegistered.php 5 years ago index.php 5 years ago
SettingsPage.php
1701 lines
1 <?php // phpcs:ignore
2
3 namespace WP2FA\Admin;
4
5 use WP2FA\EmailTemplate;
6 use \WP2FA\WP2FA as WP2FA;
7 use WP2FA\Utils\UserUtils;
8 use WP2FA\Admin\Controllers\Settings;
9 use WP2FA\Authenticator\Authentication;
10 use \WP2FA\Utils\Debugging as Debugging;
11 use WP2FA\Admin\Views\FirstTimeWizardSteps;
12 use WP2FA\Utils\SettingsUtils as SettingsUtils;
13 use \WP2FA\Utils\GenerateModal as GenerateModal;
14
15 /**
16 * SettingsPage - Class for handling settings
17 */
18 class SettingsPage {
19
20 /**
21 * Holds the status of the backupcodes functionality
22 *
23 * @var bool
24 */
25 private static $backupCodesEnabled = null;
26
27 /**
28 * Create admin menu entru and settings page
29 */
30 public function create_settings_admin_menu() {
31 // Create admin menu item.
32 add_menu_page(
33 esc_html__( 'WP 2FA Settings', 'wp-2fa' ),
34 esc_html__( 'WP 2FA', 'wp-2fa' ),
35 'manage_options',
36 'wp-2fa-settings',
37 [
38 $this,
39 'settings_page_render',
40 ],
41 "data:image/svg+xml;base64,".base64_encode(file_get_contents(WP_2FA_PATH . 'dist/images/wp-2fa-white-icon20x28.svg')),
42 81
43 );
44 // Register our settings page.
45 register_setting(
46 WP_2FA_SETTINGS_NAME,
47 WP_2FA_SETTINGS_NAME,
48 array( $this, 'validate_and_sanitize' )
49 );
50
51 register_setting(
52 WP_2FA_EMAIL_SETTINGS_NAME,
53 WP_2FA_EMAIL_SETTINGS_NAME,
54 array( $this, 'validate_and_sanitize_email' )
55 );
56 }
57
58 /**
59 * Create admin menu entru and settings page
60 */
61 public function create_settings_admin_menu_multisite() {
62 // Create admin menu item.
63 add_menu_page(
64 esc_html__( 'WP 2FA Settings', 'wp-2fa' ),
65 esc_html__( 'WP 2FA', 'wp-2fa' ),
66 'manage_options',
67 'wp-2fa-settings',
68 [
69 $this,
70 'settings_page_render',
71 ],
72 "data:image/svg+xml;base64,".base64_encode(file_get_contents(WP_2FA_PATH . 'dist/images/wp-2fa-white-icon20x28.svg')),
73 81
74 );
75 }
76
77 /**
78 * Render the settings
79 */
80 public function settings_page_render() {
81
82 $user = wp_get_current_user();
83 if ( ! empty( WP2FA::get_wp2fa_setting( '2fa_settings_last_updated_by' ) ) ) {
84 $main_user = (int) WP2FA::get_wp2fa_setting( '2fa_settings_last_updated_by' );
85 } else {
86 $main_user = '';
87 }
88
89 // Check if new user page has been published.
90 if ( ! empty( get_transient( WP_2FA_PREFIX . 'new_custom_page_created' ) ) ) {
91 delete_transient( WP_2FA_PREFIX . 'new_custom_page_created' );
92 $new_page_id = WP2FA::get_wp2fa_setting( 'custom-user-page-id' );
93 $new_page_permalink = get_permalink( $new_page_id );
94
95 $new_page_modal_content = '<h3>'.esc_html__( 'The plugin created the 2FA settings page with the URL:', 'wp-2fa' ).'</h3>';
96 $new_page_modal_content .= '<h4><a target="_blank" href="'.esc_url( $new_page_permalink ).'">'.esc_url( $new_page_permalink ).'</a></h4>';
97 $new_page_modal_content .= '<p>'.esc_html__( 'You can edit this page using the page editor, like you do with all other pages.', 'wp-2fa' );
98 $new_page_modal_content .= '</p>';
99 $new_page_modal_content .= sprintf(
100 esc_html__( 'Use the %s html tag in the email templates to include the URL of the 2FA configuration page when notifying the users to configure two-factor authentication.', 'wp-2fa' ),
101 '<strong>{2fa_settings_page_url}</strong>'
102 );
103 $new_page_modal_content .= '</p>';
104
105 echo GenerateModal::generate_modal(
106 'new-page-created',
107 false,
108 $new_page_modal_content,
109 [
110 '<a href="#" class="modal__btn modal__btn-primary button-primary" data-close-2fa-modal>'. __( 'OK', 'wp-2fa' ) .'</a>',
111 ],
112 true,
113 '560px'
114 );
115 }
116
117 $wp2faUser = new User( $user );
118 $enabled_methods = $wp2faUser->getEnabledMethods();
119
120 if ( empty( $enabled_methods ) ) {
121 $new_page_modal_content = '<h3>'.esc_html__( 'Exclude yourself?', 'wp-2fa' ).'</h3>';
122 $new_page_modal_content .= '</p>'. esc_html__( 'You are about to enforce 2FA instantly on all users, including yourself, however you have not yet configured your own 2FA method. What would you like to do?', 'wp-2fa' ) .'</p>';
123
124 echo GenerateModal::generate_modal(
125 'exclude-self-from-instant-2fa',
126 false,
127 $new_page_modal_content,
128 [
129 '<a href="#" class="modal__btn modal__btn-primary button-secondary" data-close-2fa-modal>'. __( 'Continue anyway', 'wp-2fa' ) .'</a>',
130 '<a href="#" class="modal__btn modal__btn-primary button-primary" data-close-2fa-modal data-user-login-name="'. esc_attr( $user->user_login ) .'">'. __( 'Exclude myself from 2FA policies', 'wp-2fa' ) .'</a>',
131 ],
132 false,
133 '560px'
134 );
135 }
136
137 $exclusions_modal_content = '<h3>'.esc_html__( 'Excluded Roles/Users still present', 'wp-2fa' ).'</h3>';
138 $exclusions_modal_content .= '</p>'. esc_html__( 'Changing this setting whislt you still have roles/users excluded will result in these being removed from the settngs.', 'wp-2fa' ) .'</p>';
139
140
141 echo GenerateModal::generate_modal(
142 'warn-exclusions-will-be-removed',
143 false,
144 $exclusions_modal_content,
145 [
146 '<a href="#" class="modal__btn modal__btn-primary button-secondary" data-close-2fa-modal data-clear-exclusions>'. __( 'Continue anyway', 'wp-2fa' ) .'</a>',
147 '<a href="#" class="modal__btn modal__btn-primary button-primary" data-close-2fa-modal data-cancel-action>'. __( 'Cancel', 'wp-2fa' ) .'</a>',
148 ],
149 false,
150 '560px'
151 );
152 ?>
153
154 <div class="wrap wp-2fa-settings-wrapper">
155 <h2><?php esc_html_e( 'WP 2FA Settings', 'wp-2fa' ); ?></h2>
156 <hr>
157 <?php if ( ! empty( WP2FA::get_wp2fa_setting( 'limit_access' ) ) && $main_user !== $user->ID ) { ?>
158
159 <?php
160 echo esc_html__( 'These settings have been disabled by your site administrator, please contact them for further assistance.', 'wp-2fa' );
161 ?>
162
163 <?php } else { ?>
164
165 <div class="nav-tab-wrapper">
166 <a href="<?php echo esc_url( add_query_arg( array( 'page' => 'wp-2fa-settings' ), network_admin_url( 'admin.php' ) ) ); ?>" class="nav-tab <?php echo ! isset( $_REQUEST['tab'] ) ? 'nav-tab-active' : ''; ?>"><?php _e( '2FA Settings', 'wp-2fa' ); ?></a>
167 <a href="
168 <?php
169 echo esc_url(
170 add_query_arg(
171 array(
172 'page' => 'wp-2fa-settings',
173 'tab' => 'email-settings',
174 ),
175 network_admin_url( 'admin.php' )
176 )
177 );
178 ?>
179 " class="nav-tab <?php echo isset( $_REQUEST['tab'] ) && 'email-settings' === $_REQUEST['tab'] ? 'nav-tab-active' : ''; ?>"><?php _e( 'Email Settings & Templates', 'wp-2fa' ); ?></a>
180 </div>
181 <?php
182 if ( ! current_user_can( 'manage_options' ) ) {
183 return;
184 }
185 if ( WP2FA::is_this_multisite() ) {
186 $action = 'edit.php?action=update_wp2fa_network_options';
187 } else {
188 $action = 'options.php';
189 }
190 if ( ! isset( $_REQUEST['tab'] ) || isset( $_REQUEST['tab'] ) && '2fa-settings' === $_REQUEST['tab'] ) :
191 ?>
192 <br/>
193 <?php
194 printf( '<p class="description">%1$s <a href="mailto:support@wpwhitesecurity.com">%2$s</a></p>',
195 esc_html__( 'Use the settings below to configure the properties of the two-factor authentication on your website and how users use it. If you have any questions send us an email at', 'wp-2fa' ),
196 esc_html__( 'support@wpwhitesecurity.com', 'wp-2fa' )
197 );
198 ?>
199 <br/>
200 <?php $total_users = count_users(); ?>
201 <form id="wp-2fa-admin-settings" action='<?php echo esc_attr( $action ); ?>' method='post' autocomplete="off" data-2fa-total-users="<?php echo $total_users['total_users']; ?>">
202 <?php
203 if ( ! current_user_can( 'manage_options' ) ) {
204 return;
205 }
206
207 settings_fields( WP_2FA_SETTINGS_NAME );
208 $this->select_method_setting();
209 $this->select_enforcement_policy_setting();
210 $this->excluded_roles_or_users_setting();
211 if ( WP2FA::is_this_multisite() ) {
212 $this->excluded_network_sites();
213 }
214 $this->grace_period_setting();
215 $this->user_redirect_after_wizard();
216 $this->user_profile_settings();
217 $this->disable_2fa_removal_setting();
218 $this->changeDefaultTextArea();
219 $this->gracePeriodFrequency();
220 $this->limit_settings_access();
221 $this->remove_data_upon_uninstall();
222 submit_button();
223 ?>
224 </form>
225 <?php endif; ?>
226
227 <?php
228 if ( WP2FA::is_this_multisite() ) {
229 $action = 'edit.php?action=update_wp2fa_network_email_options';
230 } else {
231 $action = 'options.php';
232 }
233 ?>
234
235 <?php if ( isset( $_REQUEST['tab'] ) && 'email-settings' === $_REQUEST['tab'] ) : ?>
236 <br/>
237 <?php
238 printf( '<p class="description">%1$s <a href="mailto:support@wpwhitesecurity.com">%2$s</a></p>',
239 esc_html__( 'Use the settings below to configure the emails which are sent to users as part of the 2FA plugin. If you have any questions send us an email at', 'wp-2fa' ),
240 esc_html__( 'support@wpwhitesecurity.com', 'wp-2fa' )
241 );
242 ?>
243 <br/>
244 <form action='<?php echo esc_attr( $action ); ?>' method='post' autocomplete="off">
245 <?php
246 if ( ! current_user_can( 'manage_options' ) ) {
247 return;
248 }
249
250 settings_fields( WP_2FA_EMAIL_SETTINGS_NAME );
251 $this->email_from_settings();
252 $this->email_settings();
253 submit_button( 'Save email settings and templates' );
254 ?>
255 </form>
256 <?php endif; ?>
257
258 <?php } ?>
259 </div>
260 <?php
261 }
262
263 private function changeDefaultTextArea() {
264 ?>
265 <h3><?php esc_html_e( 'Change the default text used in the 2FA code page?', 'wp-2fa' ); ?></h3>
266 <p class="description">
267 <?php esc_html_e( 'This is the text shown to the users on the page when they are asked to enter the 2FA code. To change the default text, simply type it in the below placeholder.', 'wp-2fa' ); ?>
268 </p>
269 <table class="form-table">
270 <tbody>
271 <tr>
272 <th><label for="2fa-method"><?php esc_html_e( '2FA code page text', 'wp-2fa' ); ?></label></th>
273 <td>
274 <fieldset>
275 <label for="default-text-code-page">
276 <textarea cols="70" rows="10" name="wp_2fa_settings[default-text-code-page]" id="default-text-code-page"><?php echo WP2FA::get_wp2fa_setting( 'default-text-code-page', true ); ?></textarea>
277 <div><span><strong><i><?php esc_html_e( 'Note:', 'wp-2fa' ); ?></i></strong> <?php esc_html_e( 'Only plain text is allowed.', 'wp-2fa' ); ?></span></div>
278 </label>
279 </fieldset>
280 </td>
281 </tr>
282 </tbody>
283 </table>
284 <?php
285
286 }
287
288 /**
289 * General settings
290 */
291 private function select_method_setting() {
292 FirstTimeWizardSteps::selectMethod( false );
293 }
294
295 /**
296 * Policy settings
297 */
298 private function select_enforcement_policy_setting() {
299 FirstTimeWizardSteps::enforcementPolicy( false );
300 }
301
302 /**
303 * User profile settings
304 */
305 private function user_profile_settings() {
306 ?>
307 <h3><?php esc_html_e( 'Can users access the WordPress dashboard or you have custom profile pages? ', 'wp-2fa' ); ?></h3>
308 <p class="description">
309 <?php esc_html_e( 'If your users do not have access to the WordPress dashboard (because you use custom user profile pages) enable this option. Once enabled, the plugin creates a page which ONLY authenticated users can access to configure their user 2FA settings. A link to this page is sent in the 2FA welcome email.', 'wp-2fa' ); ?></a>
310 </p>
311 <table class="form-table">
312 <tbody>
313 <tr>
314 <th><label for="enforcement-policy"><?php esc_html_e( 'Frontend 2FA settings page', 'wp-2fa' ); ?></label></th>
315 <td>
316 <fieldset>
317 <label class="radio-inline">
318 <input id="use_custom_page" type="radio" name="wp_2fa_settings[create-custom-user-page]" value="yes"
319 <?php checked( WP2FA::get_wp2fa_setting( 'create-custom-user-page' ), 'yes' ); ?>
320 >
321 <?php esc_html_e( 'Yes', 'wp-2fa' ); ?>
322 </label>
323 <label class="radio-inline">
324 <input id="dont_use_custom_page" type="radio" name="wp_2fa_settings[create-custom-user-page]" value="no"
325 <?php checked( WP2FA::get_wp2fa_setting( 'create-custom-user-page' ), 'no' ); ?>
326 <?php checked( WP2FA::get_wp2fa_setting( 'create-custom-user-page' ), '' ); ?>
327 >
328 <?php esc_html_e( 'No', 'wp-2fa' ); ?>
329 </label>
330 </fieldset>
331 </td>
332 </tr>
333 <tr class="custom-user-page-setting disabled">
334 <th><label for="enforcement-policy"><?php esc_html_e( 'Frontend 2FA settings page URL', 'wp-2fa' ); ?></label></th>
335 <td>
336 <fieldset>
337 <?php
338 if ( ! empty( WP2FA::get_wp2fa_setting( 'custom-user-page-id' ) ) ) {
339 $custom_slug = get_post_field( 'post_name', get_post( WP2FA::get_wp2fa_setting( 'custom-user-page-id' ) ) );
340 } else {
341 $custom_slug = WP2FA::get_wp2fa_setting( 'custom-user-page-url' );
342 }
343
344 $has_error = false;
345 $settings_errors = get_settings_errors( WP_2FA_SETTINGS_NAME );
346 if (!empty($settings_errors)) {
347 foreach ( $settings_errors as $error ) {
348 if ($error['code'] == 'no_page_slug_provided') {
349 $has_error = true;
350 break;
351 }
352 }
353 }
354
355 ?>
356 <?php echo trailingslashit( get_site_url() ); ?>
357 <input type="text" id="custom-user-page-url" name="wp_2fa_settings[custom-user-page-url]" value="<?php echo sanitize_text_field( $custom_slug ); ?>"<?php if ($has_error): ?> class="error"<?php endif; ?>>
358 </fieldset>
359 <?php
360 if ( ! empty( WP2FA::get_wp2fa_setting( 'custom-user-page-id' ) ) ) {
361 $edit_post_link = get_edit_post_link( WP2FA::get_wp2fa_setting( 'custom-user-page-id' ) );
362 $view_post_link = get_permalink( WP2FA::get_wp2fa_setting( 'custom-user-page-id' ) );
363 ?>
364 <br>
365 <a href="<?php echo esc_url( $edit_post_link ); ?>" target="_blank" class="button button-secondary" style="margin-right: 5px;"><?php esc_html_e( 'Edit Page', 'wp-2fa' ); ?></a> <a href="<?php echo esc_url( $view_post_link ); ?>" target="_blank" class="button button-primary"><?php esc_html_e( 'View Page', 'wp-2fa' ); ?></a>
366 <?php
367 }
368 ?>
369 </td>
370 </tr>
371 <tr class="custom-user-page-setting">
372 <th colspan="2"><p class="description"><?php esc_html_e( 'Specify the page where you want to redirect your users to after they complete the 2FA setup. This will override the global redirect setting.', 'wp-2fa' ); ?></p></th>
373 </tr>
374 <tr class="custom-user-page-setting">
375 <th><label for="enforcement-policy"><?php esc_html_e( 'Redirect users after 2FA setup', 'wp-2fa' ); ?></label></th>
376 <td>
377 <fieldset>
378 <?php
379 $custom_slug = WP2FA::get_wp2fa_setting( 'redirect-user-custom-page' );
380 ?>
381 <?php echo trailingslashit( get_site_url() ); ?>
382 <input type="text" id="redirect-user-custom-page" name="wp_2fa_settings[redirect-user-custom-page]" value="<?php echo sanitize_text_field( $custom_slug ); ?>">
383 </fieldset>
384 </td>
385 </tr>
386 </tbody>
387 </table>
388 <?php
389 }
390
391 /**
392 * User profile settings
393 */
394 private function user_redirect_after_wizard() {
395 ?>
396 <h3><?php esc_html_e( 'Do you want to redirect the user to a specific page after completing the 2FA setup wizard?', 'wp-2fa' ); ?></h3>
397 <p class="description">
398 <?php esc_html_e( 'Specify a URL of a page where you want to redirect the users once they complete the 2FA setup wizard. Leave empty for default behaviour, in which users are redirected back to the page from where they launched the wizard.', 'wp-2fa' ); ?></a>
399 </p>
400 <table class="form-table">
401 <tbody>
402 <tr>
403 <th><label for="redirect-user-custom-page-global"><?php esc_html_e( 'Redirect users after 2FA setup to', 'wp-2fa' ); ?></label></th>
404 <td>
405 <fieldset>
406 <?php echo trailingslashit( get_site_url() ); ?>
407 <input type="text" id="redirect-user-custom-page-global" name="wp_2fa_settings[redirect-user-custom-page-global]" value="<?php echo sanitize_text_field( WP2FA::get_wp2fa_setting( 'redirect-user-custom-page-global' ) ); ?>">
408 </fieldset>
409 </td>
410 </tr>
411 </tbody>
412 </table>
413 <?php
414 }
415
416 /**
417 * Role and users exclusion settings
418 */
419 private function excluded_roles_or_users_setting() {
420 $enforcement = WP2FA::get_wp2fa_setting( 'enforcement-policy' );
421 $disabled_class = ( 'all-users' == $enforcement ) ? 'enabled' : 'disabled';
422 ?>
423 <div id="exclusion_settings_wrapper" class="<?php echo esc_attr( $disabled_class ); ?>">
424 <?php FirstTimeWizardSteps::excludeUsers(); ?>
425 </div>
426 <?php
427 }
428
429 /**
430 * Role and users exclusion settings
431 */
432 private function excluded_network_sites() {
433 FirstTimeWizardSteps::excludedNetworkSites();
434 }
435
436 /**
437 * Grace period settings
438 */
439 private function grace_period_setting() {
440 ?>
441 <br>
442 <h3><?php esc_html_e( 'Should users be asked to setup 2FA instantly or should they have a grace period?', 'wp-2fa' ); ?></h3>
443 <p class="description">
444 <?php esc_html_e( 'When you enforce 2FA on users they have a grace period to configure 2FA. If they fail to configure it within the configured stipulated time, their account will be locked and have to be unlocked manually. Note that user accounts cannot be unlocked automatically, even if you change the settings. As a security precaution they always have to be unlocked them manually. Maximum grace period is 10 days.', 'wp-2fa' ); ?> <a href="https://www.wpwhitesecurity.com/support/kb/configure-grace-period-2fa/?utm_source=plugin&utm_medium=referral&utm_campaign=WP2FA&utm_content=settings+pages" target="_blank"><?php esc_html_e( 'Learn more.', 'wp-2fa' ); ?></a>
445 </p>
446
447 <table class="form-table">
448 <tbody>
449 <tr>
450 <th><label for="grace-policy"><?php esc_html_e( 'Grace period', 'wp-2fa' ); ?></label></th>
451 <td>
452 <?php FirstTimeWizardSteps::gracePeriod( true ); ?>
453 </td>
454 </tr>
455 </tbody>
456 </table>
457 <?php
458 }
459
460 /**
461 * Grace period frequency
462 */
463 private function gracePeriodFrequency() {
464 ?>
465 <h3><?php esc_html_e( 'How often should the plugin check if a user\'s grace period is over?', 'wp-2fa' ); ?></h3>
466 <p class="description">
467 <?php esc_html_e( 'By default the plugin checks if a users grace periods to setup 2FA has passed when the user tries to login. If you would like the plugin to advise the user within an hour, enable the below option to add a cron job that runs every hour.', 'wp-2fa' ); ?>
468 </p>
469 <table class="form-table">
470 <tbody>
471 <tr>
472 <th><label for="grace-period"><?php esc_html_e( 'Enable cron', 'wp-2fa' ); ?></label></th>
473 <td>
474 <fieldset>
475 <input type="checkbox" id="grace-cron" name="wp_2fa_settings[enable_grace_cron]" value="enable_grace_cron"
476 <?php checked( 1, WP2FA::get_wp2fa_setting( 'enable_grace_cron' ), true ); ?>
477 >
478 <?php esc_html_e( 'Use cron job to check grace periods', 'wp-2fa' ); ?>
479 </fieldset>
480 </td>
481 </tr>
482 <tr class="disabled destory-session-setting">
483 <th><label for="destory-session"><?php esc_html_e( 'Destroy session', 'wp-2fa' ); ?></label></th>
484 <td>
485 <fieldset>
486 <input type="checkbox" id="destory-session" name="wp_2fa_settings[enable_destroy_session]" value="enable_destroy_session"
487 <?php checked( 1, WP2FA::get_wp2fa_setting( 'enable_destroy_session' ), true ); ?>
488 >
489 <?php esc_html_e( 'Destroy user session when grace period expires?', 'wp-2fa' ); ?>
490 </fieldset>
491 </td>
492 </tr>
493 </tbody>
494 </table>
495 <?php
496 }
497
498 /**
499 * Disable removal of 2FA settings
500 */
501 private function disable_2fa_removal_setting() {
502 $user = wp_get_current_user();
503 ?>
504 <br>
505 <h3><?php esc_html_e( 'Should users be able to disable 2FA on their user profile?', 'wp-2fa' ); ?></h3>
506 <p class="description">
507 <?php esc_html_e( 'Users can configure and also disable 2FA on their profile by clicking the "Remove 2FA" button. Enable this setting to disable the Remove 2FA button so users cannot disable 2FA from their user profile.', 'wp-2fa' ); ?>
508 </p>
509 <table class="form-table">
510 <tbody>
511 <tr>
512 <th><label for="hide-remove-2fa"><?php esc_html_e( 'Hide the Remove 2FA button', 'wp-2fa' ); ?></label></th>
513 <td>
514 <fieldset>
515 <input type="checkbox" id="hide-remove-2fa" name="wp_2fa_settings[hide_remove_button]" value="hide_remove_button"
516 <?php checked( 1, WP2FA::get_wp2fa_setting( 'hide_remove_button' ), true ); ?>
517 >
518 <?php esc_html_e( 'Hide the Remove 2FA button on user profile pages', 'wp-2fa' ); ?>
519 </fieldset>
520 </td>
521 </tr>
522 </tbody>
523 </table>
524 <?php
525 }
526
527 /**
528 * Limit settings setting
529 */
530 private function limit_settings_access() {
531 ?>
532 <br>
533 <h3><?php esc_html_e( 'Limit 2FA settings access?', 'wp-2fa' ); ?></h3>
534 <p class="description">
535 <?php esc_html_e( 'Use this setting to hide this plugin configuration area from all other admins.', 'wp-2fa' ); ?>
536 </p>
537 <table class="form-table">
538 <tbody>
539 <tr>
540 <th><label for="grace-period"><?php esc_html_e( 'Limit access to 2FA settings', 'wp-2fa' ); ?></label></th>
541 <td>
542 <fieldset>
543 <input type="checkbox" id="limit_access" name="wp_2fa_settings[limit_access]" value="limit_access"
544 <?php checked( 1, WP2FA::get_wp2fa_setting( 'limit_access' ), true ); ?>
545 >
546 <?php esc_html_e( 'Hide settings from other administrators', 'wp-2fa' ); ?>
547 </fieldset>
548 </td>
549 </tr>
550 </tbody>
551 </table>
552 <?php
553 }
554
555 /**
556 * Limit settings setting
557 */
558 private function remove_data_upon_uninstall() {
559 ?>
560 <div class="danger-zone-wrapper">
561 <h3><?php esc_html_e( 'Do you want to delete the plugin data from the database upon uninstall?', 'wp-2fa' ); ?></h3>
562 <p class="description">
563 <?php esc_html_e( 'The plugin saves its settings in the WordPress database. By default the plugin settings are kept in the database so if it is installed again, you do not have to reconfigure the plugin. Enable this setting to delete the plugin settings from the database upon uninstall.', 'wp-2fa' ); ?>
564 </p>
565 <table class="form-table">
566 <tbody>
567 <tr>
568 <th><label for="delete_data"><?php esc_html_e( 'Delete data', 'wp-2fa' ); ?></label></th>
569 <td>
570 <fieldset>
571 <input type="checkbox" id="elete_data" name="wp_2fa_settings[delete_data_upon_uninstall]" value="delete_data_upon_uninstall"
572 <?php checked( 1, WP2FA::get_wp2fa_setting( 'delete_data_upon_uninstall' ), true ); ?>
573 >
574 <?php esc_html_e( 'Delete data upon uninstall', 'wp-2fa' ); ?>
575 </fieldset>
576 </td>
577 </tr>
578 </tbody>
579 </table>
580 </div>
581 <?php
582 }
583
584 /**
585 * Get all users
586 */
587 public function get_all_users() {
588 // Die if user does not have permission to view.
589 if ( ! current_user_can( 'manage_options' ) ) {
590 die( 'Access Denied.' );
591 }
592 // Filter $_GET array for security.
593 $get_array = filter_input_array( INPUT_GET );
594
595 // Die if nonce verification failed.
596 if ( ! wp_verify_nonce( sanitize_text_field( $get_array['wp_2fa_nonce'] ), 'wp-2fa-settings-nonce' ) ) {
597 die( esc_html__( 'Nonce verification failed.', 'wp-2fa' ) );
598 }
599
600 $users_args = array(
601 'fields' => array( 'ID', 'user_login' ),
602 );
603 if ( WP2FA::is_this_multisite() ) {
604 $users_args['blog_id'] = 0;
605 }
606 $users_data = UserUtils::get_all_user_ids_and_login_names( 'query', $users_args );
607
608 // Create final array which we will fill in below.
609 $users = [];
610
611 foreach ( $users_data as $user ) {
612 if ( strpos( $user['user_login'], $get_array['term'] ) !== false ) {
613 array_push( $users, [
614 'value' => $user['user_login'],
615 'label' => $user['user_login']
616 ]);
617 }
618 }
619
620 echo wp_json_encode( $users );
621 exit;
622 }
623
624 /**
625 * Get all network sites
626 */
627 public function get_all_network_sites() {
628 // Die if user does not have permission to view.
629 if ( ! current_user_can( 'manage_options' ) ) {
630 die( 'Access Denied.' );
631 }
632 // Filter $_GET array for security.
633 $get_array = filter_input_array( INPUT_GET );
634 // Die if nonce verification failed.
635 if ( ! wp_verify_nonce( sanitize_text_field( $get_array['wp_2fa_nonce'] ), 'wp-2fa-settings-nonce' ) ) {
636 die( esc_html__( 'Nonce verification failed.', 'wp-2fa' ) );
637 }
638 // Fetch sites.
639 $sites_found = array();
640
641 foreach ( get_sites() as $site ) {
642 $subsite_id = get_object_vars( $site )['blog_id'];
643 $subsite_name = get_blog_details( $subsite_id )->blogname;
644 $site_details = '';
645 $site_details[ $subsite_id ] = $subsite_name;
646 if ( false !== stripos( $subsite_name, $get_array['term'] ) ) {
647 array_push( $sites_found, [
648 'label' => $subsite_id,
649 'value' => $subsite_name
650 ]);
651 }
652 }
653 echo wp_json_encode( $sites_found );
654 exit;
655 }
656
657 /**
658 * Unlock users accounts if they have overrun grace period
659 *
660 * @param int $user_id User ID.
661 */
662 public function unlock_account( $user_id ) {
663 // Die if user does not have permission to view.
664 if ( ! current_user_can( 'manage_options' ) ) {
665 die( 'Access Denied.' );
666 }
667
668 $grace_period = WP2FA::get_wp2fa_setting( 'grace-period' );
669 $grace_period_denominator = WP2FA::get_wp2fa_setting( 'grace-period-denominator' );
670 $create_a_string = $grace_period . ' ' . $grace_period_denominator;
671 // Turn that string into a time.
672 $grace_expiry = strtotime( $create_a_string );
673
674 // Filter $_GET array for security.
675 $get_array = filter_input_array( INPUT_GET );
676 $nonce = sanitize_text_field( $get_array['wp_2fa_nonce'] );
677
678 // Die if nonce verification failed.
679 if ( ! wp_verify_nonce( $nonce, 'wp-2fa-unlock-account-nonce' ) ) {
680 die( esc_html__( 'Nonce verification failed.', 'wp-2fa' ) );
681 }
682
683 if ( isset( $get_array['user_id'] ) ) {
684 global $wpdb;
685 $wpdb->query(
686 $wpdb->prepare(
687 "
688 DELETE FROM $wpdb->usermeta
689 WHERE user_id = %d
690 AND meta_key IN ( %s, %s )
691 ",
692 [
693 intval( $get_array['user_id'] ),
694 WP_2FA_PREFIX . 'user_grace_period_expired',
695 WP_2FA_PREFIX . 'locked_account_notification',
696 ]
697 )
698 );
699 $update = update_user_meta( intval( $get_array['user_id'] ), WP_2FA_PREFIX . 'grace_period_expiry', $grace_expiry );
700 $this->send_account_unlocked_email( intval( $get_array['user_id'] ) );
701 add_action( 'admin_notices', array( $this, 'user_unlocked_notice' ) );
702 }
703 }
704
705 /**
706 * Remove user 2fa config
707 *
708 * @param int $user_id User ID.
709 */
710 public function remove_user_2fa( $user_id ) {
711 // Filter $_GET array for security.
712 $get_array = filter_input_array( INPUT_GET );
713 $nonce = sanitize_text_field( $get_array['wp_2fa_nonce'] );
714
715 if ( ! wp_verify_nonce( $nonce, 'wp-2fa-remove-user-2fa-nonce' ) ) {
716 die( esc_html__( 'Nonce verification failed.', 'wp-2fa' ) );
717 }
718
719 if ( isset( $get_array['user_id'] ) ) {
720 $user_id = intval( $get_array['user_id'] );
721 global $wpdb;
722 $wpdb->query(
723 $wpdb->prepare(
724 "DELETE FROM $wpdb->usermeta
725 WHERE user_id = %d
726 AND meta_key LIKE %s",
727 [
728 $user_id,
729 'wp_2fa_%'
730 ]
731 )
732 );
733
734 $is_needed = WP2FA::isUserEnforced( $user_id );
735
736 if ( $is_needed ) {
737 if ( 'do-not-enforce' !== WP2FA::get_wp2fa_setting( 'enforcement-policy' ) ) {
738 // Turn inputs into a useable string.
739 $create_a_string = WP2FA::get_wp2fa_setting( 'grace-period' ) . ' ' . WP2FA::get_wp2fa_setting( 'grace-period-denominator' );
740 // Turn that string into a time.
741 $grace_expiry = strtotime( $create_a_string );
742 update_user_meta( $user_id, WP_2FA_PREFIX . 'grace_period_expiry', $grace_expiry );
743 update_user_meta( $user_id, WP_2FA_PREFIX . 'update_nag_dismissed', true );
744 }
745 $grace_policy = WP2FA::get_wp2fa_setting( 'grace-policy' );
746 if ( 'no-grace-period' === $grace_policy ) {
747 update_user_meta( $user_id, WP_2FA_PREFIX . 'user_enforced_instantly', true );
748 // Set this to a known older value so its already expired.
749 update_user_meta( $user_id, WP_2FA_PREFIX . 'grace_period_expiry', '1609502400' );
750 // Get sessions for user with ID $user_id.
751 $sessions = \WP_Session_Tokens::get_instance( $user_id );
752 // Log them out.
753 $sessions->destroy_all();
754 }
755 }
756 if ( isset( $get_array['admin_reset'] ) ) {
757 add_action( 'admin_notices', array( $this, 'admin_deleted_2fa_notice' ) );
758 } else {
759 add_action( 'admin_notices', array( $this, 'user_deleted_2fa_notice' ) );
760 }
761 }
762 }
763
764 /**
765 * Send account unlocked notification via email.
766 *
767 * @param int $user_id user ID.
768 *
769 * @return boolean
770 */
771 public static function send_account_unlocked_email( $user_id ) {
772 // Bail if the user has not enabled this email.
773 if ( 'enable_account_unlocked_email' !== WP2FA::get_wp2fa_email_templates( 'send_account_unlocked_email' ) ) {
774 return false;
775 }
776
777 // Grab user data.
778 $user = get_userdata( $user_id );
779 // Grab user email.
780 $email = $user->user_email;
781 // Setup the email contents.
782 $subject = wp_strip_all_tags( WP2FA::replace_email_strings( WP2FA::get_wp2fa_email_templates( 'user_account_unlocked_email_subject' ) ) );
783 $message = wpautop( WP2FA::replace_email_strings( WP2FA::get_wp2fa_email_templates( 'user_account_unlocked_email_body' ), $user_id ) );
784
785 self::send_email($email, $subject, $message);
786 }
787
788 /**
789 * Validate options before saving
790 *
791 * @param array $input The settings array.
792 *
793 * @return array|void
794 */
795 public function validate_and_sanitize( $input ) {
796
797 // Bail if user doesnt have permissions to be here.
798 if ( ! current_user_can( 'manage_options' ) || ! isset( $_POST['action'] ) && ! check_admin_referer( 'wp2fa-step-choose-method' ) ) {
799 return;
800 }
801
802 $noMethodEnabled = false;
803 if ( ! isset( $input['enable_totp'] ) && ! isset( $input['enable_email'] ) && ! isset( $_POST['save_step'] ) ) {
804 add_settings_error(
805 WP_2FA_SETTINGS_NAME,
806 esc_attr( 'enable_email_settings_error' ),
807 esc_html__( 'At least one 2FA method should be enabled.', 'wp-2fa' ),
808 'error'
809 );
810 $noMethodEnabled = true;
811 }
812
813 $simple_settings_we_can_loop = array(
814 'enable_totp',
815 'enable_email',
816 'backup_codes_enabled',
817 'grace-policy',
818 'enable_grace_cron',
819 'enable_destroy_session',
820 '2fa_settings_last_updated_by',
821 'limit_access',
822 'delete_data_upon_uninstall',
823 'hide_remove_button',
824 'redirect-user-custom-page',
825 'redirect-user-custom-page-global',
826 'superadmins-role-add',
827 'superadmins-role-exclude',
828 );
829
830 $settings_to_turn_into_bools = array(
831 'enable_grace_cron',
832 'enable_destroy_session',
833 'limit_access',
834 'delete_data_upon_uninstall',
835 'hide_remove_button'
836 );
837
838 $settings_to_turn_into_array = [
839 'enforced_roles',
840 'enforced_users',
841 'excluded_users',
842 'excluded_roles',
843 'excluded_sites',
844 ];
845
846 foreach ( $simple_settings_we_can_loop as $simple_setting ) {
847 if ( ! in_array( $simple_setting, $settings_to_turn_into_bools ) ) {
848 // Is item is not one of our possible settings we want to turn into a bool, process.
849 $output[ $simple_setting ] = ( isset( $input[ $simple_setting ] ) && ! empty( $input[ $simple_setting ] ) ) ? trim( sanitize_text_field( $input[ $simple_setting ] ) ) : false;
850 } else {
851 // This item is one we treat as a bool, so process correctly.
852 $output[ $simple_setting ] = ( isset( $input[ $simple_setting ] ) && ! empty( $input[ $simple_setting ] ) ) ? true : false;
853 }
854 }
855
856 if ( $noMethodEnabled ) {
857 // No method is enabled, fall back to previous selected one - we don't want to break the logic
858 $totpEnabled = WP2FA::get_wp2fa_setting( 'enable_totp' );
859 $emailEnabled = WP2FA::get_wp2fa_setting( 'enable_email' );
860
861 if ( $totpEnabled ) {
862 $output['enable_totp'] = $totpEnabled;
863 }
864 if ( $emailEnabled ) {
865 $output['enable_email'] = $emailEnabled;
866 }
867 }
868
869 $output['included_sites'] = [];
870 if ( isset( $input['included_sites'] ) && is_array($input['included_sites']) && ! empty( $input['included_sites'] ) ) {
871 foreach ( $input['included_sites'] as &$site ) {
872 if ( ! filter_var($site, FILTER_VALIDATE_INT) ) {
873 unset( $site );
874 continue;
875 }
876
877 $output['included_sites'][] = $site;
878 }
879 }
880 unset( $site );
881
882 foreach ($settings_to_turn_into_array as $setting) {
883 if ( isset( $input[$setting] ) ) {
884 $output[$setting] = $input[$setting];
885 } else {
886 $output[$setting] = [];
887 }
888 }
889
890 $output['default-text-code-page'] = WP2FA::get_wp2fa_setting('default-text-code-page', false, true);
891
892 if ( isset( $input['default-text-code-page'] ) && '' !== trim( $input['default-text-code-page'] ) ) {
893 $output['default-text-code-page'] = \strip_tags( $input['default-text-code-page'] );
894 }
895
896 $log_content = __( 'The following setting are being saved: ', 'wp-2fa' ) . "\n" . json_encode( $input ) . "\n";
897 Debugging::log( $log_content );
898
899 if ( isset( $input['grace-period'] ) ) {
900 if ( 0 === (int) $input['grace-period'] ) {
901 add_settings_error(
902 WP_2FA_SETTINGS_NAME,
903 esc_attr( 'grace_settings_error' ),
904 esc_html__( 'Grace period must be at least 1 day/hour', 'wp-2fa' ),
905 'error'
906 );
907 $output['grace-period'] = 1;
908 } else {
909 $output['grace-period'] = (int) $input['grace-period'];
910 }
911 }
912
913 if ( isset( $input['grace-period-denominator'] ) && 'days' === $input['grace-period-denominator'] || isset( $input['grace-period-denominator'] ) && 'hours' === $input['grace-period-denominator'] || isset( $input['grace-period-denominator'] ) && 'seconds' === $input['grace-period-denominator'] ) {
914 $output['grace-period-denominator'] = sanitize_text_field( $input['grace-period-denominator'] );
915 }
916
917 if ( isset( $input['create-custom-user-page'] ) && 'yes' === $input['create-custom-user-page'] || isset( $input['create-custom-user-page'] ) && 'no' === $input['create-custom-user-page'] ) {
918 $output['create-custom-user-page'] = sanitize_text_field( $input['create-custom-user-page'] );
919 }
920
921 if ( isset( $input['custom-user-page-url'] ) ) {
922 if ( $input['custom-user-page-url'] !== WP2FA::get_wp2fa_setting( 'custom-user-page-url' ) ) {
923 if ( ! empty( WP2FA::get_wp2fa_setting( 'custom-user-page-id' ) ) ) {
924 $updated_post = array(
925 'ID' => WP2FA::get_wp2fa_setting( 'custom-user-page-id' ),
926 'post_name' => sanitize_title_with_dashes( $input['custom-user-page-url'] ),
927 );
928 wp_update_post( $updated_post );
929 $output['custom-user-page-url'] = sanitize_title_with_dashes( $input['custom-user-page-url'] );
930 $output['custom-user-page-id'] = WP2FA::get_wp2fa_setting( 'custom-user-page-id' );
931 } elseif ( 'yes' === $input['create-custom-user-page'] && ! empty( $input['custom-user-page-url'] ) ) {
932 $output['custom-user-page-url'] = sanitize_title_with_dashes( $input['custom-user-page-url'] );
933 $create_page = $this->generate_custom_user_profile_page( $output['custom-user-page-url'] );
934 $output['custom-user-page-id'] = (int) $create_page;
935 }
936 } else {
937 $output['custom-user-page-url'] = sanitize_title_with_dashes( $input['custom-user-page-url'] );
938 $output['custom-user-page-id'] = WP2FA::get_wp2fa_setting( 'custom-user-page-id' );
939 }
940 }
941
942 if ( isset( $_REQUEST['page'] ) && 'wp-2fa-setup' !== $_REQUEST['page'] || isset( $_REQUEST[ WP_2FA_SETTINGS_NAME ]['create-custom-user-page'] ) ) {
943
944 if ( isset( $input['create-custom-user-page'] ) && 'no' === $input['create-custom-user-page'] ) {
945 $output['custom-user-page-url'] = '';
946 $output['custom-user-page-id'] = '';
947 wp_delete_post( WP2FA::get_wp2fa_setting( 'custom-user-page-id' ), true );
948 }
949 }
950
951 if ( isset( $input['create-custom-user-page'] ) && 'yes' === $input['create-custom-user-page'] && empty( $input['custom-user-page-url'] ) ) {
952 add_settings_error(
953 WP_2FA_SETTINGS_NAME,
954 esc_attr( 'no_page_slug_provided' ),
955 esc_html__( 'You must provide a new page slug.', 'wp-2fa' ),
956 'error'
957 );
958 }
959
960 if ( isset( $input['grace-period'] ) && isset( $input['grace-period-denominator'] ) ) {
961 // Turn inputs into a useable string.
962 $create_a_string = $output['grace-period'] . ' ' . $output['grace-period-denominator'];
963 // Turn that string into a time.
964 $grace_expiry = strtotime( $create_a_string );
965 $output['grace-period-expiry-time'] = sanitize_text_field( $grace_expiry );
966 }
967
968 // Process main policy.
969 if ( isset( $input['enforcement-policy'] ) && in_array( $input['enforcement-policy'], [ 'all-users', 'certain-users-only', 'certain-roles-only', 'do-not-enforce', 'superadmins-only', 'superadmins-siteadmins-only', "enforce-on-multisite" ] ) ) {
970
971 // Clear enforced roles/users if setting has changed.
972 if ( 'all-users' === $input['enforcement-policy'] || 'do-not-enforce' === $input['enforcement-policy'] ) {
973 $input['enforced_users'] = [];
974 $input['enforced_roles'] = [];
975 $output['enforced_users'] = [];
976 $output['enforced_roles'] = [];
977 $output['superadmins-role-add'] = 'no';
978 }
979
980 $output['enforcement-policy'] = sanitize_text_field( $input['enforcement-policy'] );
981
982 if ( 'certain-roles-only' === $input['enforcement-policy'] && empty( $input['enforced_roles'] ) && empty( $input['enforced_users'] ) ) {
983 add_settings_error(
984 WP_2FA_SETTINGS_NAME,
985 esc_attr( 'enforced_roles_settings_error' ),
986 esc_html__( 'You must specify at least one role or user', 'wp-2fa' ),
987 'error'
988 );
989 }
990
991 // If any users are being exluded, delete any wp 2fa data.
992 if ( isset( $output['excluded_users'] ) &&
993 !empty( array_diff(WP2FA::get_wp2fa_setting( 'excluded_users' ), $output['excluded_users']) ) ) {
994 // Wipe user 2fa data.
995 $user_array = $output['excluded_users'];
996 foreach ( $user_array as $user ) {
997 if ( ! empty( $user ) ) {
998 $user_to_wipe = get_user_by( 'login', $user );
999 global $wpdb;
1000 $wpdb->query(
1001 $wpdb->prepare(
1002 "
1003 DELETE FROM $wpdb->usermeta
1004 WHERE user_id = %d
1005 AND meta_key LIKE %s
1006 ",
1007 [
1008 $user_to_wipe->ID,
1009 'wp_2fa_%'
1010 ]
1011 )
1012 );
1013
1014 }
1015 }
1016 }
1017 }
1018
1019 // Remove duplicates from settings errors. We do this as this sanitization callback is actually fired twice, so we end up with duplicates when saving the settings for the FIRST TIME only. The issue is not present once the settings are in the DB as the sanitization wont fire again. For details on this core issue - https://core.trac.wordpress.org/ticket/21989.
1020 global $wp_settings_errors;
1021 if ( isset( $wp_settings_errors ) ) {
1022 $errors = array_map( 'unserialize', array_unique( array_map( 'serialize', $wp_settings_errors ) ) );
1023 $wp_settings_errors = $errors;
1024 }
1025
1026 // Create a hash for comparison when we interact with a use.
1027 $settings_hash = SettingsUtils::create_settings_hash( $output );
1028 $update_options = SettingsUtils::update_option( WP_2FA_PREFIX . 'settings_hash', $settings_hash );
1029
1030 $log_content = __( 'Settings saving processes complete', 'wp-2fa' );
1031 Debugging::log( $log_content );
1032
1033 // We have overwridden any defaults by now so can clear this.
1034 SettingsUtils::delete_option( WP_2FA_PREFIX . 'default_settings_applied' );
1035
1036 return $output;
1037 }
1038
1039 /**
1040 * Hide settings menu item
1041 */
1042 public function hide_settings() {
1043 $user = wp_get_current_user();
1044
1045 // Check we have a user before doing anything else.
1046 if ( is_a( $user, '\WP_User' ) ) {
1047 $user_id = (int) $user->ID;
1048 if ( ! empty( WP2FA::get_wp2fa_setting( '2fa_settings_last_updated_by' ) ) ) {
1049 $main_user = (int) WP2FA::get_wp2fa_setting( '2fa_settings_last_updated_by' );
1050 } else {
1051 $main_user = '';
1052 }
1053 if ( ! empty( WP2FA::get_wp2fa_setting( 'limit_access' ) ) && $user->ID !== $main_user ) {
1054 // Remove admin menu item.
1055 remove_submenu_page( 'options-general.php', 'wp-2fa-settings' );
1056 }
1057 }
1058 }
1059
1060 /**
1061 * Add unlock user link to user actions.
1062 *
1063 * @param array $links Default row content.
1064 *
1065 * @return array
1066 */
1067 public function add_plugin_action_links( $links ) {
1068 $url = network_admin_url( '/admin.php?page=wp-2fa-settings' );
1069
1070 $url = Settings::getSettingsPageLink();
1071
1072 $links = array_merge(
1073 array(
1074 '<a href="' . esc_url( $url ) . '">' . esc_html__( 'Configure 2FA Settings', 'wp-2fa' ) . '</a>',
1075 ),
1076 $links
1077 );
1078
1079 return $links;
1080
1081 }
1082
1083 /**
1084 * User unlocked notice.
1085 */
1086 public function user_unlocked_notice() {
1087 ?>
1088 <div class="notice notice-success is-dismissible">
1089 <p><?php esc_html_e( 'User account successfully unlocked. User can login again.', 'wp-2fa' ); ?></p>
1090 <button type="button" class="notice-dismiss">
1091 <span class="screen-reader-text"><?php esc_html_e( 'Dismiss this notice.', 'wp-2fa' ); ?></span>
1092 </button>
1093 </div>
1094 <?php
1095 }
1096
1097 /**
1098 * User deleted 2FA settings notification
1099 */
1100 public function user_deleted_2fa_notice() {
1101 ?>
1102 <div class="notice notice-success is-dismissible">
1103 <p><?php esc_html_e( 'Your 2FA settings have been removed.', 'wp-2fa' ); ?></p>
1104 <button type="button" class="notice-dismiss">
1105 <span class="screen-reader-text"><?php esc_html_e( 'Dismiss this notice.', 'wp-2fa' ); ?></span>
1106 </button>
1107 </div>
1108 <?php
1109 }
1110
1111 /**
1112 * Admin deleted user 2FA settings notification
1113 */
1114 public function admin_deleted_2fa_notice() {
1115 ?>
1116 <div class="notice notice-success is-dismissible">
1117 <p><?php esc_html_e( 'User 2FA settings have been removed.', 'wp-2fa' ); ?></p>
1118 <button type="button" class="notice-dismiss">
1119 <span class="screen-reader-text"><?php esc_html_e( 'Dismiss this notice.', 'wp-2fa' ); ?></span>
1120 </button>
1121 </div>
1122 <?php
1123 }
1124
1125 public function update_wp2fa_network_options() {
1126 check_admin_referer( 'wp_2fa_settings-options' );
1127
1128 if ( isset( $_POST[ WP_2FA_SETTINGS_NAME ] ) ) {
1129 $options = $this->validate_and_sanitize( wp_unslash( $_POST[ WP_2FA_SETTINGS_NAME ] ) );
1130 $settings_errors = get_settings_errors( WP_2FA_SETTINGS_NAME );
1131 if ( ! empty( $settings_errors ) ) {
1132
1133 // redirect back to our options page.
1134 wp_safe_redirect(
1135 add_query_arg(
1136 array(
1137 'page' => 'wp-2fa-settings',
1138 'wp_2fa_network_settings_error' => urlencode_deep( $settings_errors[ 0 ]['message'] ),
1139 ),
1140 network_admin_url( 'settings.php' )
1141 )
1142 );
1143 exit;
1144
1145 }
1146 $update_options = SettingsUtils::update_option( WP_2FA_SETTINGS_NAME, $options );
1147 }
1148
1149 // redirect back to our options page.
1150 wp_safe_redirect(
1151 add_query_arg(
1152 array(
1153 'page' => 'wp-2fa-settings',
1154 'wp_2fa_network_settings_updated' => 'true',
1155 ),
1156 network_admin_url( 'admin.php' )
1157 )
1158 );
1159 exit;
1160 }
1161
1162 /**
1163 * Handle saving email options to the network main site options.
1164 */
1165 public function update_wp2fa_network_email_options() {
1166 if ( isset( $_POST['email_from_setting'] ) ) {
1167 $options = $this->validate_and_sanitize_email( wp_unslash( $_POST ) );
1168
1169 if ( isset( $_POST['email_from_setting'] ) && 'use-custom-email' === $_POST['email_from_setting'] && isset( $_POST['custom_from_display_name'] ) && empty( $_POST['custom_from_display_name'] ) || isset( $_POST['email_from_setting'] ) && 'use-custom-email' === $_POST['email_from_setting'] && isset( $_POST['custom_from_email_address'] ) && empty( $_POST['custom_from_email_address'] ) ) {
1170 // redirect back to our options page.
1171 wp_safe_redirect(
1172 add_query_arg(
1173 array(
1174 'page' => 'wp-2fa-settings',
1175 'wp_2fa_network_settings_updated' => 'false',
1176 'tab' => 'email-settings',
1177 ),
1178 network_admin_url( 'admin.php' )
1179 )
1180 );
1181 exit;
1182 }
1183
1184 $update_options = SettingsUtils::update_option( WP_2FA_EMAIL_SETTINGS_NAME, $options );
1185 }
1186
1187 // redirect back to our options page.
1188 wp_safe_redirect(
1189 add_query_arg(
1190 array(
1191 'page' => 'wp-2fa-settings',
1192 'wp_2fa_network_settings_updated' => 'true',
1193 'tab' => 'email-settings',
1194 ),
1195 network_admin_url( 'admin.php' )
1196 )
1197 );
1198 exit;
1199 }
1200
1201 /**
1202 * These are used instead of add_settings_error which in a network site. Used to show if settings have been updated or failed.
1203 */
1204 public function settings_saved_network_admin_notice() {
1205 if ( isset( $_GET['wp_2fa_network_settings_updated'] ) && $_GET['wp_2fa_network_settings_updated'] == 'true' ) :
1206 ?>
1207 <div class="notice notice-success is-dismissible">
1208 <p><?php esc_html_e( '2FA Settings Updated', 'wp-2fa' ); ?></p>
1209 <button type="button" class="notice-dismiss">
1210 <span class="screen-reader-text"><?php esc_html_e( 'Dismiss this notice.', 'wp-2fa' ); ?></span>
1211 </button>
1212 </div>
1213 <?php
1214 endif;
1215 if ( isset( $_GET['wp_2fa_network_settings_updated'] ) && $_GET['wp_2fa_network_settings_updated'] == 'false' ) :
1216 ?>
1217 <div class="notice notice-error is-dismissible">
1218 <p><?php esc_html_e( 'Please ensure both custom email address and display name are provided.', 'wp-2fa' ); ?></p>
1219 <button type="button" class="notice-dismiss">
1220 <span class="screen-reader-text"><?php esc_html_e( 'Dismiss this notice.', 'wp-2fa' ); ?></span>
1221 </button>
1222 </div>
1223 <?php
1224 endif;
1225 if ( isset( $_GET['wp_2fa_network_settings_error'] ) ) :
1226 ?>
1227 <div class="notice notice-error is-dismissible">
1228 <p><?php echo urldecode_deep( $_GET['wp_2fa_network_settings_error'] ); ?></p>
1229 <button type="button" class="notice-dismiss">
1230 <span class="screen-reader-text"><?php esc_html_e( 'Dismiss this notice.', 'wp-2fa' ); ?></span>
1231 </button>
1232 </div>
1233 <?php
1234 endif;
1235 }
1236
1237 /**
1238 * Email settings
1239 */
1240 private function email_from_settings() {
1241 ?>
1242 <h3><?php esc_html_e( 'Which email address should the plugin use as a from address?', 'wp-2fa' ); ?></h3>
1243 <p class="description">
1244 <?php esc_html_e( 'Use these settings to customize the "from" name and email address for all correspondence sent from our plugin.', 'wp-2fa' ); ?>
1245 </p>
1246 <table class="form-table">
1247 <tbody>
1248 <tr>
1249 <th><label for="2fa-method"><?php esc_html_e( 'From email & name', 'wp-2fa' ); ?></label>
1250 </th>
1251 <td>
1252 <fieldset class="contains-hidden-inputs">
1253 <label for="use-defaults">
1254 <input type="radio" name="email_from_setting" id="use-defaults" value="use-defaults"
1255 <?php checked( WP2FA::get_wp2fa_email_templates( 'email_from_setting' ), 'use-defaults' ); ?>
1256 >
1257 <span><?php esc_html_e( 'Use the email address from the WordPress general settings.', 'wp-2fa' ); ?></span>
1258 </label>
1259
1260 <br/>
1261 <label for="use-custom-email">
1262 <input type="radio" name="email_from_setting" id="use-custom-email" value="use-custom-email"
1263 <?php checked( WP2FA::get_wp2fa_email_templates( 'email_from_setting' ), 'use-custom-email' ); ?>
1264 data-unhide-when-checked=".custom-from-inputs">
1265 <span><?php esc_html_e( 'Use another email address', 'wp-2fa' ); ?></span>
1266 </label>
1267 <fieldset class="hidden custom-from-inputs">
1268 <br/>
1269 <span><?php esc_html_e( 'Email Address:', 'wp-2fa' ); ?></span> <input type="text" id="custom_from_email_address" name="custom_from_email_address" value="<?php echo WP2FA::get_wp2fa_email_templates( 'custom_from_email_address' ); ?>"><br><br>
1270 <span><?php esc_html_e( 'Display Name:', 'wp-2fa' ); ?></span> <input type="text" id="custom_from_display_name" name="custom_from_display_name" value="<?php echo WP2FA::get_wp2fa_email_templates( 'custom_from_display_name' ); ?>">
1271 </fieldset>
1272
1273 </fieldset>
1274 </td>
1275 </tr>
1276 </tbody>
1277 </table>
1278
1279 <br>
1280 <hr>
1281
1282 <h3><?php esc_html_e( 'Email delivery test', 'wp-2fa' ); ?></h3>
1283 <p class="description">
1284 <?php esc_html_e( 'The plugin sends emails with one-time codes, blocked account notifications and more. Use the button below to confirm the plugin can successfully send emails.', 'wp-2fa' ); ?>
1285 </p>
1286 <p>
1287 <button type="button" name="test_email_config_test"
1288 class="button js-button-test-email-trigger"
1289 data-email-id="config_test"
1290 data-nonce="<?php echo esc_attr( wp_create_nonce( 'wp-2fa-email-test-config_test' ) ); ?>">
1291 <?php esc_html_e( 'Test email delivery', 'wp-2fa' ); ?>
1292 </button>
1293 </p>
1294
1295 <br>
1296 <hr>
1297
1298 <?php
1299 }
1300
1301 /**
1302 * Creates the email notification definitions.
1303 *
1304 * @return EmailTemplate[]
1305 */
1306 public function get_email_notification_definitions( ) {
1307 $result = [
1308 new EmailTemplate(
1309 'login_code',
1310 esc_html__( 'Login code email', 'wp-2fa' ),
1311 esc_html__( 'This is the email sent to a user when a login code is required.', 'wp-2fa' )
1312 ),
1313 new EmailTemplate(
1314 'account_locked',
1315 esc_html__( 'User account locked email', 'wp-2fa' ),
1316 esc_html__( 'This is the email sent to a user upon grace period expiry.', 'wp-2fa' )
1317 ),
1318 new EmailTemplate(
1319 'account_unlocked',
1320 esc_html__( 'User account unlocked email', 'wp-2fa' ),
1321 esc_html__( 'This is the email sent to a user when the user\'s account has been unlocked.', 'wp-2fa' )
1322 )
1323 ];
1324
1325 $result[0]->setCanBeToggled(false);
1326 $result[1]->setEmailContentId('user_account_locked');
1327 $result[2]->setEmailContentId('user_account_unlocked');
1328 return $result;
1329 }
1330 /**
1331 * Email settings
1332 */
1333 private function email_settings() {
1334 $custom_user_page_id = WP2FA::get_wp2fa_setting( 'custom-user-page-id' );
1335 $email_template_definitions = $this->get_email_notification_definitions();
1336 ?>
1337 <h1><?php esc_html_e( 'Email Templates', 'wp-2fa' ); ?></h1>
1338 <?php foreach ($email_template_definitions as $email_template) : ?>
1339 <?php $template_id = $email_template->getId(); ?>
1340 <h3><?php echo $email_template->getTitle(); ?></h3>
1341 <p class="description"><?php echo $email_template->getDescription(); ?></p>
1342 <table class="form-table">
1343 <tbody>
1344 <?php if ($email_template->canBeToggled()): ?>
1345 <tr>
1346 <th><label for="send_<?php echo $template_id; ?>_email"><?php esc_html_e( 'Send this email', 'wp-2fa' ); ?></label></th>
1347 <td>
1348 <fieldset>
1349 <input type="checkbox" id="send_<?php echo $template_id; ?>_email" name="send_<?php echo $template_id; ?>_email" value="enable_<?php echo $template_id; ?>_email"
1350 <?php checked( 'enable_' . $template_id . '_email', WP2FA::get_wp2fa_email_templates( 'send_' . $template_id . '_email' )); ?>
1351 >
1352 <label for="send_<?php echo $template_id; ?>_email"><?php esc_html_e( 'Uncheck to disable this message.', 'wp-2fa' ); ?></label>
1353 </fieldset>
1354 </td>
1355 </tr>
1356 <?php endif; ?>
1357 <?php $template_id = $email_template->getEmailContentId(); ?>
1358 <tr>
1359 <th><label for="<?php echo $template_id; ?>_email_subject"><?php esc_html_e( 'Email subject', 'wp-2fa' ); ?></label></th>
1360 <td>
1361 <fieldset>
1362 <input type="text" id="<?php echo $template_id; ?>_email_subject" name="<?php echo $template_id; ?>_email_subject" class="large-text" value="<?php esc_html_e( WP2FA::get_wp2fa_email_templates( $template_id . '_email_subject' ) ); ?>">
1363 </fieldset>
1364 </td>
1365 </tr>
1366 <tr>
1367 <th>
1368 <label for="<?php echo $template_id; ?>_email_body"><?php esc_html_e( 'Email body', 'wp-2fa' ); ?></label>
1369 </br>
1370 <label for="<?php echo $template_id; ?>_email_tags" style="font-weight: 400;"><?php esc_html_e( 'Available template tags:', 'wp-2fa' ); ?></label>
1371 </br>
1372 </br>
1373 <span style="font-weight: 400;">
1374 {site_url}</br>
1375 {site_name}</br>
1376 {grace_period}</br>
1377 {user_login_name}</br>
1378 {user_first_name}</br>
1379 {user_last_name}</br>
1380 {user_display_name}</br>
1381 {login_code}
1382 <?php
1383 if ( ! empty( $custom_user_page_id ) ) {
1384 echo '</br>{2fa_settings_page_url}';
1385 }
1386 ?>
1387 </span>
1388 </th>
1389 <td>
1390 <fieldset>
1391 <?php
1392 $message = WP2FA::get_wp2fa_email_templates( $template_id . '_email_body' );
1393 $content = $message;
1394 $editor_id = $template_id . '_email_body';
1395 $settings = array(
1396 'media_buttons' => false,
1397 'editor_height' => 200,
1398 );
1399 wp_editor( $content, $editor_id, $settings );
1400 ?>
1401 </fieldset>
1402 <p>
1403 <button type="button" name="test_email_<?php echo esc_attr( $template_id ); ?>"
1404 class="button js-button-test-email-trigger"
1405 data-email-id="<?php echo esc_attr( $template_id ); ?>"
1406 data-nonce="<?php echo esc_attr( wp_create_nonce( 'wp-2fa-email-test-' . $template_id ) ); ?>">
1407 <?php esc_html_e( 'Send test email', 'wp-2fa' ); ?>
1408 </button>
1409 </p>
1410 </td>
1411 </tr>
1412 </tbody>
1413 </table>
1414
1415 <br>
1416 <hr>
1417 <?php endforeach; ?>
1418 <?php
1419 }
1420
1421 /**
1422 * Validate email templates before saving
1423 *
1424 * @param array $input The settings array.
1425 */
1426 public function validate_and_sanitize_email( $input ) {
1427
1428 // Bail if user doesnt have permissions to be here.
1429 if ( ! current_user_can( 'manage_options' ) ) {
1430 return;
1431 }
1432
1433 if ( empty( $_POST ) || ! wp_verify_nonce( $_POST['_wpnonce'], WP_2FA_PREFIX .'email_settings-options' ) && ! wp_verify_nonce( $_POST['_wpnonce'], WP_2FA_PREFIX .'settings-options' ) || ! wp_verify_nonce( $_POST['_wpnonce'], WP_2FA_PREFIX . 'email_settings-options' ) && ! wp_verify_nonce( $_POST['_wpnonce'], WP_2FA_PREFIX . 'settings-options' ) ) {
1434 die( esc_html__( 'Nonce verification failed.', 'wp-2fa' ) );
1435 }
1436
1437 if ( isset( $_POST['email_from_setting'] ) && 'use-defaults' === $_POST['email_from_setting'] || isset( $_POST['email_from_setting'] ) && 'use-custom-email' === $_POST['email_from_setting'] ) {
1438 $output['email_from_setting'] = sanitize_text_field( wp_unslash( $_POST['email_from_setting'] ) );
1439 }
1440
1441 if ( isset( $_POST['email_from_setting'] ) && 'use-custom-email' === $_POST['email_from_setting'] && isset( $_POST['custom_from_email_address'] ) && empty( $_POST['custom_from_email_address'] ) ) {
1442 add_settings_error(
1443 WP_2FA_SETTINGS_NAME,
1444 esc_attr( 'email_from_settings_error' ),
1445 esc_html__( 'Please provide an email address', 'wp-2fa' ),
1446 'error'
1447 );
1448 $output['custom_from_email_address'] = '';
1449 }
1450
1451 if ( isset( $_POST['email_from_setting'] ) && 'use-custom-email' === $_POST['email_from_setting'] && isset( $_POST['custom_from_display_name'] ) && empty( $_POST['custom_from_display_name'] ) ) {
1452 add_settings_error(
1453 WP_2FA_SETTINGS_NAME,
1454 esc_attr( 'display_name_settings_error' ),
1455 esc_html__( 'Please provide a display name.', 'wp-2fa' ),
1456 'error'
1457 );
1458 $output['custom_from_email_address'] = '';
1459 }
1460
1461 if ( isset( $_POST['custom_from_email_address'] ) && ! empty( $_POST['custom_from_email_address'] ) ) {
1462 if ( ! filter_var( $_POST['custom_from_email_address'], FILTER_VALIDATE_EMAIL ) ) {
1463 add_settings_error(
1464 WP_2FA_SETTINGS_NAME,
1465 esc_attr( 'email_invalid_settings_error' ),
1466 esc_html__( 'Please provide a valid email address. Your email address has not been updated.', 'wp-2fa' ),
1467 'error'
1468 );
1469 }
1470 $output['custom_from_email_address'] = sanitize_email( wp_unslash( $_POST['custom_from_email_address'] ) );
1471 }
1472
1473 if ( isset( $_POST['custom_from_display_name'] ) && ! empty( $_POST['custom_from_display_name'] ) ) {
1474 // Check if the string contains HTML/tags.
1475 preg_match( "/<\/?\w+((\s+\w+(\s*=\s*(?:\".*?\"|'.*?'|[^'\">\s]+))?)+\s*|\s*)\/?>/", $_POST['custom_from_display_name'], $matches );
1476 if ( count( $matches ) > 0 ) {
1477 add_settings_error(
1478 WP_2FA_SETTINGS_NAME,
1479 esc_attr( 'display_name_invalid_settings_error' ),
1480 esc_html__( 'Please only use alphanumeric text. Your display name has not been updated.', 'wp-2fa' ),
1481 'error'
1482 );
1483 } else {
1484 $output['custom_from_display_name'] = sanitize_text_field( wp_unslash( $_POST['custom_from_display_name'] ) );
1485 }
1486 }
1487
1488 if ( isset( $_POST['login_code_email_subject'] ) ) {
1489 $output['login_code_email_subject'] = wp_kses_post( wp_unslash( $_POST['login_code_email_subject'] ) );
1490 }
1491
1492 if ( isset( $_POST['login_code_email_body'] ) ) {
1493 $output['login_code_email_body'] = wpautop( wp_kses_post( wp_unslash( $_POST['login_code_email_body'] ) ) );
1494 }
1495
1496 if ( isset( $_POST['user_account_locked_email_subject'] ) ) {
1497 $output['user_account_locked_email_subject'] = wp_kses_post( wp_unslash( $_POST['user_account_locked_email_subject'] ) );
1498 }
1499
1500 if ( isset( $_POST['user_account_locked_email_body'] ) ) {
1501 $output['user_account_locked_email_body'] = wpautop( wp_kses_post( wp_unslash( $_POST['user_account_locked_email_body'] ) ) );
1502 }
1503
1504 if ( isset( $_POST['user_account_unlocked_email_subject'] ) ) {
1505 $output['user_account_unlocked_email_subject'] = wp_kses_post( wp_unslash( $_POST['user_account_unlocked_email_subject'] ) );
1506 }
1507
1508 if ( isset( $_POST['user_account_unlocked_email_body'] ) ) {
1509 $output['user_account_unlocked_email_body'] = wpautop( wp_kses_post( wp_unslash( $_POST['user_account_unlocked_email_body'] ) ) );
1510 }
1511
1512 if ( isset( $_POST['send_account_locked_email'] ) && 'enable_account_locked_email' === $_POST['send_account_locked_email'] ) {
1513 $output['send_account_locked_email'] = sanitize_text_field( $_POST['send_account_locked_email'] );
1514 }
1515
1516 if ( isset( $_POST['send_account_unlocked_email'] ) && 'enable_account_unlocked_email' === $_POST['send_account_unlocked_email'] ) {
1517 $output['send_account_unlocked_email'] = sanitize_text_field( $_POST['send_account_unlocked_email'] );
1518 }
1519
1520 // Remove duplicates from settings errors. We do this as this sanitization callback is actually fired twice, so we end up with duplicates when saving the settings for the FIRST TIME only. The issue is not present once the settings are in the DB as the sanitization wont fire again. For details on this core issue - https://core.trac.wordpress.org/ticket/21989.
1521 global $wp_settings_errors;
1522 if ( isset( $wp_settings_errors ) ) {
1523 $errors = array_map( 'unserialize', array_unique( array_map( 'serialize', $wp_settings_errors ) ) );
1524 $wp_settings_errors = $errors;
1525 }
1526
1527 if ( isset( $output ) ) {
1528 return $output;
1529 } else {
1530 return;
1531 }
1532
1533 }
1534
1535 /**
1536 * Creates a new page with our shortcode present.
1537 */
1538 public function generate_custom_user_profile_page( $page_slug ) {
1539 // Bail if user doesnt have permissions to be here.
1540 if ( ! current_user_can( 'manage_options' ) ) {
1541 return;
1542 }
1543
1544 // Check if a page with slug exists.
1545 $page_exists = $this->get_post_by_post_name( $page_slug, 'page' );
1546 if ( $page_exists ) {
1547 // Seeing as the page exisits, return its ID.
1548 return $page_exists->ID;
1549 }
1550
1551 $generated_by_message = '<p>'.esc_html__( 'Page generated by', 'wp-2fa' );
1552 $generated_by_message .= ' <a href="https://www.wpwhitesecurity.com/wordpress-plugins/wp-2fa/" target="_blank">'.esc_html__( 'WP 2FA Plugin', 'wp-2fa' ).'</a>';
1553 $generated_by_message .= '</p>';
1554
1555 $user = wp_get_current_user();
1556 $post_data = array(
1557 'post_title' => 'WP 2FA User Profile',
1558 'post_name' => $page_slug,
1559 'post_content' => '[wp-2fa-setup-form] ' . $generated_by_message,
1560 'post_status' => 'publish',
1561 'post_author' => $user->ID,
1562 'post_type' => 'page',
1563 );
1564
1565 // Lets insert the post now.
1566 $result = wp_insert_post( $post_data );
1567
1568 if ( $result && ! is_wp_error( $result ) ) {
1569 $post_id = $result;
1570 set_transient( WP_2FA_PREFIX . 'new_custom_page_created', true, 60 );
1571 set_site_transient( WP_2FA_PREFIX .'new_custom_page_created', true, 60 );
1572 return $post_id;
1573 }
1574 }
1575
1576 /**
1577 * Check if page with slug exisits.
1578 */
1579 public function get_post_by_post_name( $slug = '', $post_type = '' ) {
1580 if ( ! $slug || ! $post_type ) {
1581 return false;
1582 }
1583
1584 $post_object = get_page_by_path( $slug, OBJECT, $post_type );
1585
1586 if ( ! $post_object ) {
1587 return false;
1588 }
1589
1590 return $post_object;
1591 }
1592
1593 /**
1594 * Add our custom state to our created page.
1595 */
1596 public function add_display_post_states( $post_states, $post ) {
1597 if ( ! empty( WP2FA::get_wp2fa_setting( 'custom-user-page-id' ) ) ) {
1598 if ( WP2FA::get_wp2fa_setting( 'custom-user-page-id' ) === $post->ID ) {
1599 $post_states['wp_2fa_page_for_user'] = __( 'WP 2FA User Page', 'wp-2fa' );
1600 }
1601 }
1602
1603 return $post_states;
1604 }
1605
1606 /**
1607 * Handles sending of an email. It sets necessary header such as content type and custom from email address and name.
1608 *
1609 * @param string $recipient_email Email address to send message to.
1610 * @param string $subject Email subject.
1611 * @param string $message Message contents.
1612 *
1613 * @return bool Whether the email contents were sent successfully.
1614 */
1615 public static function send_email( $recipient_email, $subject, $message ) {
1616
1617 // Specify our desired headers.
1618 $headers = 'Content-type: text/html;charset=utf-8' . "\r\n";
1619
1620 if ( 'use-custom-email' === WP2FA::get_wp2fa_email_templates( 'email_from_setting' ) ) {
1621 $headers .= 'From: ' . WP2FA::get_wp2fa_email_templates( 'custom_from_display_name' ) . ' <' . WP2FA::get_wp2fa_email_templates( 'custom_from_email_address' ) . '>' . "\r\n";
1622 } else {
1623 $headers .= 'From: ' . get_bloginfo( 'name' ) . ' <' . get_bloginfo('admin_email') . '>' . "\r\n";
1624 }
1625
1626 // Fire our email.
1627 return wp_mail( $recipient_email, $subject, $message, $headers );
1628
1629 }
1630
1631 /**
1632 * Turns user roles data in any form and shape to an array of strings.
1633 *
1634 * @param mixed $value User role names (slugs) as raw value.
1635 *
1636 * @return string[] List of user role names (slugs).
1637 */
1638 public static function extract_roles_from_input( $value ) {
1639 if ( is_array( $value ) ) {
1640 return $value;
1641 }
1642
1643 if ( is_string( $value ) && ! empty( $value ) ) {
1644 return explode( ',', $value );
1645 }
1646
1647 return [];
1648 }
1649
1650 /**
1651 * Determine if any BG processes are currently running.
1652 *
1653 * @return int|false Number of jobs.
1654 */
1655 public function get_current_number_of_active_bg_processes() {
1656 global $wpdb;
1657
1658 $bg_jobs = $wpdb->get_results(
1659 "SELECT option_value FROM $wpdb->options
1660 WHERE option_name LIKE '%_2fa_bg_%'"
1661 );
1662
1663 return count( $bg_jobs );
1664 }
1665
1666 /**
1667 * Cancel BG processes.
1668 *
1669 */
1670 public function cancel_bg_processes() {
1671 global $wpdb;
1672 $wpdb->query("DELETE FROM $wpdb->options WHERE option_name LIKE '%_2fa_bg_%'");
1673
1674 $cron_hook_identifiers = [ '2fa_check_grace_period_status', '2fa_bg_update_user_meta', '2fa_bg_wipe_all_user_data', '2fa_bg_remove_enabled_methods' ];
1675
1676 foreach ( $cron_hook_identifiers as $cron_hook_identifier ) {
1677 $cleared_jobs = wp_clear_scheduled_hook( $wpdb->prefix.$cron_hook_identifier );
1678 }
1679
1680 wp_send_json_success( $cleared_jobs );
1681 }
1682
1683 /**
1684 * Checks if the backup codes option is globally enabled
1685 *
1686 * @return bool
1687 */
1688 public static function are_backup_codes_enabled() {
1689
1690 if ( null === self::$backupCodesEnabled ) {
1691 self::$backupCodesEnabled = false;
1692
1693 if ( 'yes' === WP2FA::get_wp2fa_setting( 'backup_codes_enabled' ) ) {
1694 self::$backupCodesEnabled = true;
1695 }
1696 }
1697
1698 return self::$backupCodesEnabled;
1699 }
1700 }
1701