Currently we have some license issues. We are working on it.

Commit a379251d authored by noplanman's avatar noplanman
Browse files

Merge branch 'harden_wpcs' into 'develop'

Adhere to WPCS, improve and harden code.

See merge request !16
parents 4d95b1b1 0f154683
Pipeline #4510 passed with stage
in 32 seconds
......@@ -13,6 +13,7 @@ use chillerlan\QRCode\QRCode;
use chillerlan\QRCode\QROptions;
use OTPHP\TOTP;
use ParagonIE\ConstantTime\Base32;
use Throwable;
use WP_User;
/**
......@@ -31,7 +32,7 @@ class Wp_Otp_Admin {
*/
public function enqueue_styles( $hook ): void {
if ( 'profile.php' === $hook ) {
wp_enqueue_style( WP_OTP_SLUG . '-admin', plugin_dir_url( __FILE__ ) . 'css/wp-otp-admin.css' );
wp_enqueue_style( WP_OTP_SLUG . '-admin', plugin_dir_url( __FILE__ ) . 'css/wp-otp-admin.css', [], WP_OTP_VERSION );
}
}
......@@ -46,12 +47,10 @@ class Wp_Otp_Admin {
if ( 'profile.php' === $hook ) {
$handle = WP_OTP_SLUG . '-admin';
wp_enqueue_script( $handle, plugin_dir_url( __FILE__ ) . 'js/wp-otp-admin.js', [ 'jquery' ], null, true );
wp_enqueue_script( $handle, plugin_dir_url( __FILE__ ) . 'js/wp-otp-admin.js', [ 'jquery' ], WP_OTP_VERSION, true );
wp_localize_script( $handle, 'wp_otp', [
'confirm_reconfigure' =>
__( 'Are you sure you want to reconfigure WP-OTP?', 'wp-otp' ),
'confirm_new_recovery_codes' =>
__( 'Are you sure you want to regenerate your recovery codes?', 'wp-otp' ),
'confirm_reconfigure' => __( 'Are you sure you want to reconfigure WP-OTP?', 'wp-otp' ),
'confirm_new_recovery_codes' => __( 'Are you sure you want to regenerate your recovery codes?', 'wp-otp' ),
] );
}
}
......@@ -61,16 +60,17 @@ class Wp_Otp_Admin {
*
* @since 0.1.0
*
* @param int $user_id
* @param int $user_id WordPress User ID.
*
* @return void
* @throws \Exception
*/
public function user_profile_updated( $user_id ): void {
if ( ! current_user_can( 'edit_user', $user_id ) ) {
return;
}
check_admin_referer( 'wp_otp_code', 'wp_otp_code' );
$user = get_userdata( $user_id );
$user_meta_data = Wp_Otp_User_Meta::get_instance();
......@@ -81,7 +81,7 @@ class Wp_Otp_Admin {
$otp = TOTP::create( $secret );
$otp->setLabel( $user->user_login );
$otp_code = $_POST['wp_otp_code'] ?? '';
$otp_code = sanitize_key( $_POST['wp_otp_code'] ?? '' );
if ( $otp_code && ! $user_meta_data->get( 'enabled', false ) ) {
/** Filter documented in class-wp-otp-public.php */
$otp_window = (int) apply_filters( 'wp_otp_code_expiration_window', 2 );
......@@ -124,13 +124,15 @@ class Wp_Otp_Admin {
* @since 0.1.0
*/
public function admin_init(): void {
if ( isset( $_GET['wp-otp-reconfigure'] ) && 'yes' === $_GET['wp-otp-reconfigure'] ) {
// phpcs:ignore WordPress.Security.NonceVerification.Recommended
if ( 'yes' === ( sanitize_key( $_GET['wp-otp-reconfigure'] ?? '' ) ) ) {
Wp_Otp_User_Meta::clear();
wp_redirect( get_edit_profile_url() . '#wp_otp' );
wp_safe_redirect( get_edit_profile_url() . '#wp_otp' );
exit;
}
if ( isset( $_GET['wp-otp-new-recovery-codes'] ) && 'yes' === $_GET['wp-otp-new-recovery-codes'] ) {
// phpcs:ignore WordPress.Security.NonceVerification.Recommended
if ( 'yes' === ( sanitize_key( $_GET['wp-otp-new-recovery-codes'] ?? '' ) ) ) {
$otp_recovery_codes = $this->get_random_recovery_codes();
Wp_Otp_User_Meta::get_instance()->set_all( [
'recovery_codes' => $otp_recovery_codes,
......@@ -145,7 +147,7 @@ class Wp_Otp_Admin {
],
], true );
wp_redirect( get_edit_profile_url() );
wp_safe_redirect( get_edit_profile_url() );
exit;
}
}
......@@ -161,7 +163,6 @@ class Wp_Otp_Admin {
* @param null|int $codes_length_override Override the filter and default for the codes length.
*
* @return array
* @throws \Exception
*/
public function get_random_recovery_codes( $codes_count_override = null, $codes_length_override = null ): array {
/**
......@@ -185,8 +186,9 @@ class Wp_Otp_Admin {
$codes_length = min( max( 8, $codes_length ), 64 );
$codes = [];
// phpcs:ignore Squiz.PHP.DisallowSizeFunctionsInLoops.Found
while ( count( $codes ) < $codes_count ) {
$code = substr( bin2hex( random_bytes( 32 ) ), 0, $codes_length );
$code = $this->get_random_hash( $codes_length );
if ( ! array_key_exists( $code, $codes ) ) {
$codes[ $code ] = true;
}
......@@ -203,7 +205,6 @@ class Wp_Otp_Admin {
* @param null|int $secret_length_override Override the filter and default for the codes count.
*
* @return string
* @throws \Exception
*/
public function get_random_secret( $secret_length_override = null ): string {
/**
......@@ -216,7 +217,30 @@ class Wp_Otp_Admin {
$secret_length = $secret_length_override ?: (int) apply_filters( 'wp_otp_secret_length', 16 );
$secret_length = min( max( 8, $secret_length ), 64 );
return substr( Base32::encode( random_bytes( 42 ) ), 0, $secret_length );
return $this->get_random_hash( $secret_length );
}
/**
* Get a random hash string of up to 100 characters.
*
* @since Unreleased
*
* @param int $length Length of the random hash (max 100).
*
* @return string
*/
public function get_random_hash( $length = 0 ): string {
try {
$random_hash = Base32::encode( random_bytes( 64 ) );
} catch ( Throwable $e ) {
$random_hash = Base32::encode( md5( microtime( true ) ) . md5( microtime( true ) ) );
}
if ( $length <= 0 ) {
return substr( $random_hash, 0, 100 );
}
return substr( $random_hash, 0, min( $length, 100 ) );
}
/**
......@@ -224,9 +248,7 @@ class Wp_Otp_Admin {
*
* @since 0.1.0
*
* @param WP_User $user
*
* @throws \Exception
* @param WP_User $user WordPress User Object.
*/
public function user_profile_render( $user ): void {
$user_meta_data = Wp_Otp_User_Meta::get_instance();
......@@ -252,7 +274,6 @@ class Wp_Otp_Admin {
* @param string $qr_code_provisioning_uri
*/
$qr_code_provisioning_uri = apply_filters( 'wp_otp_qr_code_provisioning_uri', $qr_code_provisioning_uri_default );
$otp_qr_code_img_uri = $otp->getQrCodeUri( $qr_code_provisioning_uri, '{PROVISIONING_URI}' );
// If no custom provisioning URI is set, opt for internal QR code processing, if possible.
if ( $qr_code_provisioning_uri === $qr_code_provisioning_uri_default ) {
......@@ -261,11 +282,15 @@ class Wp_Otp_Admin {
$qr_code = new QRCode( $qr_code_options );
$otp_qr_code_raw_uri = $otp->getProvisioningUri();
$otp_qr_code_img_uri = $qr_code->render( $otp_qr_code_raw_uri );
} catch ( \Throwable $e ) {
// Silently fail and fall back to online provisioning.
} catch ( Throwable $e ) {
$otp_qr_code_img_uri = null;
}
}
if ( ! isset( $otp_qr_code_img_uri ) ) {
$otp_qr_code_img_uri = $otp->getQrCodeUri( $qr_code_provisioning_uri, '{PROVISIONING_URI}' );
}
$otp_enabled = $user_meta_data->get( 'enabled' );
$otp_apps = [
......@@ -339,7 +364,7 @@ class Wp_Otp_Admin {
$class = $classes[ array_key_exists( $type, $classes ) ? $type : 'notice' ];
?>
<div id="message" class="<?php echo esc_attr( $class ); ?>">
<p><?php echo implode( '<br>', $messages ); ?></p>
<p><?php echo implode( '<br>', $messages ); // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped ?></p>
</div>
<?php
}
......@@ -354,24 +379,14 @@ class Wp_Otp_Admin {
public function admin_notices(): void {
$user_meta_data = Wp_Otp_User_Meta::get_instance();
/*if ( ! $user_meta_data->get( 'enabled' ) ) {
$this->show_user_notification( [
__( '<strong>Note:</strong> You have not configured WP-OTP yet.', 'wp-otp' ),
sprintf(
'<a href="%1$s#wp_otp" class="button">%2$s</a>',
get_edit_profile_url(),
_x( 'Configure now', 'Link text to go to WP-OTP section in user profile', 'wp-otp' )
),
] );
} else {*/
if ( $user_meta_data->get( 'enabled' ) ) {
$recovery_codes = array_filter( $user_meta_data->get( 'recovery_codes' ) );
$recovery_codes_count = count( $recovery_codes );
if ( $recovery_codes_count < 3 ) {
$this->show_user_notification( [
'<strong>' . __( 'Important', 'wp-otp' ) . '</strong>',
'<strong>' . esc_html__( 'Important', 'wp-otp' ) . '</strong>',
sprintf(
_n(
_n( // phpcs:ignore WordPress.WP.I18n.MissingTranslatorsComment
'You have %d WP-OTP recovery code left. You should generate new ones.',
'You have %d WP-OTP recovery codes left. You should generate new ones.',
$recovery_codes_count,
......@@ -381,8 +396,8 @@ class Wp_Otp_Admin {
),
sprintf(
'<a href="%1$s" class="button">%2$s</a>',
add_query_arg( 'wp-otp-new-recovery-codes', 'yes', get_edit_profile_url() ),
_x( 'Regenerate', 'Link to regenerate the WP-OTP recovery codes', 'wp-otp' )
esc_url( add_query_arg( 'wp-otp-new-recovery-codes', 'yes', get_edit_profile_url() ) ),
esc_html_x( 'Regenerate', 'Link to regenerate the WP-OTP recovery codes', 'wp-otp' )
),
], 'error' );
}
......
.wp-otp-app-box {
display: inline-block;
margin: 8px 16px;
display: inline-block;
margin: 8px 16px;
}
.wp-otp-link-reconfigure,
.wp-otp-link-new-recovery-codes {
margin: 4px !important;
display: inline-block;
margin: 4px !important;
display: inline-block;
}
.wp-otp-recovery-codes-box del,
.wp-otp-recovery-codes-box span {
font-weight: normal;
display: block;
margin: 4px;
font-weight: normal;
display: block;
margin: 4px;
}
.wp-otp-recovery-codes-box del {
color: #800;
color: #800;
}
<?php // Silence is golden
<?php // Silence is golden
......@@ -2,76 +2,79 @@
/**
* Render the WP-OTP section in the user profile in WP Admin
*
* @since 0.1.0
* @package Wp_Otp
* @subpackage Admin
* @since 0.1.0
*/
?>
<a name="wp_otp"></a>
<h2><?php _e( 'Set up WP-OTP (WordPress One Time Password)', 'wp-otp' ); ?></h2>
<h2><?php esc_html_e( 'Set up WP-OTP (WordPress One Time Password)', 'wp-otp' ); ?></h2>
<table class="form-table">
<tr>
<th scope="row">
<?php echo __( 'OTP Secret', 'wp-otp' ) . ':<br>' . implode( ' ', str_split( $secret, 4 ) ) . '<br><br>'; ?>
<?php esc_html_e( 'OTP Secret', 'wp-otp' ); ?>:<br>
<?php echo esc_html( implode( ' ', str_split( $secret, 4 ) ) ); ?><br><br>
<?php if ( $otp_enabled ) : ?>
<?php
printf(
'<em>%1$s</em><br><a href="%2$s" class="button button-small wp-otp-link-reconfigure">%3$s</a>',
__( 'WP-OTP has been configured successfully.', 'wp-otp' ),
add_query_arg( 'wp-otp-reconfigure', 'yes' ),
_x( 'Reconfigure', 'Link to reset and reconfigure WP-OTP secret', 'wp-otp' )
esc_html__( 'WP-OTP has been configured successfully.', 'wp-otp' ),
esc_url( add_query_arg( 'wp-otp-reconfigure', 'yes' ) ),
esc_html_x( 'Reconfigure', 'Link to reset and reconfigure WP-OTP secret', 'wp-otp' )
);
?>
<br><br>
<div class="wp-otp-recovery-codes-box">
<?php _e( 'Recovery codes', 'wp-otp' ); ?>:<br>
<?php esc_html_e( 'Recovery codes', 'wp-otp' ); ?>:<br>
<?php
foreach ( $user_meta_data->get( 'recovery_codes' ) as $code => $unused ) {
printf( '<%1$s>%2$s</%1$s>', $unused ? 'span' : 'del', $code );
printf( '<%1$s>%2$s</%1$s>', $unused ? 'span' : 'del', $code ); // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped
}
printf(
'<a href="%1$s" class="button button-small wp-otp-link-new-recovery-codes">%2$s</a>',
add_query_arg( 'wp-otp-new-recovery-codes', 'yes' ),
_x( 'Regenerate', 'Link to regenerate the WP-OTP recovery codes', 'wp-otp' )
esc_url( add_query_arg( 'wp-otp-new-recovery-codes', 'yes' ) ),
esc_html_x( 'Regenerate', 'Link to regenerate the WP-OTP recovery codes', 'wp-otp' )
);
?>
</div>
<?php else : ?>
<em><?php _e( 'To activate WP-OTP, enter the One Time Password from your authenticator app and save your profile.', 'wp-otp' ); ?></em><br><br>
<label for="wp_otp_code"><?php _e( 'One Time Password', 'wp-otp' ); ?></label><br>
<em><?php esc_html_e( 'To activate WP-OTP, enter the One Time Password from your authenticator app and save your profile.', 'wp-otp' ); ?></em><br><br>
<label for="wp_otp_code"><?php esc_html_e( 'One Time Password', 'wp-otp' ); ?></label><br>
<input type="text" size="25" name="wp_otp_code" id="wp_otp_code"/>
<?php wp_nonce_field( 'wp_otp_nonce', 'wp_otp_nonce' ); ?>
<?php endif; ?>
</th>
<td>
<img src="<?php echo $otp_qr_code_img_uri; ?>" alt="<?php _e( 'QR Code to scan with mobile app', 'wp-otp' ); ?>"/><br>
<img src="<?php echo esc_attr( $otp_qr_code_img_uri ); ?>" alt="<?php esc_attr_e( 'QR Code to scan with mobile app', 'wp-otp' ); ?>" style="background-color:#fff;"/><br>
</td>
<td>
<span class="description">
<?php _e( 'Download any OTP Authenticator app on your smart phone and scan the QR Code to activate WP-OTP.', 'wp-otp' ); ?>
<?php esc_html_e( 'Download any OTP Authenticator app on your smart phone and scan the QR Code to activate WP-OTP.', 'wp-otp' ); ?>
</span><br><br>
<?php foreach ( $otp_apps as $otp_app ) : ?>
<span class="wp-otp-app-box">
<?php $app_name = esc_attr( $otp_app['name'] ); ?>
<strong><?php echo $otp_app['name']; ?></strong><br>
<a href="<?php echo $otp_app['uri']; ?>" target="_blank">
<img src="<?php echo $otp_app['uri_logo']; ?>"
alt="<?php echo $app_name; ?>"
title="<?php echo $app_name; ?>"
<strong><?php echo esc_html( $otp_app['name'] ); ?></strong><br>
<a href="<?php echo esc_html( $otp_app['uri'] ); ?>" target="_blank">
<img src="<?php echo esc_url( $otp_app['uri_logo'] ); ?>"
alt="<?php echo esc_attr( $otp_app['name'] ); ?>"
title="<?php echo esc_attr( $otp_app['name'] ); ?>"
/></a>&nbsp;
<?php foreach ( $app_providers as $app_provider_key => $app_provider ) : ?>
<?php
if ( ! array_key_exists( 'uri_' . $app_provider_key, $otp_app ) ) {
continue;
}
$get_it_on_text = sprintf(
esc_attr__( 'Get it on %s', 'wp-otp' ),
$app_provider['name']
);
// translators: Placeholder is an app provider name.
$get_it_on_text = sprintf( esc_attr__( 'Get it on %s', 'wp-otp' ), $app_provider['name'] );
?>
<a href="<?php echo $otp_app[ 'uri_' . $app_provider_key ]; ?>" target="_blank">
<img src="<?php echo $app_provider['uri_logo']; ?>"
alt="<?php echo $get_it_on_text; ?>"
title="<?php echo $get_it_on_text; ?>"
<a href="<?php echo esc_url( $otp_app[ 'uri_' . $app_provider_key ] ); ?>" target="_blank">
<img src="<?php echo esc_url( $app_provider['uri_logo'] ); ?>"
alt="<?php echo esc_attr( $get_it_on_text ); ?>"
title="<?php echo esc_attr( $get_it_on_text ); ?>"
/></a>&nbsp;
<?php endforeach; ?>
</span>
......
......@@ -71,7 +71,7 @@ class Wp_Otp_Setup {
return;
}
$plugin = $_REQUEST['plugin'] ?? '';
$plugin = sanitize_key( $_REQUEST['plugin'] ?? '' );
check_admin_referer( "activate-plugin_{$plugin}" );
}
......@@ -89,7 +89,7 @@ class Wp_Otp_Setup {
return;
}
$plugin = $_REQUEST['plugin'] ?? '';
$plugin = sanitize_key( $_REQUEST['plugin'] ?? '' );
check_admin_referer( "deactivate-plugin_{$plugin}" );
}
......
......@@ -87,7 +87,7 @@ class Wp_Otp_User_Meta {
public static function get_instance( $user_id = 0 ): Wp_Otp_User_Meta {
if ( null === self::$instance ) {
self::$user_id = $user_id ?: get_current_user_id();
self::$instance = new self;
self::$instance = new self();
}
return self::$instance;
......@@ -126,10 +126,14 @@ class Wp_Otp_User_Meta {
if ( isset( self::$user_meta[ $key ] ) ) {
// Return found option value.
return self::$user_meta[ $key ];
} elseif ( null !== $default ) {
}
if ( null !== $default ) {
// Return overridden default value.
return $default;
} elseif ( isset( self::$default_user_meta[ $key ] ) ) {
}
if ( isset( self::$default_user_meta[ $key ] ) ) {
// Return default option value.
return self::$default_user_meta[ $key ];
}
......
<?php // Silence is golden
<?php // Silence is golden
<?xml version="1.0"?>
<ruleset name="WordPress Coding Standards">
<ruleset name="WordPress Coding Standards based custom ruleset for your plugin">
<description>Generally-applicable sniffs for WordPress plugins.</description>
<arg value="snp"/>
<arg name="colors"/>
<arg name="extensions" value="php"/>
<arg name="encoding" value="utf-8"/>
<arg name="parallel" value="8"/>
<arg name="report-width" value="150"/>
<!-- What to scan -->
<file>.</file>
<exclude-pattern>/tests/</exclude-pattern>
<exclude-pattern>/vendor/</exclude-pattern>
<file>.</file>
<!-- How to scan -->
<!-- Usage instructions: https://github.com/squizlabs/PHP_CodeSniffer/wiki/Usage -->
<!-- Annotated ruleset: https://github.com/squizlabs/PHP_CodeSniffer/wiki/Annotated-ruleset.xml -->
<arg value="sp"/> <!-- Show sniff and progress -->
<arg name="basepath" value="./"/><!-- Strip the file paths down to the relevant bit -->
<arg name="colors"/>
<arg name="extensions" value="php"/>
<arg name="parallel" value="8"/><!-- Enables parallel processing when available for faster results. -->
<!-- Exclude Composer vendor directory. -->
<exclude-pattern>*/vendor/*</exclude-pattern>
<!-- Rules: Check PHP version compatibility -->
<!-- https://github.com/PHPCompatibility/PHPCompatibility#sniffing-your-code-for-compatibility-with-specific-php-versions -->
<config name="testVersion" value="7.2-"/>
<!-- https://github.com/PHPCompatibility/PHPCompatibilityWP -->
<rule ref="PHPCompatibilityWP"/>
<rule ref="WordPress-Core">
<exclude name="Generic.Arrays.DisallowShortArraySyntax.Found"/>
<exclude name="WordPress.PHP.DisallowShortTernary.Found"/>
</rule>
<rule ref="Generic.Commenting.DocComment.MissingShort">
<!-- Temporarily disabled until https://github.com/WordPress/WordPress-Coding-Standards/issues/403 is fixed. -->
<severity>0</severity>
</rule>
<rule ref="PEAR.Functions.FunctionCallSignature">
<exclude name="PEAR.Functions.FunctionCallSignature.ContentAfterOpenBracket"/>
<exclude name="PEAR.Functions.FunctionCallSignature.CloseBracketLine"/>
<exclude name="PEAR.Functions.FunctionCallSignature.MultipleArguments"/>
</rule>
<!-- Rules: WordPress Coding Standards -->
<!-- https://github.com/WordPress-Coding-Standards/WordPress-Coding-Standards -->
<!-- https://github.com/WordPress-Coding-Standards/WordPress-Coding-Standards/wiki/Customizable-sniff-properties -->
<config name="minimum_supported_wp_version" value="4.6"/>
<rule ref="WordPress">
<exclude name="Generic.Arrays.DisallowShortArraySyntax.Found" />
<exclude name="PEAR.Functions.FunctionCallSignature.ContentAfterOpenBracket" />
<exclude name="PEAR.Functions.FunctionCallSignature.CloseBracketLine" />
<exclude name="PEAR.Functions.FunctionCallSignature.MultipleArguments" />
<exclude name="WordPress.PHP.DisallowShortTernary.Found" />
<exclude name="WordPress.WhiteSpace.PrecisionAlignment.Found" />
</rule>
<rule ref="WordPress.WP.I18n">
<properties>
<!-- Value: replace the text domain used. -->
<property name="text_domain" type="array" value="wp-otp"/>
</properties>
</rule>
<rule ref="WordPress.WhiteSpace.ControlStructureSpacing">
<properties>
<property name="blank_line_check" value="true"/>
</properties>
</rule>
</ruleset>
......@@ -12,6 +12,7 @@
namespace Wp_Otp;
use OTPHP\TOTP;
use OTPHP\TOTPInterface;
use WP_Error;
use WP_User;
......@@ -52,8 +53,8 @@ class Wp_Otp_Public {
);
?>
<p>
<label for="wp_otp_code"><?php echo $otp_text; ?></label><br/>
<?php '' !== $otp_text_sub && printf( '<em>%s</em>', $otp_text_sub ); ?>
<label for="wp_otp_code"><?php echo wp_kses_data( $otp_text ); ?></label><br/>
<?php '' !== $otp_text_sub && print wp_kses_data( sprintf( '<em>%s</em>', $otp_text_sub ) ); ?>
<input type="text" class="input" name="wp_otp_code" id="wp_otp_code"/>
</p>
<?php
......@@ -77,7 +78,10 @@ class Wp_Otp_Public {
if ( null === $otp ) {
return $user;
}
$otp_code = $_POST['wp_otp_code'] ?? '';
// We can safely ignore the PHPCS error here, as this gets handled by WP.
// phpcs:ignore WordPress.Security.NonceVerification.Missing
$otp_code = sanitize_key( $_POST['wp_otp_code'] ?? '' );
// If this is a valid OTP code, all good!
if ( $this->verify_otp( $otp, $otp_code ) ) {
......@@ -137,8 +141,8 @@ class Wp_Otp_Public {
}
// First let's check for a valid OTP code input.
$otp_code = substr( $password, - 6 );
$tmp_pass = substr( $password, 0, - 6 );
$otp_code = substr( $password, -6 );
$tmp_pass = substr( $password, 0, -6 );
if ( wp_check_password( $tmp_pass, $user->user_pass, $user->ID ) && $this->verify_otp( $otp, $otp_code ) ) {
$password = $tmp_pass;
return;
......@@ -147,12 +151,12 @@ class Wp_Otp_Public {
// Then check if it's a recovery code.
$recovery_codes = $user_meta_data->get( 'recovery_codes' );
foreach ( array_keys( array_filter( $recovery_codes ) ) as $recovery_code ) {
$otp_code = substr( $password, - strlen( $recovery_code ) );
$otp_code = substr( $password, -strlen( $recovery_code ) );
if ( $otp_code !== $recovery_code ) {
continue;
}
$tmp_pass = substr( $password, 0, - strlen( $recovery_code ) );
$tmp_pass = substr( $password, 0, -strlen( $recovery_code ) );
if ( wp_check_password( $tmp_pass, $user->user_pass, $user->ID ) ) {
// Unset the recovery code that has just been used.
$recovery_codes[ $otp_code ] = false;
......@@ -168,11 +172,11 @@ class Wp_Otp_Public {
*
* @since 0.3.0
*
* @param Wp_Otp_User_Meta $user_meta_data
* @param Wp_Otp_User_Meta $user_meta_data Meta data object of the user.
*
* @return null|TOTP
* @return TOTPInterface|null
*/
private function get_otp_if_enabled( $user_meta_data ): ?TOTP {
private function get_otp_if_enabled( $user_meta_data ): ?TOTPInterface {
if ( $user_meta_data->get( 'enabled' ) && null !== $user_meta_data->get( 'secret' ) ) {
return TOTP::create( $user_meta_data->get( 'secret' ) );
}
......@@ -185,8 +189,8 @@ class Wp_Otp_Public {
*
* @since 0.3.0
*
* @param TOTP $otp
* @param string $otp_code
* @param TOTP $otp OTP object.
* @param string $otp_code OTP code to be verified.
*
* @return bool
*/
......
<?php // Silence is golden
......@@ -98,6 +98,7 @@ This means that you will need to add your OTP (or recovery) code at the end of y
* Require at least PHP 7.2.
* Update OTPHP to 10.0.
* Add native QR code rendering.
* Harden security by adhering to WordPress Code Sniffer.
= 0.4.1 =
* Fix nullable return type when checking if OTP is enabled.
......
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment