time() ) {
$minutes = (int) max( 1, ceil( ( $data['until'] - time() ) / MINUTE_IN_SECONDS ) );
return new \WP_Error(
'siteaudithub_locked',
sprintf(
/* translators: %d: minutes */
_n( 'Error: Too many failed login attempts. Please try again in %d minute.', 'Error: Too many failed login attempts. Please try again in %d minutes.', $minutes, 'siteaudithub' ),
$minutes
)
);
}
return $user;
}
/**
* Count a failed attempt.
*
* @param string $username Submitted username or email.
*/
public static function failed( $username ) {
$key = self::key( $username );
if ( '' === $key ) {
return;
}
$data = self::get( $key );
if ( $data['until'] > time() ) {
return; // Already locked; do not extend.
}
$max = max( 1, (int) Settings::get( 'limit_login_attempts' ) );
$seconds = max( 1, (int) Settings::get( 'limit_login_minutes' ) ) * MINUTE_IN_SECONDS;
++$data['count'];
if ( $data['count'] >= $max ) {
$data['until'] = time() + $seconds;
$data['count'] = 0;
}
set_transient( self::PREFIX . $key, $data, $seconds );
}
/**
* Forget failures after a successful login.
*
* @param string $user_login Login.
*/
public static function succeeded( $user_login ) {
$key = self::key( $user_login );
if ( '' !== $key ) {
delete_transient( self::PREFIX . $key );
}
}
/**
* Remove every lockout (used when the feature is switched off).
*/
public static function clear_all() {
global $wpdb;
$like = $wpdb->esc_like( '_transient_' . self::PREFIX ) . '%';
$keys = $wpdb->get_col( $wpdb->prepare( 'SELECT option_name FROM %i WHERE option_name LIKE %s LIMIT 5000', $wpdb->options, $like ) ); // phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery,WordPress.DB.DirectDatabaseQuery.NoCaching -- Transients have no lookup-by-prefix API.
foreach ( (array) $keys as $name ) {
delete_transient( substr( $name, strlen( '_transient_' ) ) );
}
}
/**
* Stored attempt data.
*
* @param string $key Key.
* @return array{count:int,until:int}
*/
private static function get( $key ) {
$data = get_transient( self::PREFIX . $key );
return array(
'count' => is_array( $data ) && isset( $data['count'] ) ? (int) $data['count'] : 0,
'until' => is_array( $data ) && isset( $data['until'] ) ? (int) $data['until'] : 0,
);
}
/**
* Anonymous key for "username + visitor".
*
* @param string $username Username or email.
* @return string Empty when there is no username.
*/
private static function key( $username ) {
$username = strtolower( trim( (string) $username ) );
if ( '' === $username ) {
return '';
}
// Count "admin" and its email address as the same account.
if ( is_email( $username ) ) {
$user = get_user_by( 'email', $username );
if ( $user ) {
$username = strtolower( $user->user_login );
}
}
$ip = isset( $_SERVER['REMOTE_ADDR'] ) ? sanitize_text_field( wp_unslash( $_SERVER['REMOTE_ADDR'] ) ) : '';
return substr( hash_hmac( 'sha256', $username . '|' . $ip, wp_salt( 'auth' ) ), 0, 32 );
}
}