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 | } |