PluginProbe ʕ •ᴥ•ʔ
WP 2FA – Two-factor authentication for WordPress / 1.4.1
WP 2FA – Two-factor authentication for WordPress v1.4.1
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
SettingsPage.php 5 years ago SetupWizard.php 5 years ago UserNotices.php 5 years ago UserProfile.php 5 years ago UserRegistered.php 5 years ago
SettingsPage.php
1947 lines
1 <?php // phpcs:ignore
2
3 namespace WP2FA\Admin;
4
5 use \WP2FA\WP2FA as WP2FA;
6 use \WP2FA\Authenticator\Authentication as Authentication;
7 use \WP2FA\BackgroundProcessing\ProcessUserMetaUpdate as ProcessUserMetaUpdate;
8
9 /**
10 * SettingsPage - Class for handling settings
11 */
12 class SettingsPage {
13
14 /**
15 * Create admin menu entru and settings page
16 */
17 public function create_settings_admin_menu() {
18 // Create sub menu item.
19 add_options_page(
20 esc_html__( 'WP 2FA Settings', 'wp-2fa' ),
21 esc_html__( 'Two-factor Authentication', 'wp-2fa' ),
22 'manage_options',
23 'wp-2fa-settings',
24 array( $this, 'settings_page_render' )
25 );
26
27 // Register our settings page.
28 register_setting(
29 'wp_2fa_settings',
30 'wp_2fa_settings',
31 array( $this, 'validate_and_sanitize' )
32 );
33
34 register_setting(
35 'wp_2fa_email_settings',
36 'wp_2fa_email_settings',
37 array( $this, 'validate_and_sanitize_email' )
38 );
39 }
40
41 /**
42 * Create admin menu entru and settings page
43 */
44 public function create_settings_admin_menu_multisite() {
45 // Create sub menu item.
46 add_submenu_page(
47 'settings.php',
48 esc_html__( 'WP 2FA Settings', 'wp-2fa' ),
49 esc_html__( 'Two-factor Authentication', 'wp-2fa' ),
50 'manage_options',
51 'wp-2fa-settings',
52 array( $this, 'settings_page_render' )
53 );
54 // Register our settings page.
55 register_setting(
56 'wp_2fa_settings',
57 'wp_2fa_settings',
58 array( $this, 'validate_and_sanitize' )
59 );
60
61 register_setting(
62 'wp_2fa_email_settings',
63 'wp_2fa_email_settings',
64 array( $this, 'validate_and_sanitize_email' )
65 );
66 }
67
68 /**
69 * Render the settings
70 */
71 public function settings_page_render() {
72 $user = wp_get_current_user();
73 $user_id = (int) $user->ID;
74 if ( ! empty( WP2FA::get_wp2fa_setting( '2fa_settings_last_updated_by' ) ) ) {
75 $main_user = (int) WP2FA::get_wp2fa_setting( '2fa_settings_last_updated_by' );
76 } else {
77 $main_user = '';
78 }
79
80 // Check if new user page has been published.
81 if ( ! empty( get_transient( 'wp_2fa_new_custom_page_created' ) ) ) {
82 delete_transient( 'wp_2fa_new_custom_page_created' );
83 $new_page_id = WP2FA::get_wp2fa_setting( 'custom-user-page-id' );
84 $new_page_permalink = get_permalink( $new_page_id );
85 ?>
86 <div class="wp2fa-modal micromodal-slide" id="new-page-created" aria-hidden="false">
87 <div class="modal__overlay" tabindex="-1" data-micromodal-close>
88 <div class="modal__container" role="dialog" aria-modal="true" aria-labelledby="modal-1-title">
89 <main class="modal__content" id="modal-1-content">
90 <h3><?php esc_html_e( 'The plugin created the 2FA settings page with the URL:', 'wp-2fa' ); ?></h3>
91 <h4>
92 <a target="_blank" href="<?php echo esc_url( $new_page_permalink ); ?>"><?php echo esc_attr( $new_page_permalink ); ?></a>
93 </h4>
94 <p>
95 <?php esc_html_e( 'You can edit this page using the page editor, like you do with all other pages.', 'wp-2fa' ); ?>
96 </p>
97 <p>
98 <?php esc_html_e( 'Use the', 'wp-2fa' ); ?> <strong><?php esc_html_e( '{2fa_settings_page_url}', 'wp-2fa' ); ?></strong> <?php esc_html_e( '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' ); ?>
99 </p>
100 </main>
101 <footer class="modal__footer">
102 <a href="#" class="modal__btn modal__btn-primary" data-trigger-remove-new-page-notice onclick="MicroModal.close('new-page-created');"><?php esc_html_e( 'OK', 'wp-2fa' ); ?></a>
103 </footer>
104 </div>
105 </div>
106 </div>
107 <script>
108 MicroModal.show('new-page-created');
109 </script>
110 <?php
111 }
112 ?>
113 <div class="wp2fa-modal micromodal-slide" id="notify-users" aria-hidden="false">
114 <div class="modal__overlay" tabindex="-1" data-micromodal-close>
115 <div class="modal__container" role="dialog" aria-modal="true" aria-labelledby="modal-1-title">
116 <main class="modal__content" id="modal-1-content">
117 <h3><?php esc_html_e( 'Notify users?', 'wp-2fa' ); ?></h3>
118 <p>
119 <?php esc_html_e( 'Would you like to notify all applicable users based on your changes?', 'wp-2fa' ); ?>
120 </p>
121 </main>
122 <footer class="modal__footer">
123 <a href="#" id="send-notification-email" class="modal__btn modal__btn"><?php esc_html_e( 'Notify users & save settings', 'wp-2fa' ); ?></a>
124 <a href="#" id="save-settings" class="modal__btn modal__btn-primary"><?php esc_html_e( 'Save settings only', 'wp-2fa' ); ?></a>
125 </footer>
126 </div>
127 </div>
128 </div>
129 <div class="wrap wp-2fa-settings-wrapper">
130 <h2><?php esc_html_e( 'WP 2FA Settings', 'wp-2fa' ); ?></h2>
131 <hr>
132 <?php if ( ! empty( WP2FA::get_wp2fa_setting( 'limit_access' ) ) && $main_user !== $user->ID ) { ?>
133
134 <?php
135 echo esc_html__( 'These settings have been disabled by your site administrator, please contact them for further assistance.', 'wp-2fa' );
136 ?>
137
138 <?php } else { ?>
139
140 <div class="nav-tab-wrapper">
141 <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>
142 <a href="
143 <?php
144 echo esc_url(
145 add_query_arg(
146 array(
147 'page' => 'wp-2fa-settings',
148 'tab' => 'email-settings',
149 ),
150 network_admin_url( 'admin.php' )
151 )
152 );
153 ?>
154 " class="nav-tab <?php echo isset( $_REQUEST['tab'] ) && 'email-settings' === $_REQUEST['tab'] ? 'nav-tab-active' : ''; ?>"><?php _e( 'Email Settings & Templates', 'wp-2fa' ); ?></a>
155 </div>
156 <?php
157 if ( ! current_user_can( 'manage_options' ) ) {
158 return;
159 }
160 if ( WP2FA::is_this_multisite() ) {
161 $action = 'edit.php?action=update_wp2fa_network_options';
162 } else {
163 $action = 'options.php';
164 }
165 if ( ! isset( $_REQUEST['tab'] ) || isset( $_REQUEST['tab'] ) && '2fa-settings' === $_REQUEST['tab'] ) :
166 ?>
167 <br/>
168 <?php
169 printf( '<p class="description">%1$s <a href="mailto:support@wpwhitesecurity.com">%2$s</a></p>', 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' ), esc_html__( 'support@wpwhitesecurity.com', 'wp-2fa' ) );
170 ?>
171 <br/>
172 <?php $total_users = count_users(); ?>
173 <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']; ?>">
174 <?php
175 if ( ! current_user_can( 'manage_options' ) ) {
176 return;
177 }
178
179 settings_fields( 'wp_2fa_settings' );
180 $this->select_method_setting();
181 $this->select_enforcment_policy_setting();
182 $this->user_profile_settings();
183 $this->excluded_roles_or_users_setting();
184 if ( WP2FA::is_this_multisite() ) {
185 $this->excluded_network_sites();
186 }
187 $this->grace_period_setting();
188 $this->disable_2fa_removal_setting();
189 $this->limit_settings_access();
190 $this->remove_data_upon_uninstall();
191 submit_button();
192 ?>
193 </form>
194 <?php endif; ?>
195
196 <?php
197 if ( WP2FA::is_this_multisite() ) {
198 $action = 'edit.php?action=update_wp2fa_network_email_options';
199 } else {
200 $action = 'options.php';
201 }
202 ?>
203
204 <?php if ( isset( $_REQUEST['tab'] ) && 'email-settings' === $_REQUEST['tab'] ) : ?>
205 <br/>
206 <?php
207 printf( '<p class="description">%1$s <a href="mailto:support@wpwhitesecurity.com">%2$s</a></p>', 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' ), esc_html__( 'support@wpwhitesecurity.com', 'wp-2fa' ) );
208 ?>
209 <br/>
210 <form action='<?php echo esc_attr( $action ); ?>' method='post' autocomplete="off">
211 <?php
212 if ( ! current_user_can( 'manage_options' ) ) {
213 return;
214 }
215
216 settings_fields( 'wp_2fa_email_settings' );
217 $this->email_from_settings();
218 $this->email_settings();
219 submit_button( 'Save email settings and templates' );
220 ?>
221 </form>
222 <?php endif; ?>
223
224 <?php } ?>
225 </div>
226 <?php
227 }
228
229 /**
230 * General settings
231 */
232 private function select_method_setting() {
233 ?>
234 <h3><?php esc_html_e( 'Which two-factor authentication methods can your users use on this website?', 'wp-2fa' ); ?></h3>
235 <p class="description">
236 <?php esc_html_e( 'When you disable one of the below 2FA methods none of your users can use it.', 'wp-2fa' ); ?>
237 </p>
238 <table class="form-table">
239 <tbody>
240 <tr>
241 <th><label for="2fa-method"><?php esc_html_e( 'Select the methods:', 'wp-2fa' ); ?></label></th>
242 <td>
243 <fieldset>
244 <label for="totp">
245 <input type="checkbox" id="totp" name="wp_2fa_settings[enable_totp]" value="enable_totp"
246 <?php checked( 'enable_totp', WP2FA::get_wp2fa_setting( 'enable_totp' ), true ); ?>
247 >
248 <?php esc_html_e( 'one-time code via 2FA App (TOTP) - ', 'wp-2fa' ); ?><a href="https://www.wpwhitesecurity.com/support/kb/configuring-2fa-apps/?utm_source=plugin&utm_medium=referral&utm_campaign=wp2fa&utm_content=settings+pages" target="_blank"><?php esc_html_e( 'complete list of supported 2FA apps.', 'wp-2fa' ); ?></a>
249 </label>
250 <br/>
251 <label for="email">
252 <input type="checkbox" id="hotp" name="wp_2fa_settings[enable_email]" value="enable_email"
253 <?php checked( WP2FA::get_wp2fa_setting( 'enable_email' ), 'enable_email' ); ?>
254 >
255 <?php esc_html_e( 'one-time code via email (HOTP)', 'wp-2fa' ); ?>
256 </label>
257 <br />
258 </fieldset>
259 </td>
260 </tr>
261 </tbody>
262 </table>
263 <?php
264 }
265
266 /**
267 * Policy settings
268 */
269 private function select_enforcment_policy_setting() {
270 ?>
271 <h3><?php esc_html_e( 'Do you want to enforce 2FA for some, or all the users? ', 'wp-2fa' ); ?></h3>
272 <p class="description">
273 <?php esc_html_e( 'When you enforce 2FA the users will be prompted to configure 2FA the next time they login. Users have a grace period for configuring 2FA. You can configure the grace period and also exclude user(s) or role(s) in this settings page. ', 'wp-2fa' ); ?> <a href="https://www.wpwhitesecurity.com/support/kb/configure-2fa-policies-enforce/?utm_source=plugin&utm_medium=referral&utm_campaign=wp2fa&utm_content=settings+pages" target="_blank"><?php esc_html_e( 'Learn more.', 'wp-2fa' ); ?></a>
274 </p>
275 <table class="form-table">
276 <tbody>
277 <tr>
278 <th><label for="enforcment-policy"><?php esc_html_e( 'Enforce 2FA on:', 'wp-2fa' ); ?></label></th>
279 <td>
280 <fieldset class="contains-hidden-inputs">
281 <label for="all-users">
282 <input type="radio" name="wp_2fa_settings[enforcment-policy]" id="all-users" value="all-users"
283 <?php checked( WP2FA::get_wp2fa_setting( 'enforcment-policy' ), 'all-users' ); ?>
284 >
285 <span><?php esc_html_e( 'All users', 'wp-2fa' ); ?></span>
286 </label>
287
288 <br/>
289 <label for="certain-roles-only">
290 <input type="radio" name="wp_2fa_settings[enforcment-policy]" id="certain-roles-only" value="certain-roles-only"
291 <?php checked( WP2FA::get_wp2fa_setting( 'enforcment-policy' ), 'certain-roles-only' ); ?>
292 <?php checked( WP2FA::get_wp2fa_setting( 'enforcment-policy' ), 'certain-users-only' ); ?>
293 data-unhide-when-checked=".certain-roles-only-inputs, .certain-users-only-inputs">
294 <span><?php esc_html_e( 'Only for specific users and roles', 'wp-2fa' ); ?></span>
295 </label>
296 <fieldset class="hidden certain-roles-only-inputs">
297 <br/>
298 <input type="text" id="enforced_roles_search" placeholder="Search roles">
299 <input type="hidden" id="enforced_roles" name="wp_2fa_settings[enforced_roles]" value="<?php echo esc_attr( WP2FA::get_wp2fa_setting( 'enforced_roles' ) ); ?>">
300 <div id="enforced_roles_buttons"></div>
301 </fieldset>
302 <fieldset class="hidden certain-users-only-inputs">
303 <br/>
304 <input type="text" id="enforced_users_search" placeholder="Search users">
305 <input type="hidden" id="enforced_users" name="wp_2fa_settings[enforced_users]" value="<?php echo esc_attr( WP2FA::get_wp2fa_setting( 'enforced_users' ) ); ?>">
306 <div id="enforced_users_buttons"></div>
307 </fieldset>
308
309 <br/>
310 <label for="do-not-enforce">
311 <input type="radio" name="wp_2fa_settings[enforcment-policy]" id="do-not-enforce" value="do-not-enforce"
312 <?php checked( WP2FA::get_wp2fa_setting( 'enforcment-policy' ), 'do-not-enforce' ); ?>
313 >
314 <span><?php esc_html_e( 'Do not enforce on any users', 'wp-2fa' ); ?></span>
315 </label>
316 <br/>
317 </fieldset>
318 </td>
319 </tr>
320 </tbody>
321 </table>
322 <?php
323 }
324
325 /**
326 * User profile settings
327 */
328 private function user_profile_settings() {
329 ?>
330 <h3><?php esc_html_e( 'Can users access the WordPress dashboard or you have custom profile pages? ', 'wp-2fa' ); ?></h3>
331 <p class="description">
332 <?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>
333 </p>
334 <table class="form-table">
335 <tbody>
336 <tr>
337 <th><label for="enforcment-policy"><?php esc_html_e( 'Create custom 2FA settings page', 'wp-2fa' ); ?></label></th>
338 <td>
339 <fieldset>
340 <label class="radio-inline">
341 <input id="use_custom_page" type="radio" name="wp_2fa_settings[create-custom-user-page]" value="yes"
342 <?php checked( WP2FA::get_wp2fa_setting( 'create-custom-user-page' ), 'yes' ); ?>
343 >
344 <?php esc_html_e( 'Yes', 'wp-2fa' ); ?>
345 </label>
346 <label class="radio-inline">
347 <input id="dont_use_custom_page" type="radio" name="wp_2fa_settings[create-custom-user-page]" value="no"
348 <?php checked( WP2FA::get_wp2fa_setting( 'create-custom-user-page' ), 'no' ); ?>
349 <?php checked( WP2FA::get_wp2fa_setting( 'create-custom-user-page' ), '' ); ?>
350 >
351 <?php esc_html_e( 'No', 'wp-2fa' ); ?>
352 </label>
353 </fieldset>
354 </td>
355 </tr>
356 <tr class="custom-user-page-setting disabled">
357 <th><label for="enforcment-policy"><?php esc_html_e( 'Custom 2FA settings page', 'wp-2fa' ); ?></label></th>
358 <td>
359 <fieldset>
360 <?php
361 if ( ! empty( WP2FA::get_wp2fa_setting( 'custom-user-page-id' ) ) ) {
362 $custom_slug = get_post_field( 'post_name', get_post( WP2FA::get_wp2fa_setting( 'custom-user-page-id' ) ) );
363 } else {
364 $custom_slug = WP2FA::get_wp2fa_setting( 'custom-user-page-url' );
365 }
366 ?>
367 <?php esc_html_e( 'Specify a URL for the Custom 2FA settings page URL:', 'wp-2fa' ); ?> <?php echo trailingslashit( get_site_url() ); ?>
368 <input type="text" id="custom-user-page-url" name="wp_2fa_settings[custom-user-page-url]" value="<?php echo sanitize_text_field( $custom_slug ); ?>">
369 </fieldset>
370 <?php
371 if ( ! empty( WP2FA::get_wp2fa_setting( 'custom-user-page-id' ) ) ) {
372 $edit_post_link = get_edit_post_link( WP2FA::get_wp2fa_setting( 'custom-user-page-id' ) );
373 $view_post_link = get_permalink( WP2FA::get_wp2fa_setting( 'custom-user-page-id' ) );
374 ?>
375 <br>
376 <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>
377 <?php
378 }
379 ?>
380 </td>
381 </tr>
382 </tbody>
383 </table>
384 <?php
385 }
386
387 /**
388 * Role and users exclusion settings
389 */
390 private function excluded_roles_or_users_setting() {
391 ?>
392 <br>
393 <h3><?php esc_html_e( 'Do you want to exclude any users or roles from 2FA? ', 'wp-2fa' ); ?></h3>
394 <p class="description">
395 <?php esc_html_e( 'If you are enforcing 2FA on all users but for some reason you would like to exclude individual user(s) or users with a specific role, you can exclude them below', 'wp-2fa' ); ?>
396 </p>
397 <table class="form-table">
398 <tbody>
399 <tr>
400 <th><label for="enforcment-policy"><?php esc_html_e( 'Exclude the following users', 'wp-2fa' ); ?></label></th>
401 <td>
402 <fieldset>
403 <input type="text" id="excluded_users_search" placeholder="Search user name">
404 <input type="hidden" id="excluded_users" name="wp_2fa_settings[excluded_users]"
405 value="<?php echo sanitize_text_field( WP2FA::get_wp2fa_setting( 'excluded_users' ) ); ?>">
406 <div id="excluded_users_buttons"></div>
407 </fieldset>
408 </td>
409 </tr>
410 <tr>
411 <th><label for="enforcment-policy"><?php esc_html_e( 'Exclude the following roles:', 'wp-2fa' ); ?></label></th>
412 <td>
413 <fieldset>
414 <input type="text" id="excluded_roles_search" placeholder="Search roles">
415 <input type="hidden" id="excluded_roles" name="wp_2fa_settings[excluded_roles]"
416 value="<?php echo sanitize_text_field( WP2FA::get_wp2fa_setting( 'excluded_roles' ) ); ?>">
417 <div id="excluded_roles_buttons"></div>
418 </fieldset>
419 </td>
420 </tr>
421 </tbody>
422 </table>
423 <?php
424 }
425
426 /**
427 * Role and users exclusion settings
428 */
429 private function excluded_network_sites() {
430 ?>
431 <br>
432 <h3><?php esc_html_e( 'Do you want to exclude all the users of a site from 2FA? ', 'wp-2fa' ); ?></h3>
433 <p class="description">
434 <?php esc_html_e( 'If you are enforcing 2FA on all users but for some reason you do not want to enforce it on a specific sub site, specify the sub site name below:', 'wp-2fa' ); ?>
435 </p>
436 <table class="form-table">
437 <tbody>
438 <tr>
439 <th><label for="enforcment-policy"><?php esc_html_e( 'Exclude the following sites', 'wp-2fa' ); ?></label></th>
440 <td>
441 <fieldset>
442 <input type="text" id="excluded_sites_search" placeholder="Search sites in your network">
443 <input type="hidden" id="excluded_sites" name="wp_2fa_settings[excluded_sites]"
444 value="<?php echo sanitize_text_field( WP2FA::get_wp2fa_setting( 'excluded_sites' ) ); ?>">
445 <div id="excluded_sites_buttons"></div>
446 </fieldset>
447 </td>
448 </tr>
449 </tbody>
450 </table>
451 <?php
452 }
453
454 /**
455 * Grace period settings
456 */
457 private function grace_period_setting() {
458 $user = wp_get_current_user();
459
460 $grace_period = (int) WP2FA::get_wp2fa_setting( 'grace-period' );
461 $testing = get_option( 'wp_2fa_test_grace' );
462 if ( '1' === $testing ) {
463 $grace_max = 600;
464 } else {
465 $grace_max = 10;
466 }
467 ?>
468 <br>
469 <h3><?php esc_html_e( 'Should users be asked to setup 2FA instantly or should they have a grace period?', 'wp-2fa' ); ?></h3>
470 <p class="description">
471 <?php esc_html_e( 'When you enforce 2FA on user(s) 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. 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>
472 </p>
473
474 <table class="form-table">
475 <tbody>
476 <tr>
477 <th><label for="grace-policy"><?php esc_html_e( 'Grace period:', 'wp-2fa' ); ?></label></th>
478 <td>
479 <fieldset class="contains-hidden-inputs">
480 <label for="no-grace-period">
481 <input type="radio" name="wp_2fa_settings[grace-policy]" id="no-grace-period" value="no-grace-period"
482 <?php checked( WP2FA::get_wp2fa_setting( 'grace-policy' ), 'no-grace-period' ); ?>
483 >
484 <span><?php esc_html_e( 'Users have to configure 2FA straight away.', 'wp-2fa' ); ?></span>
485 </label>
486
487 <br/>
488 <label for="use-grace-period">
489 <input type="radio" name="wp_2fa_settings[grace-policy]" id="use-grace-period" value="use-grace-period"
490 <?php checked( WP2FA::get_wp2fa_setting( 'grace-policy' ), 'use-grace-period' ); ?>
491 data-unhide-when-checked=".grace-period-inputs">
492 <span><?php esc_html_e( 'Give users a grace period to configure 2FA', 'wp-2fa' ); ?></span>
493 </label>
494 <fieldset class="hidden grace-period-inputs">
495 <br/>
496 <input type="number" id="grace-period" name="wp_2fa_settings[grace-period]" value="<?php echo esc_attr( $grace_period ); ?>" min="1" max="<?php echo esc_attr( $grace_max ); ?>">
497 <label class="radio-inline">
498 <input type="radio" name="wp_2fa_settings[grace-period-denominator]" value="hours"
499 <?php checked( WP2FA::get_wp2fa_setting( 'grace-period-denominator' ), 'hours' ); ?>
500 >
501 <?php esc_html_e( 'Hours', 'wp-2fa' ); ?>
502 </label>
503 <label class="radio-inline">
504 <input type="radio" name="wp_2fa_settings[grace-period-denominator]" value="days"
505 <?php checked( WP2FA::get_wp2fa_setting( 'grace-period-denominator' ), 'days' ); ?>
506 >
507 <?php esc_html_e( 'Days', 'wp-2fa' ); ?>
508 </label>
509 <?php
510 $testing = get_option( 'wp_2fa_test_grace' );
511 if ( '1' === $testing ) {
512 ?>
513 <label class="radio-inline">
514 <input type="radio" name="wp_2fa_settings[grace-period-denominator]" value="seconds"
515 <?php checked( WP2FA::get_wp2fa_setting( 'grace-period-denominator' ), 'seconds' ); ?>
516 >
517 <?php esc_html_e( 'Seconds', 'wp-2fa' ); ?>
518 </label>
519 <?php
520 }
521
522 if ( ! empty( WP2FA::get_wp2fa_setting( '2fa_settings_last_updated_by' ) ) ) {
523 $last_user_to_update_settings = WP2FA::get_wp2fa_setting( '2fa_settings_last_updated_by' );
524 } else {
525 $last_user_to_update_settings = $user->ID;
526 }
527
528 ?>
529 <input type="hidden" id="2fa_main_user" name="wp_2fa_settings[2fa_settings_last_updated_by]" value="<?php echo esc_attr( $last_user_to_update_settings ); ?>">
530 </fieldset>
531 <br/>
532 </fieldset>
533 </td>
534 </tr>
535 </tbody>
536 </table>
537
538
539 <h3><?php esc_html_e( 'How often should the plugin check if a user\'s grace period is over?', 'wp-2fa' ); ?></h3>
540 <p class="description">
541 <?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' ); ?>
542 </p>
543 <table class="form-table">
544 <tbody>
545 <tr>
546 <th><label for="grace-period"><?php esc_html_e( 'Enable cron', 'wp-2fa' ); ?></label></th>
547 <td>
548 <fieldset>
549 <input type="checkbox" id="grace-cron" name="wp_2fa_settings[enable_grace_cron]" value="enable_grace_cron"
550 <?php checked( 1, WP2FA::get_wp2fa_setting( 'enable_grace_cron' ), true ); ?>
551 >
552 <?php esc_html_e( 'Use cron job to check grace periods', 'wp-2fa' ); ?>
553 </fieldset>
554 </td>
555 </tr>
556 <tr class="disabled destory-session-setting">
557 <th><label for="destory-session"><?php esc_html_e( 'Destroy session', 'wp-2fa' ); ?></label></th>
558 <td>
559 <fieldset>
560 <input type="checkbox" id="destory-session" name="wp_2fa_settings[enable_destroy_session]" value="enable_destroy_session"
561 <?php checked( 1, WP2FA::get_wp2fa_setting( 'enable_destroy_session' ), true ); ?>
562 >
563 <?php esc_html_e( 'Destory user session when grace period expires?', 'wp-2fa' ); ?>
564 </fieldset>
565 </td>
566 </tr>
567 </tbody>
568 </table>
569 <?php
570 }
571
572 /**
573 * Disable removal of 2FA settings
574 */
575 private function disable_2fa_removal_setting() {
576 $user = wp_get_current_user();
577 ?>
578 <br>
579 <h3><?php esc_html_e( 'Should users be able to disable 2FA on their user profile?', 'wp-2fa' ); ?></h3>
580 <p class="description">
581 <?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' ); ?>
582 </p>
583 <table class="form-table">
584 <tbody>
585 <tr>
586 <th><label for="hide-remove-2fa"><?php esc_html_e( 'Hide the Remove 2FA button', 'wp-2fa' ); ?></label></th>
587 <td>
588 <fieldset>
589 <input type="checkbox" id="hide-remove-2fa" name="wp_2fa_settings[hide_remove_button]" value="hide_remove_button"
590 <?php checked( 1, WP2FA::get_wp2fa_setting( 'hide_remove_button' ), true ); ?>
591 >
592 <?php esc_html_e( 'Hide the Remove 2FA button on user profile pages', 'wp-2fa' ); ?>
593 </fieldset>
594 </td>
595 </tr>
596 </tbody>
597 </table>
598 <?php
599 }
600
601 /**
602 * Limit settings setting
603 */
604 private function limit_settings_access() {
605 ?>
606 <br>
607 <h3><?php esc_html_e( 'Limit 2FA settings access?', 'wp-2fa' ); ?></h3>
608 <p class="description">
609 <?php esc_html_e( 'Use this setting to hide this plugin configuration area from all other admins.', 'wp-2fa' ); ?>
610 </p>
611 <table class="form-table">
612 <tbody>
613 <tr>
614 <th><label for="grace-period"><?php esc_html_e( 'Limited access', 'wp-2fa' ); ?></label></th>
615 <td>
616 <fieldset>
617 <input type="checkbox" id="limit_access" name="wp_2fa_settings[limit_access]" value="limit_access"
618 <?php checked( 1, WP2FA::get_wp2fa_setting( 'limit_access' ), true ); ?>
619 >
620 <?php esc_html_e( 'Hide settings from other administrators', 'wp-2fa' ); ?>
621 </fieldset>
622 </td>
623 </tr>
624 </tbody>
625 </table>
626 <?php
627 }
628
629 /**
630 * Limit settings setting
631 */
632 private function remove_data_upon_uninstall() {
633 ?>
634 <div class="danger-zone-wrapper">
635 <h3><?php esc_html_e( 'Do you want to delete the plugin data from the database upon uninstall?', 'wp-2fa' ); ?></h3>
636 <p class="description">
637 <?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' ); ?>
638 </p>
639 <table class="form-table">
640 <tbody>
641 <tr>
642 <th><label for="delete_data"><?php esc_html_e( 'Delete data', 'wp-2fa' ); ?></label></th>
643 <td>
644 <fieldset>
645 <input type="checkbox" id="elete_data" name="wp_2fa_settings[delete_data_upon_uninstall]" value="delete_data_upon_uninstall"
646 <?php checked( 1, WP2FA::get_wp2fa_setting( 'delete_data_upon_uninstall' ), true ); ?>
647 >
648 <?php esc_html_e( 'Delete data upon uninstall', 'wp-2fa' ); ?>
649 </fieldset>
650 </td>
651 </tr>
652 </tbody>
653 </table>
654 <table class="form-table hidden">
655 <tbody>
656 <tr>
657 <th></th>
658 <td>
659 <fieldset>
660 <input type="checkbox" id="notify_users" name="wp_2fa_settings[notify_users]" value="notify_users">
661 </fieldset>
662 </td>
663 </tr>
664 </tbody>
665 </table>
666 </div>
667 <?php
668 }
669
670 /**
671 * Get all useers
672 */
673 public function get_all_users() {
674 // Die if user does not have permission to view.
675 if ( ! current_user_can( 'administrator' ) ) {
676 die( 'Access Denied.' );
677 }
678 // Filter $_GET array for security.
679 $get_array = filter_input_array( INPUT_GET );
680
681 // Die if nonce verification failed.
682 if ( ! wp_verify_nonce( sanitize_text_field( $get_array['wp_2fa_nonce'] ), 'wp-2fa-settings-nonce' ) ) {
683 die( esc_html__( 'Nonce verification failed.', 'wp-2fa' ) );
684 }
685 // Fetch users.
686 $users = array();
687 if ( WP2FA::is_this_multisite() ) {
688 $users_args = array( 'blog_id' => 0 );
689 } else {
690 $users_args = array();
691 }
692
693 foreach ( get_users( $users_args ) as $user ) {
694 if ( strpos( $user->user_login, $get_array['term'] ) !== false ) {
695 array_push( $users, $user->user_login );
696 }
697 }
698 echo wp_json_encode( $users );
699 exit;
700 }
701
702 /**
703 * Get all network sites
704 */
705 public function get_all_network_sites() {
706 // Die if user does not have permission to view.
707 if ( ! current_user_can( 'administrator' ) ) {
708 die( 'Access Denied.' );
709 }
710 // Filter $_GET array for security.
711 $get_array = filter_input_array( INPUT_GET );
712 // Die if nonce verification failed.
713 if ( ! wp_verify_nonce( sanitize_text_field( $get_array['wp_2fa_nonce'] ), 'wp-2fa-settings-nonce' ) ) {
714 die( esc_html__( 'Nonce verification failed.', 'wp-2fa' ) );
715 }
716 // Fetch sites.
717 $sites_found = array();
718
719 foreach ( get_sites() as $site ) {
720 $subsite_id = get_object_vars( $site )['blog_id'];
721 $subsite_name = get_blog_details( $subsite_id )->blogname;
722 $site_details = '';
723 $site_details[ $subsite_id ] = $subsite_name;
724 if ( stripos( $subsite_name, $get_array['term'] ) !== false ) {
725 $site_details = $subsite_name . ':' . $subsite_id;
726 array_push( $sites_found, $site_details );
727 }
728 }
729 echo wp_json_encode( $sites_found );
730 exit;
731 }
732
733 /**
734 * Unlock users accounts if they have overrun grace period
735 *
736 * @param int $user_id User ID.
737 */
738 public function unlock_account( $user_id ) {
739 // Die if user does not have permission to view.
740 if ( ! current_user_can( 'administrator' ) ) {
741 die( 'Access Denied.' );
742 }
743
744 $grace_period = WP2FA::get_wp2fa_setting( 'grace-period' );
745 $grace_period_denominator = WP2FA::get_wp2fa_setting( 'grace-period-denominator' );
746 $create_a_string = $grace_period . ' ' . $grace_period_denominator;
747 // Turn that string into a time.
748 $grace_expiry = strtotime( $create_a_string );
749
750 // Filter $_GET array for security.
751 $get_array = filter_input_array( INPUT_GET );
752 $nonce = sanitize_text_field( $get_array['wp_2fa_nonce'] );
753
754 // Die if nonce verification failed.
755 if ( ! wp_verify_nonce( $nonce, 'wp-2fa-unlock-account-nonce' ) ) {
756 die( esc_html__( 'Nonce verification failed.', 'wp-2fa' ) );
757 }
758
759 if ( isset( $get_array['user_id'] ) ) {
760 $unlock = delete_user_meta( intval( $get_array['user_id'] ), 'wp_2fa_user_grace_period_expired' );
761 $notification = delete_user_meta( intval( $get_array['user_id'] ), 'wp_2fa_locked_account_notification' );
762 $update = update_user_meta( intval( $get_array['user_id'] ), 'wp_2fa_grace_period_expiry', $grace_expiry );
763 $this->send_account_unlocked_email( intval( $get_array['user_id'] ) );
764 add_action( 'admin_notices', array( $this, 'user_unlocked_notice' ) );
765 }
766 }
767
768 /**
769 * Remove user 2fa config
770 *
771 * @param int $user_id User ID.
772 */
773 public function remove_user_2fa( $user_id ) {
774 // Filter $_GET array for security.
775 $get_array = filter_input_array( INPUT_GET );
776 $nonce = sanitize_text_field( $get_array['wp_2fa_nonce'] );
777
778 if ( ! wp_verify_nonce( $nonce, 'wp-2fa-remove-user-2fa-nonce' ) ) {
779 die( esc_html__( 'Nonce verification failed.', 'wp-2fa' ) );
780 }
781
782 if ( isset( $get_array['user_id'] ) ) {
783 $user_id = intval( $get_array['user_id'] );
784 $wipe_totp_key = delete_user_meta( $user_id, 'wp_2fa_totp_key' );
785 $wipe_backup_codes = delete_user_meta( $user_id, 'wp_2fa_backup_codes' );
786 $wipe_enabled_methods = delete_user_meta( $user_id, 'wp_2fa_enabled_methods' );
787 $wipe_grace_period = delete_user_meta( $user_id, 'wp_2fa_grace_period_expiry' );
788 $wipe_email_address = delete_user_meta( $user_id, 'wp_2fa_nominated_email_address' );
789 $is_needed = Authentication::is_user_eligible_for_2fa( $user_id );
790 if ( $is_needed ) {
791 if ( 'do-not-enforce' !== WP2FA::get_wp2fa_setting( 'enforcment-policy' ) ) {
792 // Turn inputs into a useable string.
793 $create_a_string = WP2FA::get_wp2fa_setting( 'grace-period' ) . ' ' . WP2FA::get_wp2fa_setting( 'grace-period-denominator' );
794 // Turn that string into a time.
795 $grace_expiry = strtotime( $create_a_string );
796 update_user_meta( $user_id, 'wp_2fa_grace_period_expiry', $grace_expiry );
797 update_user_meta( $user_id, 'wp_2fa_update_nag_dismissed', true );
798 }
799 }
800 if ( isset( $get_array['admin_reset'] ) ) {
801 add_action( 'admin_notices', array( $this, 'admin_deleted_2fa_notice' ) );
802 } else {
803 add_action( 'admin_notices', array( $this, 'user_deleted_2fa_notice' ) );
804 }
805 }
806 }
807
808 /**
809 * Send account unlocked notification via email.
810 *
811 * @param int $user_id user ID.
812 */
813 public static function send_account_unlocked_email( $user_id ) {
814 // Bail if the user has not enabled this email.
815 if ( 'enable_account_unlocked_email' !== WP2FA::get_wp2fa_email_templates( 'send_account_unlocked_email' ) ) {
816 return false;
817 }
818
819 // Grab user data.
820 $user = get_userdata( $user_id );
821 // Grab user email.
822 $email = $user->user_email;
823 // Setup the email contents.
824 $subject = wp_strip_all_tags( WP2FA::replace_email_strings( WP2FA::get_wp2fa_email_templates( 'user_account_unlocked_email_subject' ) ) );
825 $message = wpautop( WP2FA::replace_email_strings( WP2FA::get_wp2fa_email_templates( 'user_account_unlocked_email_body' ), $user_id ) );
826 // Specify our desired headers.
827 $headers = 'Content-type: text/html;charset=utf-8' . "\r\n";
828
829 if ( 'use-custom-email' === WP2FA::get_wp2fa_email_templates( 'email_from_setting' ) ) {
830 $headers .= 'From: ' . WP2FA::get_wp2fa_email_templates( 'custom_from_display_name' ) . ' <' . WP2FA::get_wp2fa_email_templates( 'custom_from_email_address' ) . '>' . "\r\n";
831 }
832
833 // Fire our email.
834 $mail = wp_mail( $user->user_email, $subject, $message, $headers );
835 }
836
837 /**
838 * Validate options before saving
839 *
840 * @param array $input The settings array.
841 */
842 public function validate_and_sanitize( $input ) {
843 // Bail if user doesnt have permissions to be here.
844 if ( ! current_user_can( 'manage_options' ) ) {
845 return;
846 }
847
848 // Setup args we may need, depending if this is a MS setup or not.
849 $users = array();
850 $users_args = array();
851 if ( WP2FA::is_this_multisite() ) {
852 $users_args['blog_id'] = 0;
853 }
854 $total_users = count_users();
855 $batch_size = 2000;
856 $slices = ceil( $total_users['total_users'] / $batch_size );
857
858 if ( ! isset( $input['enable_totp'] ) && ! isset( $input['enable_email'] ) ) {
859 add_settings_error(
860 'wp_2fa_settings',
861 esc_attr( 'enable_email_settings_error' ),
862 esc_html__( 'At least one 2FA method should be enabled.', 'wp-2fa' ),
863 'error'
864 );
865 }
866
867 // Compare current to old value to see if a method which was once enabled, has now been disabled.
868 if ( ! isset( $input['enable_totp'] ) && 'enable_totp' === WP2FA::get_wp2fa_setting( 'enable_totp' ) || ! isset( $input['enable_email'] ) && 'enable_email' === WP2FA::get_wp2fa_setting( 'enable_email' ) ) {
869 if ( ! isset( $input['enable_totp'] ) && 'enable_totp' === WP2FA::get_wp2fa_setting( 'enable_totp' ) ) {
870 $removing = 'totp';
871 } elseif ( ! isset( $input['enable_email'] ) && 'enable_email' === WP2FA::get_wp2fa_setting( 'enable_email' ) ) {
872 $removing = 'email';
873 }
874 // Flush the old expiry away from ALL users, we will re-apply them based on the current setup at the end of this.
875 for ( $count = 0; $count < $slices; $count++ ) {
876 $users_args = array(
877 'number' => $batch_size,
878 'offset' => $count * $batch_size,
879 'fields' => array( 'ID' ),
880 );
881 $users = get_users( $users_args );
882 if ( ! empty( $users ) ) {
883 $background_process = new ProcessUserMetaUpdate();
884 $item_to_process = array();
885 $item_to_process['users'] = $users;
886 $item_to_process['task'] = 'remove_enabled_methods';
887 $item_to_process['method_to_remove'] = $removing;
888 $background_process->push_to_queue( $item_to_process );
889 }
890
891 $background_process->save()->dispatch();
892 }
893 }
894
895 if ( isset( $input['enable_totp'] ) && 'enable_totp' === $input['enable_totp'] ) {
896 $output['enable_totp'] = sanitize_text_field( $input['enable_totp'] );
897 }
898
899 if ( isset( $input['enable_email'] ) && 'enable_email' === $input['enable_email'] ) {
900 $output['enable_email'] = sanitize_text_field( $input['enable_email'] );
901 }
902
903 if ( isset( $input['enforcment-policy'] ) && 'all-users' === $input['enforcment-policy'] || isset( $input['enforcment-policy'] ) && 'certain-users-only' === $input['enforcment-policy'] || isset( $input['enforcment-policy'] ) && 'certain-roles-only' === $input['enforcment-policy'] || isset( $input['enforcment-policy'] ) && 'do-not-enforce' === $input['enforcment-policy'] ) {
904 // Clear enforced roles/users if setting has changed.
905 if ( 'all-users' === $input['enforcment-policy'] ) {
906 $input['enforced_users'] = '';
907 $input['enforced_roles'] = '';
908 }
909
910 $output['enforcment-policy'] = sanitize_text_field( $input['enforcment-policy'] );
911 }
912
913 if ( WP2FA::get_wp2fa_setting( 'enforcment-policy' ) !== $input['enforcment-policy'] && 'do-not-enforce' === $input['enforcment-policy'] ) {
914 $input['enforced_users'] = '';
915 $input['enforced_roles'] = '';
916 }
917
918 if ( 'certain-roles-only' === $input['enforcment-policy'] && empty( $input['enforced_roles'] ) && empty( $input['enforced_users'] ) ) {
919 add_settings_error(
920 'wp_2fa_settings',
921 esc_attr( 'enforced_roles_settings_error' ),
922 esc_html__( 'You must specify at least one role or user', 'wp-2fa' ),
923 'error'
924 );
925 }
926
927 if ( isset( $input['enforced_roles'] ) ) {
928 $output['enforced_roles'] = trim( sanitize_text_field( $input['enforced_roles'] ) );
929 }
930
931 if ( isset( $input['enforced_users'] ) ) {
932 $output['enforced_users'] = trim( sanitize_text_field( $input['enforced_users'] ) );
933 }
934
935 if ( isset( $input['excluded_users'] ) ) {
936 $output['excluded_users'] = trim( sanitize_text_field( $input['excluded_users'] ) );
937
938 // Wipe user 2fa data.
939 $user_array = explode( ',', $output['excluded_users'] );
940 foreach ( $user_array as $user ) {
941 if ( ! empty( $user ) ) {
942 $user_to_wipe = get_user_by( 'login', $user );
943 $wipe_totp_key = delete_user_meta( $user_to_wipe->ID, 'wp_2fa_totp_key' );
944 $wipe_backup_codes = delete_user_meta( $user_to_wipe->ID, 'wp_2fa_backup_codes' );
945 $wipe_enabled_methods = delete_user_meta( $user_to_wipe->ID, 'wp_2fa_enabled_methods' );
946 $wipe_grace_period = delete_user_meta( $user_to_wipe->ID, 'wp_2fa_grace_period_expiry' );
947 }
948 }
949 }
950
951 if ( isset( $input['excluded_roles'] ) ) {
952 $output['excluded_roles'] = trim( sanitize_text_field( $input['excluded_roles'] ) );
953 $excluded_roles_array = array_filter( explode( ',', strtolower( $output['excluded_roles'] ) ) );
954
955 // Flush the old expiry away from ALL users, we will re-apply them based on the current setup at the end of this.
956 for ( $count = 0; $count < $slices; $count++ ) {
957 $users_args = array(
958 'number' => $batch_size,
959 'offset' => $count * $batch_size,
960 'fields' => array( 'ID' ),
961 );
962 $users = get_users( $users_args );
963 if ( ! empty( $users ) ) {
964 $background_process = new ProcessUserMetaUpdate();
965 $item_to_process = array();
966 $item_to_process['users'] = $users;
967 $item_to_process['task'] = 'wipe_all_2fa_user_data';
968 $item_to_process['excluded_roles'] = $excluded_roles_array;
969 $background_process->push_to_queue( $item_to_process );
970 }
971
972 $background_process->save()->dispatch();
973 }
974 }
975
976 if ( WP2FA::is_this_multisite() ) {
977 if ( isset( $input['excluded_sites'] ) ) {
978 $output['excluded_sites'] = trim( sanitize_text_field( $input['excluded_sites'] ) );
979 }
980 } else {
981 $output['excluded_sites'] = '';
982 }
983
984 if ( isset( $input['grace-policy'] ) ) {
985 $output['grace-policy'] = sanitize_text_field( $input['grace-policy'] );
986 }
987
988 if ( isset( $input['notify_users'] ) ) {
989 $output['notify_users'] = sanitize_text_field( $input['notify_users'] );
990 } else {
991 $input['notify_users'] = '';
992 }
993
994 if ( isset( $input['grace-period'] ) ) {
995 if ( 0 === (int) $input['grace-period'] ) {
996 add_settings_error(
997 'wp_2fa_settings',
998 esc_attr( 'grace_settings_error' ),
999 esc_html__( 'Grace period must be at least 1 day/hour', 'wp-2fa' ),
1000 'error'
1001 );
1002 $output['grace-period'] = 1;
1003 } else {
1004 $output['grace-period'] = (int) $input['grace-period'];
1005 }
1006 }
1007
1008 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'] ) {
1009 $output['grace-period-denominator'] = sanitize_text_field( $input['grace-period-denominator'] );
1010 }
1011
1012 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'] ) {
1013 $output['create-custom-user-page'] = sanitize_text_field( $input['create-custom-user-page'] );
1014 }
1015
1016 if ( isset( $input['custom-user-page-url'] ) ) {
1017 if ( $input['custom-user-page-url'] !== WP2FA::get_wp2fa_setting( 'custom-user-page-url' ) ) {
1018 if ( ! empty( WP2FA::get_wp2fa_setting( 'custom-user-page-id' ) ) ) {
1019 $updated_post = array(
1020 'ID' => WP2FA::get_wp2fa_setting( 'custom-user-page-id' ),
1021 'post_name' => sanitize_title_with_dashes( $input['custom-user-page-url'] ),
1022 );
1023 wp_update_post( $updated_post );
1024 $output['custom-user-page-url'] = sanitize_title_with_dashes( $input['custom-user-page-url'] );
1025 $output['custom-user-page-id'] = WP2FA::get_wp2fa_setting( 'custom-user-page-id' );
1026 } elseif ( 'yes' === $input['create-custom-user-page'] && ! empty( $input['custom-user-page-url'] ) ) {
1027 $output['custom-user-page-url'] = sanitize_title_with_dashes( $input['custom-user-page-url'] );
1028 $create_page = $this->generate_custom_user_profile_page( $output['custom-user-page-url'] );
1029 $output['custom-user-page-id'] = (int) $create_page;
1030 }
1031 } else {
1032 $output['custom-user-page-url'] = sanitize_title_with_dashes( $input['custom-user-page-url'] );
1033 $output['custom-user-page-id'] = WP2FA::get_wp2fa_setting( 'custom-user-page-id' );
1034 }
1035 }
1036
1037 if ( isset( $_REQUEST['page'] ) && 'wp-2fa-setup' !== $_REQUEST['page'] || isset( $_REQUEST['wp_2fa_settings']['create-custom-user-page'] ) ) {
1038
1039 if ( isset( $input['create-custom-user-page'] ) && 'no' === $input['create-custom-user-page'] ) {
1040 $output['custom-user-page-url'] = '';
1041 $output['custom-user-page-id'] = '';
1042 wp_delete_post( WP2FA::get_wp2fa_setting( 'custom-user-page-id' ), true );
1043 }
1044 }
1045
1046 if ( isset( $input['create-custom-user-page'] ) && 'yes' === $input['create-custom-user-page'] && empty( $input['custom-user-page-url'] ) ) {
1047 add_settings_error(
1048 'wp_2fa_settings',
1049 esc_attr( 'no_page_slug_provided' ),
1050 esc_html__( 'You must provide a new page slug.', 'wp-2fa' ),
1051 'error'
1052 );
1053 }
1054
1055 if ( isset( $input['enable_grace_cron'] ) ) {
1056 $output['enable_grace_cron'] = (bool) $input['enable_grace_cron'];
1057 }
1058
1059 if ( isset( $input['enable_destroy_session'] ) ) {
1060 $output['enable_destroy_session'] = (bool) $input['enable_destroy_session'];
1061 }
1062
1063 if ( isset( $input['2fa_settings_last_updated_by'] ) ) {
1064 $output['2fa_settings_last_updated_by'] = esc_attr( trim( $input['2fa_settings_last_updated_by'] ) );
1065 }
1066
1067 if ( isset( $input['limit_access'] ) ) {
1068 $output['limit_access'] = (bool) $input['limit_access'];
1069 }
1070
1071 if ( isset( $input['delete_data_upon_uninstall'] ) ) {
1072 $output['delete_data_upon_uninstall'] = (bool) $input['delete_data_upon_uninstall'];
1073 }
1074
1075 if ( isset( $input['grace-period'] ) && isset( $input['grace-period-denominator'] ) ) {
1076 // Turn inputs into a useable string.
1077 $create_a_string = $output['grace-period'] . ' ' . $output['grace-period-denominator'];
1078 // Turn that string into a time.
1079 $grace_expiry = strtotime( $create_a_string );
1080 $output['grace-period-expiry-time'] = sanitize_text_field( $grace_expiry );
1081 }
1082
1083 if ( isset( $input['hide_remove_button'] ) ) {
1084 $output['hide_remove_button'] = (bool) $input['hide_remove_button'];
1085 }
1086
1087 // Fetch users and apply the grace period tp their user meta.
1088 if ( isset( $input['enforcment-policy'] ) && 'do-not-enforce' !== $input['enforcment-policy'] && ! isset( $input['grace-period-expiry-time'] ) ) {
1089 // Flush the old expiry away from ALL users, we will re-apply them based on the current setup at the end of this.
1090 for ( $count = 0; $count < $slices; $count++ ) {
1091 $users_args = array(
1092 'number' => $batch_size,
1093 'offset' => $count * $batch_size,
1094 'fields' => array( 'ID' ),
1095 );
1096 if ( WP2FA::is_this_multisite() ) {
1097 $users_args['blog_id'] = 0;
1098 }
1099 $users = get_users( $users_args );
1100 if ( ! empty( $users ) ) {
1101 $background_process = new ProcessUserMetaUpdate();
1102 $item_to_process = array();
1103 $item_to_process['users'] = $users;
1104 $item_to_process['task'] = 'delete_grace_period';
1105 $background_process->push_to_queue( $item_to_process );
1106 }
1107 $background_process->save()->dispatch();
1108 }
1109
1110 // If we are specifying to enforce 2fa for specific users, we have no need to check if they are eligble or excluded, so we dont.
1111 if ( isset( $input['enforcment-policy'] ) && 'certain-roles-only' === $input['enforcment-policy'] && isset( $input['enforced_users'] ) && WP2FA::get_wp2fa_setting( 'enforced_users' ) !== $input['enforced_users'] || isset( $input['enforcment-policy'] ) && 'certain-roles-only' === $input['enforcment-policy'] && isset( $input['enforced_roles'] ) && WP2FA::get_wp2fa_setting( 'enforced_roles' ) !== $input['enforced_roles'] ) {
1112 $enforced_users_array = array_filter( explode( ',', $input['enforced_users'] ) );
1113
1114 // Flush the old expiry away from ALL users, we will re-apply them based on the current setup at the end of this.
1115 for ( $count = 0; $count < $slices; $count++ ) {
1116 $users_args = array(
1117 'number' => $batch_size,
1118 'offset' => $count * $batch_size,
1119 'fields' => array( 'ID', 'user_login' ),
1120 );
1121 if ( WP2FA::is_this_multisite() ) {
1122 $users_args['blog_id'] = 0;
1123 }
1124 $users = get_users( $users_args );
1125 if ( ! empty( $users ) ) {
1126 foreach ( $users as $user ) {
1127 if ( in_array( $user->user_login, $enforced_users_array ) ) {
1128 $background_process = new ProcessUserMetaUpdate();
1129 $item_to_process = array();
1130 $item_to_process['user'] = $user;
1131 $item_to_process['task'] = 'enforce_2fa_for_user';
1132 $item_to_process['grace_expiry'] = $grace_expiry;
1133 $item_to_process['grace_policy'] = sanitize_text_field( $input['grace-policy'] );
1134 $item_to_process['notify_users'] = $input['notify_users'];
1135 $background_process->push_to_queue( $item_to_process );
1136 } else {
1137 if ( isset( $output['enforced_roles'] ) && empty( $output['enforced_roles'] ) ) {
1138 $enforced_roles = 'none';
1139 } else {
1140 $enforced_roles = $output['enforced_roles'];
1141 }
1142 if ( isset( $output['enforced_users'] ) && empty( $output['enforced_users'] ) ) {
1143 $enforced_users = 'none';
1144 } else {
1145 $enforced_users = $output['enforced_users'];
1146 }
1147 $is_needed = Authentication::is_user_eligible_for_2fa( $user->ID, $input['enforcment-policy'], $output['excluded_users'], $output['excluded_roles'], $enforced_users, $enforced_roles );
1148 $is_user_excluded = WP2FA::is_user_excluded( $user, $output['excluded_users'], $output['excluded_roles'], $output['excluded_sites'] );
1149 if ( $is_needed && ! $is_user_excluded ) {
1150 $item_to_process = array();
1151 $item_to_process['user'] = $user;
1152 $item_to_process['task'] = 'enforce_2fa_for_user';
1153 $item_to_process['grace_expiry'] = $grace_expiry;
1154 $item_to_process['grace_policy'] = sanitize_text_field( $input['grace-policy'] );
1155 $item_to_process['notify_users'] = $input['notify_users'];
1156 $background_process->push_to_queue( $item_to_process );
1157 }
1158 }
1159 }
1160 }
1161
1162 $background_process->save()->dispatch();
1163 }
1164
1165 } else {
1166
1167 // Flush the old expiry away from ALL users, we will re-apply them based on the current setup at the end of this.
1168 for ( $count = 0; $count < $slices; $count++ ) {
1169 $users_args = array(
1170 'number' => $batch_size,
1171 'offset' => $count * $batch_size,
1172 'fields' => array( 'ID' ),
1173 );
1174 if ( WP2FA::is_this_multisite() ) {
1175 $users_args['blog_id'] = 0;
1176 }
1177 $users = get_users( $users_args );
1178
1179 if ( ! empty( $users ) ) {
1180 $background_process = new ProcessUserMetaUpdate();
1181 $item_to_process = array();
1182 $item_to_process['users'] = $users;
1183 $item_to_process['task'] = 'enforce_2fa_for_user';
1184 $item_to_process['grace_expiry'] = $grace_expiry;
1185 $item_to_process['grace_policy'] = sanitize_text_field( $input['grace-policy'] );
1186 $item_to_process['notify_users'] = $input['notify_users'];
1187 $item_to_process['enforcment-policy'] = $input['enforcment-policy'];
1188 $item_to_process['excluded_users'] = $output['excluded_users'];
1189 $item_to_process['excluded_roles'] = $output['excluded_roles'];
1190 $item_to_process['enforced_users'] = $output['enforced_users'];
1191 $item_to_process['enforced_roles'] = $output['enforced_roles'];
1192 $item_to_process['excluded_sites'] = $output['excluded_sites'];
1193 $background_process->push_to_queue( $item_to_process );
1194 }
1195
1196 $background_process->save()->dispatch();
1197 }
1198 }
1199 }
1200
1201 if ( isset( $input['enforcment-policy'] ) && 'do-not-enforce' === $input['enforcment-policy'] && ! isset( $input['grace-period-expiry-time'] ) ) {
1202 for ( $count = 0; $count < $slices; $count++ ) {
1203 $users_args = array(
1204 'number' => $batch_size,
1205 'offset' => $count * $batch_size,
1206 'fields' => array( 'ID' ),
1207 );
1208 if ( WP2FA::is_this_multisite() ) {
1209 $users_args['blog_id'] = 0;
1210 }
1211 $users = get_users( $users_args );
1212 if ( ! empty( $users ) ) {
1213 $background_process = new ProcessUserMetaUpdate();
1214 $item_to_process = array();
1215 $item_to_process['users'] = $users;
1216 $item_to_process['task'] = 'delete_grace_period';
1217 $background_process->push_to_queue( $item_to_process );
1218 }
1219 $background_process->save()->dispatch();
1220 }
1221 }
1222
1223 // 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
1224 global $wp_settings_errors;
1225 if ( isset( $wp_settings_errors ) ) {
1226 $errors = array_map( 'unserialize', array_unique( array_map( 'serialize', $wp_settings_errors ) ) );
1227 $wp_settings_errors = $errors;
1228 }
1229
1230 return $output;
1231 }
1232
1233 /**
1234 * Hide settings menu item
1235 */
1236 public function hide_settings() {
1237 $user = wp_get_current_user();
1238
1239 // Check we have a user before doing anything else.
1240 if ( is_a( $user, '\WP_User' ) ) {
1241 $user_id = (int) $user->ID;
1242 if ( ! empty( WP2FA::get_wp2fa_setting( '2fa_settings_last_updated_by' ) ) ) {
1243 $main_user = (int) WP2FA::get_wp2fa_setting( '2fa_settings_last_updated_by' );
1244 } else {
1245 $main_user = '';
1246 }
1247 if ( ! empty( WP2FA::get_wp2fa_setting( 'limit_access' ) ) && $user->ID !== $main_user ) {
1248 // Remove admin menu item.
1249 remove_submenu_page( 'options-general.php', 'wp-2fa-settings' );
1250 }
1251 }
1252 }
1253
1254 /**
1255 * Add unlock user link to user actions.
1256 *
1257 * @param array $links Default row content.
1258 */
1259 public function add_plugin_action_links( $links ) {
1260
1261 if ( WP2FA::is_this_multisite() ) {
1262 $url = network_admin_url( '/settings.php?page=wp-2fa-settings' );
1263 } else {
1264 $url = admin_url( '/options-general.php?page=wp-2fa-settings' );
1265 }
1266
1267 $links = array_merge(
1268 array(
1269 '<a href="' . esc_url( $url ) . '">' . esc_html__( 'Configure 2FA Settings', 'wp-2fa' ) . '</a>',
1270 ),
1271 $links
1272 );
1273
1274 return $links;
1275
1276 }
1277
1278 /**
1279 * User unlocked notice.
1280 */
1281 public function user_unlocked_notice() {
1282 ?>
1283 <div class="notice notice-success is-dismissible">
1284 <p><?php esc_html_e( 'User account successfully unlocked. User can login again.', 'wp-2fa' ); ?></p>
1285 <button type="button" class="notice-dismiss">
1286 <span class="screen-reader-text"><?php esc_html_e( 'Dismiss this notice.', 'wp-2fa' ); ?></span>
1287 </button>
1288 </div>
1289 <?php
1290 }
1291
1292 /**
1293 * User deleted 2FA settings notification
1294 */
1295 public function user_deleted_2fa_notice() {
1296 ?>
1297 <div class="notice notice-success is-dismissible">
1298 <p><?php esc_html_e( 'Your 2FA settings have been removed.', 'wp-2fa' ); ?></p>
1299 <button type="button" class="notice-dismiss">
1300 <span class="screen-reader-text"><?php esc_html_e( 'Dismiss this notice.', 'wp-2fa' ); ?></span>
1301 </button>
1302 </div>
1303 <?php
1304 }
1305
1306 /**
1307 * Admin deleted user 2FA settings notification
1308 */
1309 public function admin_deleted_2fa_notice() {
1310 ?>
1311 <div class="notice notice-success is-dismissible">
1312 <p><?php esc_html_e( 'User 2FA settings have been removed.', 'wp-2fa' ); ?></p>
1313 <button type="button" class="notice-dismiss">
1314 <span class="screen-reader-text"><?php esc_html_e( 'Dismiss this notice.', 'wp-2fa' ); ?></span>
1315 </button>
1316 </div>
1317 <?php
1318 }
1319
1320 /**
1321 * Semd email to let user know they need to enabled 2FA
1322 *
1323 * @param int $user_id User ID.
1324 */
1325 public static function send_2fa_enforced_email( $user_id, $override_grace_period = '' ) {
1326 // Bail if the user has not enabled this email.
1327 if ( 'enable_enforced_email' !== WP2FA::get_wp2fa_email_templates( 'send_enforced_email' ) ) {
1328 return false;
1329 }
1330
1331 $user_id = (int) $user_id;
1332 // Grab user data.
1333 $user = get_userdata( $user_id );
1334
1335 // Check if user has any enabled 2FA methods before sending.
1336 $enabled_methods = get_user_meta( $user->ID, 'wp_2fa_enabled_methods', true );
1337 if ( ! empty( $enabled_methods ) ) {
1338 return false;
1339 }
1340
1341 // Grab user email.
1342 $email = $user->user_email;
1343
1344 $subject = wp_strip_all_tags( WP2FA::replace_email_strings( WP2FA::get_wp2fa_email_templates( 'enforced_email_subject' ), $user_id, '', $override_grace_period ) );
1345 $message = wpautop( WP2FA::replace_email_strings( WP2FA::get_wp2fa_email_templates( 'enforced_email_body' ), $user_id, '', $override_grace_period ) );
1346
1347 $headers = 'Content-type: text/html;charset=utf-8' . "\r\n";
1348
1349 if ( 'use-custom-email' === WP2FA::get_wp2fa_email_templates( 'email_from_setting' ) ) {
1350 $headers .= 'From: ' . WP2FA::get_wp2fa_email_templates( 'custom_from_display_name' ) . ' <' . WP2FA::get_wp2fa_email_templates( 'custom_from_email_address' ) . '>' . "\r\n";
1351 }
1352
1353 $mail = wp_mail( $user->user_email, $subject, $message, $headers );
1354 }
1355
1356 public function update_wp2fa_network_options() {
1357 check_admin_referer( 'wp_2fa_settings-options' );
1358
1359 if ( isset( $_POST['wp_2fa_settings'] ) ) {
1360 $options = $this->validate_and_sanitize( wp_unslash( $_POST['wp_2fa_settings'] ) );
1361 $update_options = update_network_option( null, 'wp_2fa_settings', $options );
1362 }
1363
1364 // redirect back to our options page.
1365 wp_safe_redirect(
1366 add_query_arg(
1367 array(
1368 'page' => 'wp-2fa-settings',
1369 'wp_2fa_network_settings_updated' => 'true',
1370 ),
1371 network_admin_url( 'settings.php' )
1372 )
1373 );
1374 exit;
1375 }
1376
1377 /**
1378 * Handle saving email options to the network main site options.
1379 */
1380 public function update_wp2fa_network_email_options() {
1381 if ( isset( $_POST['email_from_setting'] ) ) {
1382 $options = $this->validate_and_sanitize_email( wp_unslash( $_POST ) );
1383
1384 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'] ) ) {
1385 // redirect back to our options page.
1386 wp_safe_redirect(
1387 add_query_arg(
1388 array(
1389 'page' => 'wp-2fa-settings',
1390 'wp_2fa_network_settings_updated' => 'false',
1391 'tab' => 'email-settings',
1392 ),
1393 network_admin_url( 'settings.php' )
1394 )
1395 );
1396 exit;
1397 }
1398
1399 $update_options = update_network_option( null, 'wp_2fa_email_settings', $options );
1400 }
1401
1402 // redirect back to our options page.
1403 wp_safe_redirect(
1404 add_query_arg(
1405 array(
1406 'page' => 'wp-2fa-settings',
1407 'wp_2fa_network_settings_updated' => 'true',
1408 'tab' => 'email-settings',
1409 ),
1410 network_admin_url( 'settings.php' )
1411 )
1412 );
1413 exit;
1414 }
1415
1416 /**
1417 * These are used instead of add_settings_error which in a network site. Used to show if settings have been updated or failed.
1418 */
1419 public function settings_saved_network_admin_notice() {
1420 if ( isset( $_GET['wp_2fa_network_settings_updated'] ) && $_GET['wp_2fa_network_settings_updated'] == 'true' ) :
1421 ?>
1422 <div class="notice notice-success is-dismissible">
1423 <p><?php esc_html_e( '2FA Settings Updated', 'wp-2fa' ); ?></p>
1424 <button type="button" class="notice-dismiss">
1425 <span class="screen-reader-text"><?php esc_html_e( 'Dismiss this notice.', 'wp-2fa' ); ?></span>
1426 </button>
1427 </div>
1428 <?php
1429 endif;
1430 if ( isset( $_GET['wp_2fa_network_settings_updated'] ) && $_GET['wp_2fa_network_settings_updated'] == 'false' ) :
1431 ?>
1432 <div class="notice notice-error is-dismissible">
1433 <p><?php esc_html_e( 'Please ensure both custom email address and display name are provided.', 'wp-2fa' ); ?></p>
1434 <button type="button" class="notice-dismiss">
1435 <span class="screen-reader-text"><?php esc_html_e( 'Dismiss this notice.', 'wp-2fa' ); ?></span>
1436 </button>
1437 </div>
1438 <?php
1439 endif;
1440 }
1441
1442 /**
1443 * Email settings
1444 */
1445 private function email_from_settings() {
1446 ?>
1447 <h3><?php esc_html_e( 'Which email address should the plugin use as a from address?', 'wp-2fa' ); ?></h3>
1448 <p class="description">
1449 <?php esc_html_e( 'Use these settings to customize the "from" name and email address for all correspondence sent from our plugin.', 'wp-2fa' ); ?>
1450 </p>
1451 <table class="form-table">
1452 <tbody>
1453 <tr>
1454 <th><label for="2fa-method"><?php esc_html_e( 'From email & name', 'wp-2fa' ); ?></label>
1455 </th>
1456 <td>
1457 <fieldset class="contains-hidden-inputs">
1458 <label for="use-defaults">
1459 <input type="radio" name="email_from_setting" id="use-defaults" value="use-defaults"
1460 <?php checked( WP2FA::get_wp2fa_email_templates( 'email_from_setting' ), 'use-defaults' ); ?>
1461 >
1462 <span><?php esc_html_e( 'Use the email address from the WordPress general settings.', 'wp-2fa' ); ?></span>
1463 </label>
1464
1465 <br/>
1466 <label for="use-custom-email">
1467 <input type="radio" name="email_from_setting" id="use-custom-email" value="use-custom-email"
1468 <?php checked( WP2FA::get_wp2fa_email_templates( 'email_from_setting' ), 'use-custom-email' ); ?>
1469 data-unhide-when-checked=".custom-from-inputs">
1470 <span><?php esc_html_e( 'Use another email address', 'wp-2fa' ); ?></span>
1471 </label>
1472 <fieldset class="hidden custom-from-inputs">
1473 <br/>
1474 <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>
1475 <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' ); ?>">
1476 </fieldset>
1477
1478 </fieldset>
1479 </td>
1480 </tr>
1481 </tbody>
1482 </table>
1483
1484 <br>
1485 <hr>
1486
1487 <?php
1488 }
1489
1490 /**
1491 * Email settings
1492 */
1493 private function email_settings() {
1494 ?>
1495 <h1><?php esc_html_e( 'Email Templates', 'wp-2fa' ); ?></h1>
1496 <h3><?php esc_html_e( 'Enforced 2FA email', 'wp-2fa' ); ?></h3>
1497 <p class="description">
1498 <?php esc_html_e( 'This is the email sent to applicable users when you enforce 2fa.', 'wp-2fa' ); ?>
1499 </p>
1500 <table class="form-table">
1501 <tbody>
1502 <tr>
1503 <th><label for="2fa-method"><?php esc_html_e( 'Send this email', 'wp-2fa' ); ?></label>
1504 </th>
1505 <td>
1506 <fieldset>
1507 <input type="checkbox" id="send_enforced_email" name="send_enforced_email" value="enable_enforced_email"
1508 <?php checked( 'enable_enforced_email', WP2FA::get_wp2fa_email_templates( 'send_enforced_email' ), true ); ?>
1509 >
1510 <label for="send_enforced_email"><?php esc_html_e( 'Uncheck to disable this message.', 'wp-2fa' ); ?></label>
1511 </fieldset>
1512 </td>
1513 </tr>
1514 <tr>
1515 <th><label for="2fa-method"><?php esc_html_e( 'Email subject', 'wp-2fa' ); ?></label>
1516 </th>
1517 <td>
1518 <fieldset>
1519 <input type="text" id="enforced_email_subject" name="enforced_email_subject" class="large-text" value="<?php esc_html_e( WP2FA::get_wp2fa_email_templates( 'enforced_email_subject' ) ); ?>">
1520 </fieldset>
1521 </td>
1522 </tr>
1523 <tr>
1524 <th><label for="2fa-method"><?php esc_html_e( 'Email body', 'wp-2fa' ); ?></label>
1525 </br>
1526 <label for="2fa-method" style="font-weight: 400;"><?php esc_html_e( 'Available template tags:', 'wp-2fa' ); ?></label>
1527 </br></br>
1528 <span style="font-weight: 400;">
1529 {site_url}</br>
1530 {site_name}</br>
1531 {grace_period}</br>
1532 {user_login_name}</br>
1533 {login_code}
1534 <?php
1535 if ( ! empty( WP2FA::get_wp2fa_setting( 'custom-user-page-id' ) ) ) {
1536 echo '</br>{2fa_settings_page_url}';
1537 }
1538 ?>
1539 </span>
1540 </th>
1541 <td>
1542 <fieldset>
1543 <?php
1544 $message = WP2FA::get_wp2fa_email_templates( 'enforced_email_body' );
1545 $content = $message;
1546 $editor_id = 'enforced_email_body';
1547 $settings = array(
1548 'media_buttons' => false,
1549 'editor_height' => 200,
1550 );
1551 wp_editor( $content, $editor_id, $settings );
1552 ?>
1553 </fieldset>
1554 </td>
1555 </tr>
1556 </tbody>
1557 </table>
1558
1559 <br>
1560 <hr>
1561
1562 <h3><?php esc_html_e( 'Login code email', 'wp-2fa' ); ?></h3>
1563 <p class="description">
1564 <?php esc_html_e( 'This is the email sent to a user when a login code is required.', 'wp-2fa' ); ?>
1565 </p>
1566 <table class="form-table">
1567 <tbody>
1568 <tr>
1569 <th><label for="2fa-method"><?php esc_html_e( 'Email subject', 'wp-2fa' ); ?></label>
1570 </th>
1571 <td>
1572 <fieldset>
1573 <input type="text" id="login_code_email_subject" name="login_code_email_subject" class="large-text" value="<?php esc_html_e( WP2FA::get_wp2fa_email_templates( 'login_code_email_subject' ) ); ?>">
1574 </fieldset>
1575 </td>
1576 </tr>
1577 <tr>
1578 <th><label for="2fa-method"><?php esc_html_e( 'Email body', 'wp-2fa' ); ?></label>
1579 </br>
1580 <label for="2fa-method" style="font-weight: 400;"><?php esc_html_e( 'Available template tags:', 'wp-2fa' ); ?></label>
1581 </br></br>
1582 <span style="font-weight: 400;">
1583 {site_url}</br>
1584 {site_name}</br>
1585 {grace_period}</br>
1586 {user_login_name}</br>
1587 {login_code}
1588 <?php
1589 if ( ! empty( WP2FA::get_wp2fa_setting( 'custom-user-page-id' ) ) ) {
1590 echo '</br>{2fa_settings_page_url}';
1591 }
1592 ?>
1593 </span>
1594 </th>
1595 <td>
1596 <fieldset>
1597 <?php
1598 $message = WP2FA::get_wp2fa_email_templates( 'login_code_email_body' );
1599 $content = $message;
1600 $editor_id = 'login_code_email_body';
1601 $settings = array(
1602 'media_buttons' => false,
1603 'editor_height' => 200,
1604 );
1605 wp_editor( $content, $editor_id, $settings );
1606 ?>
1607 </fieldset>
1608 </td>
1609 </tr>
1610 </tbody>
1611 </table>
1612
1613 <br>
1614 <hr>
1615
1616 <h3><?php esc_html_e( 'User account locked email', 'wp-2fa' ); ?></h3>
1617 <p class="description">
1618 <?php esc_html_e( 'This is the email sent to a user upon grace period expiry.', 'wp-2fa' ); ?>
1619 </p>
1620 <table class="form-table">
1621 <tbody>
1622 <tr>
1623 <th><label for="2fa-method"><?php esc_html_e( 'Send this email', 'wp-2fa' ); ?></label>
1624 </th>
1625 <td>
1626 <fieldset>
1627 <input type="checkbox" id="send_account_locked_email" name="send_account_locked_email" value="enable_account_locked_email"
1628 <?php checked( 'enable_account_locked_email', WP2FA::get_wp2fa_email_templates( 'send_account_locked_email' ), true ); ?>
1629 >
1630 <label for="send_account_locked_email"><?php esc_html_e( 'Uncheck to disable this message.', 'wp-2fa' ); ?></label>
1631 </fieldset>
1632 </td>
1633 </tr>
1634 <tr>
1635 <th><label for="2fa-method"><?php esc_html_e( 'Email subject', 'wp-2fa' ); ?></label>
1636 </th>
1637 <td>
1638 <fieldset>
1639 <input type="text" id="user_account_locked_email_subject" name="user_account_locked_email_subject" class="large-text" value="<?php esc_html_e( WP2FA::get_wp2fa_email_templates( 'user_account_locked_email_subject' ) ); ?>">
1640 </fieldset>
1641 </td>
1642 </tr>
1643 <tr>
1644 <th><label for="2fa-method"><?php esc_html_e( 'Email body', 'wp-2fa' ); ?></label>
1645 </br>
1646 <label for="2fa-method" style="font-weight: 400;"><?php esc_html_e( 'Available template tags:', 'wp-2fa' ); ?></label>
1647 </br></br>
1648 <span style="font-weight: 400;">
1649 {site_url}</br>
1650 {site_name}</br>
1651 {grace_period}</br>
1652 {user_login_name}</br>
1653 {login_code}
1654 <?php
1655 if ( ! empty( WP2FA::get_wp2fa_setting( 'custom-user-page-id' ) ) ) {
1656 echo '</br>{2fa_settings_page_url}';
1657 }
1658 ?>
1659 </span>
1660 </th>
1661 <td>
1662 <fieldset>
1663 <?php
1664 $message = WP2FA::get_wp2fa_email_templates( 'user_account_locked_email_body' );
1665 $content = $message;
1666 $editor_id = 'user_account_locked_email_body';
1667 $settings = array(
1668 'media_buttons' => false,
1669 'editor_height' => 200,
1670 );
1671 wp_editor( $content, $editor_id, $settings );
1672 ?>
1673 </fieldset>
1674 </td>
1675 </tr>
1676 </tbody>
1677 </table>
1678
1679 <br>
1680 <hr>
1681
1682 <h3><?php esc_html_e( 'User account unlocked email', 'wp-2fa' ); ?></h3>
1683 <p class="description">
1684 <?php esc_html_e( 'This is the email sent to a user when the user\'s account has been unlocked.', 'wp-2fa' ); ?>
1685 </p>
1686 <table class="form-table">
1687 <tbody>
1688 <tr>
1689 <th><label for="2fa-method"><?php esc_html_e( 'Send this email', 'wp-2fa' ); ?></label>
1690 </th>
1691 <td>
1692 <fieldset>
1693 <input type="checkbox" id="send_account_unlocked_email" name="send_account_unlocked_email" value="enable_account_unlocked_email"
1694 <?php checked( 'enable_account_unlocked_email', WP2FA::get_wp2fa_email_templates( 'send_account_unlocked_email' ), true ); ?>
1695 >
1696 <label for="send_account_unlocked_email"><?php esc_html_e( 'Uncheck to disable this message.', 'wp-2fa' ); ?></label>
1697 </fieldset>
1698 </td>
1699 </tr>
1700 <tr>
1701 <th><label for="2fa-method"><?php esc_html_e( 'Email subject', 'wp-2fa' ); ?></label>
1702 </th>
1703 <td>
1704 <fieldset>
1705 <input type="text" id="user_account_unlocked_email_subject" name="user_account_unlocked_email_subject" class="large-text" value="<?php esc_html_e( WP2FA::get_wp2fa_email_templates( 'user_account_unlocked_email_subject' ) ); ?>">
1706 </fieldset>
1707 </td>
1708 </tr>
1709 <tr>
1710 <th><label for="2fa-method"><?php esc_html_e( 'Email body', 'wp-2fa' ); ?></label>
1711 </br>
1712 <label for="2fa-method" style="font-weight: 400;"><?php esc_html_e( 'Available template tags:', 'wp-2fa' ); ?></label>
1713 </br></br>
1714 <span style="font-weight: 400;">
1715 {site_url}</br>
1716 {site_name}</br>
1717 {grace_period}</br>
1718 {user_login_name}</br>
1719 {login_code}
1720 <?php
1721 if ( ! empty( WP2FA::get_wp2fa_setting( 'custom-user-page-id' ) ) ) {
1722 echo '</br>{2fa_settings_page_url}';
1723 }
1724 ?>
1725 </span>
1726 </th>
1727 <td>
1728 <fieldset>
1729 <?php
1730 $message = WP2FA::get_wp2fa_email_templates( 'user_account_unlocked_email_body' );
1731 $content = $message;
1732 $editor_id = 'user_account_unlocked_email_body';
1733 $settings = array(
1734 'media_buttons' => false,
1735 'editor_height' => 200,
1736 );
1737 wp_editor( $content, $editor_id, $settings );
1738 ?>
1739 </fieldset>
1740 </td>
1741 </tr>
1742 </tbody>
1743 </table>
1744 <?php
1745 }
1746
1747 /**
1748 * Validate email templates before saving
1749 *
1750 * @param array $input The settings array.
1751 */
1752 public function validate_and_sanitize_email( $input ) {
1753
1754 // Bail if user doesnt have permissions to be here.
1755 if ( ! current_user_can( 'manage_options' ) ) {
1756 return;
1757 }
1758
1759 if ( empty( $_POST ) || ! wp_verify_nonce( $_POST['_wpnonce'], 'wp_2fa_email_settings-options' ) && ! wp_verify_nonce( $_POST['_wpnonce'], 'wp_2fa_settings-options' ) || ! wp_verify_nonce( $_POST['_wpnonce'], 'wp_2fa_email_settings-options' ) && ! wp_verify_nonce( $_POST['_wpnonce'], 'wp_2fa_settings-options' ) ) {
1760 die( esc_html__( 'Nonce verification failed.', 'wp-2fa' ) );
1761 }
1762
1763 if ( isset( $_POST['email_from_setting'] ) && 'use-defaults' === $_POST['email_from_setting'] || isset( $_POST['email_from_setting'] ) && 'use-custom-email' === $_POST['email_from_setting'] ) {
1764 $output['email_from_setting'] = sanitize_text_field( wp_unslash( $_POST['email_from_setting'] ) );
1765 }
1766
1767 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'] ) ) {
1768 add_settings_error(
1769 'wp_2fa_settings',
1770 esc_attr( 'email_from_settings_error' ),
1771 esc_html__( 'Please provide an email address', 'wp-2fa' ),
1772 'error'
1773 );
1774 $output['custom_from_email_address'] = '';
1775 }
1776
1777 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'] ) ) {
1778 add_settings_error(
1779 'wp_2fa_settings',
1780 esc_attr( 'display_name_settings_error' ),
1781 esc_html__( 'Please provide a display name.', 'wp-2fa' ),
1782 'error'
1783 );
1784 $output['custom_from_email_address'] = '';
1785 }
1786
1787 if ( isset( $_POST['custom_from_email_address'] ) && ! empty( $_POST['custom_from_email_address'] ) ) {
1788 if ( ! filter_var( $_POST['custom_from_email_address'], FILTER_VALIDATE_EMAIL ) ) {
1789 add_settings_error(
1790 'wp_2fa_settings',
1791 esc_attr( 'email_invalid_settings_error' ),
1792 esc_html__( 'Please provide a valid email address. Your email address has not been updated.', 'wp-2fa' ),
1793 'error'
1794 );
1795 }
1796 $output['custom_from_email_address'] = sanitize_email( wp_unslash( $_POST['custom_from_email_address'] ) );
1797 }
1798
1799 if ( isset( $_POST['custom_from_display_name'] ) && ! empty( $_POST['custom_from_display_name'] ) ) {
1800 // Check if the string contains HTML/tags.
1801 preg_match( "/<\/?\w+((\s+\w+(\s*=\s*(?:\".*?\"|'.*?'|[^'\">\s]+))?)+\s*|\s*)\/?>/", $_POST['custom_from_display_name'], $matches );
1802 if ( count( $matches ) > 0 ) {
1803 add_settings_error(
1804 'wp_2fa_settings',
1805 esc_attr( 'display_name_invalid_settings_error' ),
1806 esc_html__( 'Please only use alphanumeric text. Your display name has not been updated.', 'wp-2fa' ),
1807 'error'
1808 );
1809 } else {
1810 $output['custom_from_display_name'] = sanitize_text_field( wp_unslash( $_POST['custom_from_display_name'] ) );
1811 }
1812 }
1813
1814 if ( isset( $_POST['enforced_email_subject'] ) ) {
1815 $output['enforced_email_subject'] = wp_kses_post( wp_unslash( $_POST['enforced_email_subject'] ) );
1816 }
1817
1818 if ( isset( $_POST['enforced_email_body'] ) ) {
1819 $output['enforced_email_body'] = wpautop( wp_kses_post( wp_unslash( $_POST['enforced_email_body'] ) ) );
1820 }
1821
1822 if ( isset( $_POST['login_code_email_subject'] ) ) {
1823 $output['login_code_email_subject'] = wp_kses_post( wp_unslash( $_POST['login_code_email_subject'] ) );
1824 }
1825
1826 if ( isset( $_POST['login_code_email_body'] ) ) {
1827 $output['login_code_email_body'] = wpautop( wp_kses_post( wp_unslash( $_POST['login_code_email_body'] ) ) );
1828 }
1829
1830 if ( isset( $_POST['user_account_locked_email_subject'] ) ) {
1831 $output['user_account_locked_email_subject'] = wp_kses_post( wp_unslash( $_POST['user_account_locked_email_subject'] ) );
1832 }
1833
1834 if ( isset( $_POST['user_account_locked_email_body'] ) ) {
1835 $output['user_account_locked_email_body'] = wpautop( wp_kses_post( wp_unslash( $_POST['user_account_locked_email_body'] ) ) );
1836 }
1837
1838 if ( isset( $_POST['user_account_unlocked_email_subject'] ) ) {
1839 $output['user_account_unlocked_email_subject'] = wp_kses_post( wp_unslash( $_POST['user_account_unlocked_email_subject'] ) );
1840 }
1841
1842 if ( isset( $_POST['user_account_unlocked_email_body'] ) ) {
1843 $output['user_account_unlocked_email_body'] = wpautop( wp_kses_post( wp_unslash( $_POST['user_account_unlocked_email_body'] ) ) );
1844 }
1845
1846 if ( isset( $_POST['send_enforced_email'] ) && 'enable_enforced_email' === $_POST['send_enforced_email'] ) {
1847 $output['send_enforced_email'] = sanitize_text_field( $_POST['send_enforced_email'] );
1848 }
1849
1850 if ( isset( $_POST['send_account_locked_email'] ) && 'enable_account_locked_email' === $_POST['send_account_locked_email'] ) {
1851 $output['send_account_locked_email'] = sanitize_text_field( $_POST['send_account_locked_email'] );
1852 }
1853
1854 if ( isset( $_POST['send_account_unlocked_email'] ) && 'enable_account_unlocked_email' === $_POST['send_account_unlocked_email'] ) {
1855 $output['send_account_unlocked_email'] = sanitize_text_field( $_POST['send_account_unlocked_email'] );
1856 }
1857
1858 // 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.
1859 global $wp_settings_errors;
1860 if ( isset( $wp_settings_errors ) ) {
1861 $errors = array_map( 'unserialize', array_unique( array_map( 'serialize', $wp_settings_errors ) ) );
1862 $wp_settings_errors = $errors;
1863 }
1864
1865 if ( isset( $output ) ) {
1866 return $output;
1867 } else {
1868 return;
1869 }
1870
1871 }
1872
1873 /**
1874 * Creates a new page with our shortcode present.
1875 */
1876 public function generate_custom_user_profile_page( $page_slug ) {
1877 // Bail if user doesnt have permissions to be here.
1878 if ( ! current_user_can( 'manage_options' ) ) {
1879 return;
1880 }
1881
1882 // Check if a page with slug exists.
1883 $page_exists = $this->get_post_by_post_name( $page_slug, 'page' );
1884 if ( $page_exists ) {
1885 // Seeing as the page exisits, return its ID.
1886 return $page_exists->ID;
1887 }
1888
1889 $generated_by_message = sprintf(
1890 /* translators: %1$s: is the user name, %2$s is the website address */
1891 '<p>%1$s <a href="https://www.wpwhitesecurity.com/wordpress-plugins/wp-2fa/" target="_blank">%2$s</a></p>',
1892 esc_html__( 'Page generated by', 'wp-2fa' ),
1893 esc_html__( 'WP 2FA Plugin', 'wp-2fa' )
1894 );
1895
1896 $user = wp_get_current_user();
1897 $post_data = array(
1898 'post_title' => 'WP 2FA User Profile',
1899 'post_name' => $page_slug,
1900 'post_content' => '[wp-2fa-setup-form] ' . $generated_by_message,
1901 'post_status' => 'publish',
1902 'post_author' => $user->ID,
1903 'post_type' => 'page',
1904 );
1905
1906 // Lets insert the post now.
1907 $result = wp_insert_post( $post_data );
1908
1909 if ( $result && ! is_wp_error( $result ) ) {
1910 $post_id = $result;
1911 set_transient( 'wp_2fa_new_custom_page_created', true, 60 );
1912 set_site_transient( 'wp_2fa_new_custom_page_created', true, 60 );
1913 return $post_id;
1914 }
1915 }
1916
1917 /**
1918 * Check if page with slug exisits.
1919 */
1920 public function get_post_by_post_name( $slug = '', $post_type = '' ) {
1921 if ( ! $slug || ! $post_type ) {
1922 return false;
1923 }
1924
1925 $post_object = get_page_by_path( $slug, OBJECT, $post_type );
1926
1927 if ( ! $post_object ) {
1928 return false;
1929 }
1930
1931 return $post_object;
1932 }
1933
1934 /**
1935 * Add our custom state to our created page.
1936 */
1937 public function add_display_post_states( $post_states, $post ) {
1938 if ( ! empty( WP2FA::get_wp2fa_setting( 'custom-user-page-id' ) ) ) {
1939 if ( WP2FA::get_wp2fa_setting( 'custom-user-page-id' ) === $post->ID ) {
1940 $post_states['wp_2fa_page_for_user'] = __( 'WP 2FA User Page', 'wp-2fa' );
1941 }
1942 }
1943
1944 return $post_states;
1945 }
1946 }
1947