PluginProbe ʕ •ᴥ•ʔ
WP 2FA – Two-factor authentication for WordPress / 2.6.0
WP 2FA – Two-factor authentication for WordPress v2.6.0
4.0.0 1.7.1 2.0.0 2.0.1 2.1.0 2.2.0 2.2.1 2.3.0 2.4.0 2.4.1 2.4.2 2.5.0 2.6.0 2.6.1 2.6.2 2.6.3 2.6.4 2.7.0 2.8.0 2.9.0 2.9.1 2.9.2 2.9.3 3.0.0 3.0.1 3.1.0 3.1.1 3.1.1.2 trunk 1.2.0 1.3.0 1.4.0 1.4.1 1.4.2 1.5.0 1.5.1 1.5.2 1.6.0 1.6.1 1.6.2 1.7.0
wp-2fa / includes / classes / Admin / Methods / class-totp-wizard-steps.php
wp-2fa / includes / classes / Admin / Methods Last commit date
Traits 2 years ago class-backup-codes.php 2 years ago class-email-wizard-steps.php 2 years ago class-email.php 2 years ago class-totp-wizard-steps.php 2 years ago class-totp.php 2 years ago index.php 2 years ago
class-totp-wizard-steps.php
397 lines
1 <?php
2 /**
3 * Responsible for WP2FA user's TOTP manipulation.
4 *
5 * @package wp2fa
6 * @subpackage methods-wizard
7 * @since 2.6.0
8 * @copyright %%YEAR%% Melapress
9 * @license https://www.apache.org/licenses/LICENSE-2.0 Apache License 2.0
10 *
11 * @see https://wordpress.org/plugins/wp-2fa/
12 */
13
14 declare(strict_types=1);
15
16 namespace WP2FA\Methods\Wizards;
17
18 use WP2FA\WP2FA;
19 use WP2FA\Methods\TOTP;
20 use WP2FA\Admin\Helpers\WP_Helper;
21 use WP2FA\Admin\Views\Wizard_Steps;
22 use WP2FA\Admin\Helpers\User_Helper;
23 use WP2FA\Admin\Controllers\Settings;
24 use WP2FA\Admin\Methods\Traits\Methods_Wizards_Trait;
25 use WP2FA\Authenticator\Authentication;
26 use WP2FA\Extensions\RoleSettings\Role_Settings_Controller;
27
28 /**
29 * Class for handling totp codes.
30 *
31 * @since 2.6.0
32 *
33 * @package WP2FA
34 */
35 if ( ! class_exists( '\WP2FA\Methods\Wizards\TOTP_Wizard_Steps' ) ) {
36 /**
37 * TOTP code class, for handling totp (app) code generation and such.
38 *
39 * @since 2.6.0
40 */
41 class TOTP_Wizard_Steps extends Wizard_Steps {
42
43 use Methods_Wizards_Trait;
44
45 /**
46 * Keeps the main class method name, so we can call it when needed.
47 *
48 * @var string
49 *
50 * @since 2.6.0
51 */
52 private static $main_class = TOTP::class;
53
54 /**
55 * The default value of the method order in the wizards.
56 *
57 * @var integer
58 *
59 * @since 2.6.0
60 */
61 private static $order = 1;
62
63 /**
64 * Inits the class hooks
65 *
66 * @return void
67 *
68 * @since 2.4.0
69 */
70 public static function init() {
71 \add_filter( WP_2FA_PREFIX . 'methods_modal_options', array( __CLASS__, 'totp_option' ), 10, 2 );
72 \add_action( WP_2FA_PREFIX . 'modal_methods', array( __CLASS__, 'totp_modal_configure' ) );
73 \add_filter( WP_2FA_PREFIX . 'methods_re_configure', array( __CLASS__, 'totp_re_configure' ), 10, 2 );
74 \add_filter( WP_2FA_PREFIX . 'methods_settings', array( __CLASS__, 'totp_wizard_settings' ), 10, 4 );
75 }
76
77 /**
78 * Shows the option to reconfigure email (if applicable)
79 *
80 * @param array $methods - Array of methods collected.
81 * @param string $role - The name of the role to show option to.
82 *
83 * @since 2.6.0
84 *
85 * @return array
86 */
87 public static function totp_re_configure( array $methods, string $role ): array {
88
89 if ( ! TOTP::is_enabled() ) {
90 return $methods;
91 }
92 \ob_start();
93 ?>
94 <div class="option-pill">
95 <?php echo wp_kses_post( WP2FA::contextual_reconfigure_text( WP2FA::get_wp2fa_white_label_setting( 'totp_reconfigure_intro', true ), User_Helper::get_user_object()->ID, TOTP::METHOD_NAME ) ); ?>
96 <div class="wp2fa-setup-actions">
97 <a href="#" class="button button-primary wp-2fa-button-primary" data-name="next_step_setting_modal_wizard" data-trigger-reset-key <?php echo WP_Helper::create_data_nonce( self::json_nonce() ); // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped ?> data-user-id="<?php echo esc_attr( User_Helper::get_user_object()->ID ); ?>" data-next-step="2fa-wizard-totp"><?php esc_html_e( 'Reset Key', 'wp-2fa' ); ?></a>
98 </div>
99 </div>
100 <?php
101 $output = ob_get_contents();
102 ob_end_clean();
103
104 $methods[ self::get_order( $role, $methods ) ] = array(
105 'name' => self::$main_class::METHOD_NAME,
106 'output' => $output,
107 );
108
109 return $methods;
110 }
111
112 /**
113 * Shows the initial totp setup options based on enabled methods
114 *
115 * @param array $methods - Array of methods collected.
116 * @param string $role - The name of the role to show option to.
117 *
118 * @since 2.6.0
119 *
120 * @return array
121 */
122 public static function totp_option( array $methods, string $role ): array {
123 if ( TOTP::is_enabled() ) {
124 \ob_start();
125 ?>
126 <div class="option-pill">
127 <label for="basic">
128 <input id="basic" name="wp_2fa_enabled_methods" type="radio" value="totp">
129 <?php echo wp_kses_post( WP2FA::get_wp2fa_white_label_setting( 'totp-option-label', true ) ); ?><span class="wizard-tooltip" data-tooltip-content="data-totp-tooltip-content-wrapper">i</span>
130 </label>
131 <?php
132 echo '<p class="description tooltip-content-wrapper" data-totp-tooltip-content-wrapper>';
133 printf(
134 /* translators: link to the knowledge base website */
135 esc_html__( 'Refer to the %s for more information on how to setup these apps and which apps are supported.', 'wp-2fa' ),
136 '<a href="https://melapress.com/support/kb/wp-2fa-configuring-2fa-apps/?&utm_source=plugins&utm_medium=link&utm_campaign=wp2fa" target="_blank">' . esc_html__( 'guide on how to set up 2FA apps', 'wp-2fa' ) . '</a>'
137 );
138 echo '</p>';
139 ?>
140 </div>
141 <?php
142 $output = ob_get_contents();
143 ob_end_clean();
144
145 $methods[ self::get_order( $role, $methods ) ] = $output;
146 }
147
148 return $methods;
149 }
150
151 /**
152 * Shows the TOTP modal configuration.
153 *
154 * @return void
155 *
156 * @since 2.6.0
157 */
158 public static function totp_modal_configure() {
159 if ( TOTP::is_enabled() ) {
160 ?>
161 <div class="wizard-step" id="2fa-wizard-totp">
162 <fieldset>
163 <?php self::totp_configure(); ?>
164 </fieldset>
165 </div>
166 <?php
167 }
168 }
169
170 /**
171 * Reconfigures the totp form
172 *
173 * @since 2.6.0
174 *
175 * @return void
176 */
177 public static function totp_configure() {
178
179 if ( ! TOTP::is_enabled() ) {
180 return;
181 }
182
183 // Regenerate the code if the method is not in use.
184 if ( TOTP::METHOD_NAME !== User_Helper::get_enabled_method_for_user() ) {
185 TOTP::remove_user_totp_key();
186 }
187
188 /**
189 * Active on modal, additional attribute is required on standard HTML (check below)
190 */
191 $add_step_attributes = 'active';
192
193 /**
194 * Closing div for extra modal wrappers see lines above
195 */
196 $close_div = '';
197
198 $qr_code = '<img class="qr-code" src="' . ( TOTP::get_qr_code() ) . '" id="wp-2fa-totp-qrcode" />';
199 $open30_wrapper = '
200 <div class="mb-30 clear-both">
201 ';
202 $open60_wrapper = '
203 <div class="modal-60">
204 ';
205 $open40_wrapper = '
206 <div class="modal-40">
207 ';
208 $close_div = '
209 </div>
210 ';
211
212 ?>
213 <div class="step-setting-wrapper <?php echo \esc_attr( $add_step_attributes ); ?>">
214 <div class="mb-20">
215 <?php echo wp_kses_post( WP2FA::get_wp2fa_white_label_setting( 'method_help_totp_intro', true ) ); ?>
216 </div>
217 <?php echo $open30_wrapper . $open40_wrapper; // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped ?>
218
219 <div class="qr-code-wrapper">
220 <?php echo $qr_code; // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped ?>
221 </div>
222 <?php
223 echo $close_div; // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped
224 echo $open60_wrapper; // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped
225 ?>
226
227 <div class="radio-cells option-pill mb-0">
228 <ol class="wizard-custom-counter">
229 <li><?php echo wp_kses_post( WP2FA::get_wp2fa_white_label_setting( 'method_help_totp_step_1', true ) ); ?>
230 <?php
231 if ( ! empty( WP2FA::get_wp2fa_white_label_setting( 'show_help_text' ) ) ) {
232 ?>
233 <span class="wizard-tooltip" data-tooltip-content="data-totp-setup-tooltip-content-wrapper">i</span><?php } ?></li>
234 <li><?php echo wp_kses_post( WP2FA::get_wp2fa_white_label_setting( 'method_help_totp_step_2', true ) ); ?>
235 <div class="app-key-wrapper">
236 <input type="text" id="app-key-input" readonly value="<?php echo esc_html( TOTP::get_totp_decrypted() ); ?>" class="app-key">
237 <?php
238 if ( is_ssl() ) {
239 ?>
240 <span class="click-to-copy"><?php esc_html_e( 'COPY', 'wp-2fa' ); ?></span>
241 <?php } ?>
242 </div>
243 </li>
244 <li><?php echo wp_kses_post( WP2FA::get_wp2fa_white_label_setting( 'method_help_totp_step_3', true ) ); ?></li>
245 </ol>
246 </div>
247 <?php
248 echo $close_div; // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped
249 echo $close_div; // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped
250 ?>
251 <?php if ( ! empty( WP2FA::get_wp2fa_white_label_setting( 'show_help_text' ) ) ) : ?>
252 <div class="tooltip-content-wrapper" data-totp-setup-tooltip-content-wrapper>
253 <p class="description"><?php esc_html_e( 'Click on the icon of the app that you are using for a detailed guide on how to set it up.', 'wp-2fa' ); ?></p>
254 <div class="apps-wrapper">
255 <?php foreach ( Authentication::get_apps() as $app ) { ?>
256 <a href="https://melapress.com/support/kb/wp-2fa-configuring-2fa-apps/?&utm_source=plugins&utm_medium=link&utm_campaign=wp2fa#<?php echo $app['hash']; ?>" target="_blank" class="app-logo"><img src="<?php echo esc_url( WP_2FA_URL . 'dist/images/' . $app['logo'] ); // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped ?>"></a>
257 <?php } ?>
258 </div>
259 </div>
260 <?php endif; ?>
261 <div class="wp2fa-setup-actions">
262 <button class="button wp-2fa-button-primary" name="next_step_setting" value="<?php esc_attr_e( 'I\'m Ready', 'wp-2fa' ); ?>" type="button"><?php esc_html_e( 'I\'m Ready', 'wp-2fa' ); ?></button>
263 <a class="button button-primary wp-2fa-button-secondary modal_cancel"><?php esc_attr_e( 'Cancel', 'wp-2fa' ); ?></a>
264 </div>
265 </div>
266 <div class="step-setting-wrapper" data-step-title="<?php esc_html_e( 'Verify configuration', 'wp-2fa' ); ?>">
267 <div class="mb-20">
268 <?php echo wp_kses_post( WP2FA::get_wp2fa_white_label_setting( 'method_verification_totp_pre', true ) ); ?>
269 </div>
270 <fieldset>
271 <label for="2fa-totp-authcode">
272 <?php esc_html_e( 'Authentication Code', 'wp-2fa' ); ?>
273 <input type="tel" name="wp-2fa-totp-authcode" id="wp-2fa-totp-authcode" class="input" value="" size="20" pattern="[0-9]*" autocomplete="off"/>
274 <script>
275 const totp_authcode = document.getElementById('wp-2fa-totp-authcode');
276 totp_authcode.addEventListener('input', function() {
277 this.value = this.value.trim();
278 });
279 </script>
280 </label>
281 <div class="verification-response"></div>
282 </fieldset>
283 <input type="hidden" name="wp-2fa-totp-key" value="<?php echo esc_attr( TOTP::get_totp_decrypted() ); ?>" />
284
285 <a href="#" class="modal__btn button button-primary wp-2fa-button-primary" data-validate-authcode-ajax <?php echo WP_Helper::create_data_nonce( 'wp-2fa-validate-authcode' ); // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped ?>><?php esc_html_e( 'Validate & Save', 'wp-2fa' ); ?></a>
286 <button class="modal__btn wp-2fa-button-secondary button button-secondary wp-2fa-button-secondary" data-close-2fa-modal aria-label="Close this dialog window"><?php esc_html_e( 'Cancel', 'wp-2fa' ); ?></button>
287 </div>
288
289 <?php
290 }
291
292 /**
293 * Settings page and first time wizard settings render
294 *
295 * @param array $methods - Array with all the methods in which we have to add this one.
296 * @param boolean $setup_wizard - Is that the first time setup wizard.
297 * @param string $data_role - Additional HTML data attribute.
298 * @param mixed $role - Name of the role.
299 *
300 * @return array - The array with the methods with all the methods wizard steps.
301 *
302 * @since 2.6.0
303 */
304 public static function totp_wizard_settings( array $methods, bool $setup_wizard, string $data_role, $role = null ) {
305 $name_prefix = WP_2FA_POLICY_SETTINGS_NAME;
306 $role_id = '';
307 if ( null !== $role && '' !== trim( (string) $role ) ) {
308 $name_prefix .= "[{$role}]";
309 $data_role = 'data-role="' . $role . '"';
310 $role_id = '-' . $role;
311 }
312 \ob_start();
313 ?>
314 <div id="<?php echo \esc_attr( TOTP::METHOD_NAME ); ?>-method-wrapper" class="method-wrapper">
315 <?php echo self::hidden_order_setting( $role ); // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped ?>
316 <label for="totp<?php echo \esc_attr( $role_id ); ?>" style="margin-bottom: 0 !important;">
317 <input type="checkbox" id="totp<?php echo \esc_attr( $role_id ); ?>" name="<?php echo \esc_attr( $name_prefix ); ?>[enable_totp]" value="enable_totp"
318 <?php echo $data_role; // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped ?>
319
320 <?php if ( null !== $role && ! empty( $role ) ) { ?>
321 <?php checked( TOTP::POLICY_SETTINGS_NAME, Role_Settings_Controller::get_setting( $role, TOTP::POLICY_SETTINGS_NAME ), true ); ?>
322 <?php
323 } else {
324 $use_role_setting = null;
325 if ( null === $role || '' === trim( (string) $role ) ) {
326 $use_role_setting = \WP_2FA_PREFIX . 'no-user';
327 }
328
329 $enabled_settings = Settings::get_role_or_default_setting( TOTP::POLICY_SETTINGS_NAME, $use_role_setting, $role, true );
330 ?>
331 <?php checked( $enabled_settings, TOTP::POLICY_SETTINGS_NAME ); ?>
332 <?php } ?>
333 >
334 <?php esc_html_e( 'One-time code via 2FA App (TOTP) - ', 'wp-2fa' ); ?><a href="https://melapress.com/support/kb/wp-2fa-configuring-2fa-apps/?&utm_source=plugins&utm_medium=link&utm_campaign=wp2fa" target="_blank" rel=noopener><?php esc_html_e( 'complete list of supported 2FA apps.', 'wp-2fa' ); ?></a>
335 </label>
336 <?php
337 if ( $setup_wizard ) {
338 echo '<p class="description">';
339 printf(
340 /* translators: link to the knowledge base website */
341 esc_html__( 'When using this method, users will need to configure a 2FA app to get the one-time login code. The plugin supports all standard 2FA apps. Refer to the %s for more information. Allowing users to set up a secondary 2FA method is highly recommended. You can do this in the next step of the wizard. This will allow users to log in using an alternative method should they, for example lose access to their phone.', 'wp-2fa' ),
342 '<a href="https://melapress.com/support/kb/wp-2fa-configuring-2fa-apps/?&utm_source=plugins&utm_medium=link&utm_campaign=wp2fa" target="_blank">' . esc_html__( 'guide on how to set up 2FA apps', 'wp-2fa' ) . '</a>'
343 );
344 echo '</p>';
345 }
346 if ( ! $setup_wizard ) {
347 echo '<p class="description">';
348 printf(
349 /* translators: link to the knowledge base website */
350 esc_html__( 'Refer to the %s for more information on how to setup these apps and which apps are supported.', 'wp-2fa' ),
351 '<a href="https://melapress.com/support/kb/wp-2fa-configuring-2fa-apps/?&utm_source=plugins&utm_medium=link&utm_campaign=wp2fa" target="_blank">' . esc_html__( 'guide on how to set up 2FA apps', 'wp-2fa' ) . '</a>'
352 );
353 echo '</p>';
354 }
355 ?>
356 </div>
357 <?php
358 $output = ob_get_contents();
359 ob_end_clean();
360
361 $methods[ self::get_order( $role, $methods ) ] = $output;
362
363 return $methods;
364 }
365
366 /**
367 * Prints the form that prompts the user to authenticate.
368 *
369 * @param \WP_User $user - \WP_User object of the logged-in user.
370 *
371 * @since 2.6.0
372 */
373 public static function totp_authentication_page( $user ) {
374 require_once ABSPATH . '/wp-admin/includes/template.php';
375 ?>
376 <?php
377 if ( 'use-custom' == WP2FA::get_wp2fa_white_label_setting( 'use_custom_2fa_message' ) ) {
378 echo WP2FA::get_wp2fa_white_label_setting( 'custom-text-app-code-page', true ); // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped
379 } else {
380 echo WP2FA::get_wp2fa_white_label_setting( 'default-text-code-page', true ); // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped
381 }
382 ?>
383 <p>
384 </br>
385 <label for="authcode"><?php esc_html_e( 'Authentication Code:', 'wp-2fa' ); ?></label>
386 <input type="tel" name="authcode" id="authcode" class="input" value="" size="20" pattern="[0-9]*" autocomplete="off" />
387 <script>
388 const authcode = document.getElementById('authcode');
389 authcode.addEventListener('input', function() {
390 this.value = this.value.trim();
391 });
392 </script>
393 </p>
394 <?php
395 }
396 }
397 }