PluginProbe ʕ •ᴥ•ʔ
WP 2FA – Two-factor authentication for WordPress / 2.6.0
WP 2FA – Two-factor authentication for WordPress v2.6.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 / class-settings-page.php
wp-2fa / includes / classes / Admin Last commit date
Controllers 2 years ago Helpers 2 years ago Methods 2 years ago SettingsPages 2 years ago Views 2 years ago class-help-contact-us.php 2 years ago class-premium-features.php 2 years ago class-settings-page.php 2 years ago class-setup-wizard.php 2 years ago class-user-listing.php 2 years ago class-user-notices.php 2 years ago class-user-profile.php 2 years ago class-user-registered.php 2 years ago index.php 5 years ago
class-settings-page.php
521 lines
1 <?php
2 /**
3 * Settings rendering class.
4 *
5 * @package wp2fa
6 * @subpackage settings
7 * @copyright %%YEAR%% Melapress
8 * @license https://www.apache.org/licenses/LICENSE-2.0 Apache License 2.0
9 * @link https://wordpress.org/plugins/wp-2fa/
10 */
11
12 namespace WP2FA\Admin;
13
14 use WP2FA\WP2FA;
15 use WP2FA\Admin\SettingsPages\{
16 Settings_Page_Policies,
17 Settings_Page_General,
18 Settings_Page_Email
19 };
20 use WP2FA\Admin\Helpers\WP_Helper;
21 use WP2FA\Admin\Controllers\Settings;
22 use WP2FA\Utils\Settings_Utils;
23
24 /**
25 * Class for handling settings
26 */
27 if ( ! class_exists( '\WP2FA\Admin\Settings_Page' ) ) {
28 /**
29 * Class for handling settings
30 */
31 class Settings_Page {
32
33 const TOP_MENU_SLUG = 'wp-2fa-policies';
34
35 /**
36 * Create admin menu entry and settings page
37 */
38 public static function create_settings_admin_menu() {
39 // Create admin menu item.
40 add_menu_page(
41 esc_html__( 'WP 2FA', 'wp-2fa' ),
42 esc_html__( 'WP 2FA', 'wp-2fa' ),
43 'manage_options',
44 self::TOP_MENU_SLUG,
45 null,
46 'data:image/svg+xml;base64,' . base64_encode( file_get_contents( WP_2FA_PATH . 'dist/images/wp-2fa-white-icon20x28.svg' ) ), // phpcs:ignore
47 81
48 );
49
50 add_submenu_page(
51 self::TOP_MENU_SLUG,
52 esc_html__( '2FA Policies', 'wp-2fa' ),
53 esc_html__( '2FA Policies', 'wp-2fa' ),
54 'manage_options',
55 self::TOP_MENU_SLUG,
56 array( \WP2FA\Admin\SettingsPages\Settings_Page_Policies::class, 'render' ),
57 1
58 );
59
60 add_submenu_page(
61 self::TOP_MENU_SLUG,
62 esc_html__( 'WP 2FA Settings', 'wp-2fa' ),
63 esc_html__( 'Settings', 'wp-2fa' ),
64 'manage_options',
65 'wp-2fa-settings',
66 array( \WP2FA\Admin\SettingsPages\Settings_Page_Render::class, 'render' ),
67 2
68 );
69
70 // Register our policy settings.
71 register_setting(
72 WP_2FA_POLICY_SETTINGS_NAME,
73 WP_2FA_POLICY_SETTINGS_NAME,
74 array( \WP2FA\Admin\SettingsPages\Settings_Page_Policies::class, 'validate_and_sanitize' )
75 );
76
77 // Register our white label settings.
78 register_setting(
79 WP_2FA_WHITE_LABEL_SETTINGS_NAME,
80 WP_2FA_WHITE_LABEL_SETTINGS_NAME,
81 array( \WP2FA\Admin\SettingsPages\Settings_Page_White_Label::class, 'validate_and_sanitize' )
82 );
83
84 // Register our settings page.
85 register_setting(
86 WP_2FA_SETTINGS_NAME,
87 WP_2FA_SETTINGS_NAME,
88 array( \WP2FA\Admin\SettingsPages\Settings_Page_General::class, 'validate_and_sanitize' )
89 );
90
91 register_setting(
92 WP_2FA_EMAIL_SETTINGS_NAME,
93 WP_2FA_EMAIL_SETTINGS_NAME,
94 array( \WP2FA\Admin\SettingsPages\Settings_Page_Email::class, 'validate_and_sanitize' )
95 );
96
97 /**
98 * Fires after the main menu settings are registered.
99 *
100 * @param string - The menu slug.
101 * @param bool - Is that multisite install or not.
102 *
103 * @since 2.0.0
104 */
105 do_action( WP_2FA_PREFIX . 'after_admin_menu_created', self::TOP_MENU_SLUG, false );
106
107 add_action( WP_2FA_PREFIX . 'before_plugin_settings', array( __CLASS__, 'check_email' ) );
108 }
109
110 /**
111 * Create admin menu entry and settings page
112 */
113 public static function create_settings_admin_menu_multisite() {
114 // Create admin menu item.
115 add_menu_page(
116 esc_html__( 'WP 2FA Settings', 'wp-2fa' ),
117 esc_html__( 'WP 2FA', 'wp-2fa' ),
118 'manage_options',
119 self::TOP_MENU_SLUG,
120 null,
121 'data:image/svg+xml;base64,' . base64_encode( file_get_contents( WP_2FA_PATH . 'dist/images/wp-2fa-white-icon20x28.svg' ) ), // phpcs:ignore
122 81
123 );
124
125 add_submenu_page(
126 self::TOP_MENU_SLUG,
127 esc_html__( '2FA Policies', 'wp-2fa' ),
128 esc_html__( '2FA Policies', 'wp-2fa' ),
129 'manage_options',
130 self::TOP_MENU_SLUG,
131 array( \WP2FA\Admin\SettingsPages\Settings_Page_Policies::class, 'render' ),
132 1
133 );
134
135 add_submenu_page(
136 self::TOP_MENU_SLUG,
137 esc_html__( 'WP 2FA Settings', 'wp-2fa' ),
138 esc_html__( 'Settings', 'wp-2fa' ),
139 'manage_options',
140 'wp-2fa-settings',
141 array( \WP2FA\Admin\SettingsPages\Settings_Page_Render::class, 'render' ),
142 2
143 );
144
145 /**
146 * Fires after the main menu settings are registered.
147 *
148 * @param string - The menu slug.
149 * @param bool - Is that multisite install or not.
150 *
151 * @since 2.0.0
152 */
153 do_action( WP_2FA_PREFIX . 'after_admin_menu_created', self::TOP_MENU_SLUG, true );
154 }
155 /**
156 * Send account unlocked notification via email.
157 *
158 * @param int $user_id user ID.
159 *
160 * @return boolean
161 */
162 public static function send_account_unlocked_email( $user_id ) {
163 // Bail if the user has not enabled this email.
164 if ( 'enable_account_unlocked_email' !== WP2FA::get_wp2fa_email_templates( 'send_account_unlocked_email' ) ) {
165 return false;
166 }
167
168 // Grab user data.
169 $user = get_userdata( $user_id );
170 // Grab user email.
171 $email = $user->user_email;
172 // Setup the email contents.
173 $subject = wp_strip_all_tags( WP2FA::replace_email_strings( WP2FA::get_wp2fa_email_templates( 'user_account_unlocked_email_subject' ) ) );
174 $message = wpautop( WP2FA::replace_email_strings( WP2FA::get_wp2fa_email_templates( 'user_account_unlocked_email_body' ), $user_id ) );
175
176 return self::send_email( $email, $subject, $message );
177 }
178
179 /**
180 * Hide settings menu item
181 */
182 public static function hide_settings() {
183 $user = wp_get_current_user();
184
185 // Check we have a user before doing anything else.
186 if ( is_a( $user, '\WP_User' ) ) {
187 if ( ! empty( WP2FA::get_wp2fa_setting( '2fa_settings_last_updated_by' ) ) ) {
188 $main_user = (int) WP2FA::get_wp2fa_setting( '2fa_settings_last_updated_by' );
189 } else {
190 $main_user = get_current_user_id();
191 }
192 if ( ! empty( WP2FA::get_wp2fa_general_setting( 'limit_access' ) ) && $user->ID !== $main_user ) {
193 // Remove admin menu item.
194 remove_submenu_page( 'options-general.php', self::TOP_MENU_SLUG );
195 }
196 }
197 }
198
199 /**
200 * Add unlock user link to user actions.
201 *
202 * @param array $links Default row content.
203 *
204 * @return array
205 * @throws \Freemius_Exception - freemius exception.
206 */
207 public static function add_plugin_action_links( $links ) {
208 // add link to the external free trial page in free version and also in premium version if license is not active.
209 if ( ! function_exists( 'wp2fa_freemius' ) || ! wp2fa_freemius()->has_active_valid_license() ) {
210 $trial_link = 'https://melapress.com/wordpress-2fa/pricing/?utm_source=plugins&utm_medium=link&utm_campaign=wp2fa';
211 $links = array_merge(
212 array(
213 '<a style="font-weight:bold" href="' . $trial_link . '" target="_blank">' . __( 'Upgrade to Premium', 'wp-2fa' ) . '</a>',
214 ),
215 $links
216 );
217 }
218
219 // add link to the plugin settings page.
220 $url = Settings::get_settings_page_link();
221 $links = array_merge(
222 array(
223 '<a href="' . esc_url( $url ) . '">' . esc_html__( 'Configure 2FA Settings', 'wp-2fa' ) . '</a>',
224 ),
225 $links
226 );
227
228 return $links;
229 }
230
231 /**
232 * Updates options for multisite
233 *
234 * @return void
235 *
236 * @since 2.0.0
237 */
238 public static function update_wp2fa_network_options() {
239
240 Settings_Page_Policies::update_wp2fa_network_options();
241
242 Settings_Page_General::update_wp2fa_network_options();
243
244 \WP2FA\Admin\SettingsPages\Settings_Page_White_Label::update_wp2fa_network_options();
245
246 /**
247 * Gives the ability for extensions to set their settings in the plugin.
248 *
249 * @since 2.2.0
250 */
251 do_action( WP_2FA_PREFIX . 'update_network_settings' );
252 }
253
254 /**
255 * Handle saving email options to the network main site options.
256 */
257 public static function update_wp2fa_network_email_options() {
258 Settings_Page_Email::update_wp2fa_network_options();
259 }
260
261 /**
262 * These are used instead of add_settings_error which in a network site. Used to show if settings have been updated or failed.
263 */
264 public static function settings_saved_network_admin_notice() {
265 if ( isset( $_GET['wp_2fa_network_settings_updated'] ) && 'true' === $_GET['wp_2fa_network_settings_updated'] ) { // phpcs:ignore
266 ?>
267 <div class="notice notice-success is-dismissible">
268 <p><?php esc_html_e( '2FA Settings Updated', 'wp-2fa' ); ?></p>
269 <button type="button" class="notice-dismiss">
270 <span class="screen-reader-text"><?php esc_html_e( 'Dismiss this notice.', 'wp-2fa' ); ?></span>
271 </button>
272 </div>
273 <?php
274 }
275 if ( isset( $_GET['wp_2fa_network_settings_updated'] ) && 'false' === $_GET['wp_2fa_network_settings_updated'] ) { // phpcs:ignore
276 ?>
277 <div class="notice notice-error is-dismissible">
278 <?php
279 if ( isset( $_GET['wp_2fa_network_settings_custom_error_message'] ) ) { // phpcs:ignore
280 ?>
281 <p><?php echo \wp_unslash( $_GET['wp_2fa_network_settings_custom_error_message'] ); // phpcs:ignore ?></p>
282 <button type="button" class="notice-dismiss">
283 <span class="screen-reader-text"><?php esc_html_e( 'Dismiss this notice.', 'wp-2fa' ); ?></span>
284 </button>
285 <?php
286 } else {
287 ?>
288 <p><?php esc_html_e( 'Please ensure both custom email address and display name are provided.', 'wp-2fa' ); ?></p>
289 <button type="button" class="notice-dismiss">
290 <span class="screen-reader-text"><?php esc_html_e( 'Dismiss this notice.', 'wp-2fa' ); ?></span>
291 </button>
292 <?php
293 }
294 ?>
295 </div>
296 <?php
297 }
298 if ( isset( $_GET['wp_2fa_network_settings_error'] ) ) { // phpcs:ignore
299 ?>
300 <div class="notice notice-error is-dismissible">
301 <?php
302 $error = \sanitize_text_field( \wp_unslash( $_GET['wp_2fa_network_settings_error'] ) );
303
304 if ( true === \strpos( $error, 'http' ) ) {
305 ?>
306 <p><?php echo \esc_attr( \esc_url_raw( \urldecode_deep( $error ) ) ); ?></p>
307 <?php
308 } else {
309 ?>
310 <p><?php echo \esc_attr( ( $error ) ); ?></p>
311 <?php } ?>
312 <button type="button" class="notice-dismiss">
313 <span class="screen-reader-text"><?php esc_html_e( 'Dismiss this notice.', 'wp-2fa' ); ?></span>
314 </button>
315 </div>
316 <?php
317 }
318 }
319
320 /**
321 * These are used instead of add_settings_error which in a network site. Used to show if settings have been updated or failed.
322 *
323 * @return void
324 *
325 * @since 2.0.0
326 */
327 public static function settings_saved_admin_notice() {
328 if ( isset( $_GET['page'] ) && 0 === strpos( \sanitize_text_field( \wp_unslash( $_GET['page'] ) ), 'wp-2fa-' ) ) {
329 if ( isset( $_GET['settings-updated'] ) && 'true' === $_GET['settings-updated'] ) {
330 $wp_settings_errors = get_settings_errors();
331
332 if ( count( $wp_settings_errors ) ) {
333 foreach ( $wp_settings_errors as $error ) {
334 ?>
335 <div class="notice notice-<?php echo \esc_attr( $error['type'] ); ?> is-dismissible">
336 <p><?php echo \esc_html( $error['message'] ); ?></p>
337 <button type="button" class="notice-dismiss">
338 <span class="screen-reader-text"><?php esc_html_e( 'Dismiss this notice.', 'wp-2fa' ); ?></span>
339 </button>
340 </div>
341 <?php
342 }
343 } else {
344 ?>
345 <div class="notice notice-success is-dismissible">
346 <p><?php esc_html_e( '2FA Settings Updated', 'wp-2fa' ); ?></p>
347 <button type="button" class="notice-dismiss">
348 <span class="screen-reader-text"><?php esc_html_e( 'Dismiss this notice.', 'wp-2fa' ); ?></span>
349 </button>
350 </div>
351 <?php
352 }
353 }
354 if ( isset( $_GET['settings-updated'] ) && 'false' === $_GET['settings-updated'] ) {
355 ?>
356 <div class="notice notice-error is-dismissible">
357 <p><?php esc_html_e( 'Please ensure both custom email address and display name are provided.', 'wp-2fa' ); ?></p>
358 <button type="button" class="notice-dismiss">
359 <span class="screen-reader-text"><?php esc_html_e( 'Dismiss this notice.', 'wp-2fa' ); ?></span>
360 </button>
361 </div>
362 <?php
363 }
364 if ( isset( $_GET['settings_error'] ) ) {
365 ?>
366 <div class="notice notice-error is-dismissible">
367 <p><?php echo \esc_attr( \esc_url_raw( \urldecode_deep( \sanitize_text_field( \wp_unslash( $_GET['settings_error'] ) ) ) ) ); ?></p>
368 <button type="button" class="notice-dismiss">
369 <span class="screen-reader-text"><?php esc_html_e( 'Dismiss this notice.', 'wp-2fa' ); ?></span>
370 </button>
371 </div>
372 <?php
373 }
374 }
375 }
376
377 /**
378 * Add our custom state to our created page.
379 *
380 * @param array $post_states - array with the post states.
381 * @param WP_Post $post - the WP post.
382 *
383 * @return array
384 */
385 public static function add_display_post_states( $post_states, $post ) {
386 if ( ! empty( WP2FA::get_wp2fa_setting( 'custom-user-page-id' ) ) ) {
387 if ( WP2FA::get_wp2fa_setting( 'custom-user-page-id' ) === $post->ID ) {
388 $post_states['wp_2fa_page_for_user'] = __( 'WP 2FA User Page', 'wp-2fa' );
389 }
390 }
391
392 return $post_states;
393 }
394
395 /**
396 * Handles sending of an email. It sets necessary header such as content type and custom from email address and name.
397 *
398 * @param string $recipient_email Email address to send message to.
399 * @param string $subject Email subject.
400 * @param string $message Message contents.
401 *
402 * @return bool Whether the email contents were sent successfully.
403 */
404 public static function send_email( $recipient_email, $subject, $message ) {
405
406 // Specify our desired headers.
407 $headers = 'Content-type: text/html;charset=utf-8' . "\r\n";
408
409 if ( 'use-custom-email' === WP2FA::get_wp2fa_email_templates( 'email_from_setting' ) ) {
410 $headers .= 'From: ' . WP2FA::get_wp2fa_email_templates( 'custom_from_display_name' ) . ' <' . WP2FA::get_wp2fa_email_templates( 'custom_from_email_address' ) . '>' . "\r\n";
411 } else {
412 $headers .= 'From: ' . get_bloginfo( 'name' ) . ' <' . get_bloginfo( 'admin_email' ) . '>' . "\r\n";
413 }
414
415 // Fire our email.
416 return wp_mail( $recipient_email, $subject, $message, $headers );
417 }
418
419 /**
420 * Turns user roles data in any form and shape to an array of strings.
421 *
422 * @param mixed $value User role names (slugs) as raw value.
423 *
424 * @return string[] List of user role names (slugs).
425 */
426 public static function extract_roles_from_input( $value ) {
427 if ( is_array( $value ) ) {
428 return $value;
429 }
430
431 if ( is_string( $value ) && ! empty( $value ) ) {
432 return explode( ',', $value );
433 }
434
435 return array();
436 }
437
438 /**
439 * Determine if any BG processes are currently running.
440 *
441 * @return int|false Number of jobs.
442 */
443 public static function get_current_number_of_active_bg_processes() {
444 global $wpdb;
445
446 $bg_jobs = $wpdb->get_results( // phpcs:ignore
447 "SELECT option_value FROM $wpdb->options
448 WHERE option_name LIKE '%_2fa_bg_%'"
449 );
450
451 return count( $bg_jobs );
452 }
453
454 /**
455 * Checks the email against the current domain and shows an error message if they do not match.
456 *
457 * @return void
458 *
459 * @since 2.6.0
460 */
461 public static function check_email() {
462 $is_dismissed = (bool) Settings_Utils::get_option( 'dismiss_notice_mail_domain', false );
463 if ( ! $is_dismissed ) {
464 $admin_email = \get_option( 'admin_email' );
465
466 if ( 'use-custom-email' === WP2FA::get_wp2fa_email_templates( 'email_from_setting' ) ) {
467 $admin_email = WP2FA::get_wp2fa_email_templates( 'custom_from_email_address' );
468 }
469 $site_url = \get_site_url();
470
471 $email_domain = WP_Helper::extract_domain( $admin_email );
472 $site_domain = WP_Helper::extract_domain( $site_url );
473
474 if ( $email_domain !== $site_domain ) {
475 $email_settings_url = esc_url(
476 add_query_arg(
477 array(
478 'page' => 'wp-2fa-settings',
479 'tab' => 'email-settings',
480 ),
481 network_admin_url( 'admin.php' )
482 )
483 );
484 ?>
485 <div class="notice notice-error">
486 <p><?php esc_html_e( 'By default, the plugin uses the Administrator\'s email address as configured in WordPress settings as the "from address" when sending emails with the 2FA code for users to log in. This email address is currently configured as: ', 'wp-2fa' ); ?><b><?php echo $admin_email; ?>.</b></p>
487 <p>
488 <a href="<?php echo \esc_url( $email_settings_url ); ?>"><?php esc_html_e( 'Configure email address now', 'wp-2fa' ); ?></a>
489 <a href="#" class="2fa-email-notice" style="margin-left:10px;">
490 <?php esc_html_e( 'Use the Administrator\'s email address.', 'wp-2fa' ); ?>
491 </a>
492 </p>
493
494 <?php wp_nonce_field( 'wp2fa_dismiss_notice_mail_domain', 'wp2fa_dismiss_notice_mail_domain', false ); ?>
495 </div>
496 <?php
497 } else {
498 Settings_Utils::update_option( 'dismiss_notice_mail_domain', true );
499 }
500 }
501 }
502
503 /**
504 * Sets the email domain do not match setting as dismissed.
505 *
506 * @return void
507 *
508 * @since 2.6.0
509 */
510 public static function dismiss_notice_mail_domain() {
511 // Verify nonce.
512 if ( isset( $_POST['nonce'] ) && \wp_verify_nonce( \sanitize_text_field( \wp_unslash( $_POST['nonce'] ) ), 'wp2fa_dismiss_notice_mail_domain' ) ) {
513 Settings_Utils::update_option( 'dismiss_notice_mail_domain', true );
514 die();
515 }
516
517 die( 'Nonce verification failed!' );
518 }
519 }
520 }
521