log( 'Unlock Me. Incorrect result. Not found user for UID(-s) ' . $string_uid, Firewall::FIREWALL_LOG ); return false; } $user_email = $user->user_email; if ( ! hash_equals( $hash, hash( 'sha256', $user_email . AUTH_SALT ) ) ) { $this->log( 'Unlock Me. Incorrect result. Invalid hash.', Firewall::FIREWALL_LOG ); return false; } $ips = array(); // Get the line of ID or several IDs for multiple lockouts, and change status(-es) in Unlockout table. $limit_time = strtotime( self::get_expired_time() ); // There are some ID's. $arr_uids = explode( '-', $string_uid ); if ( ! is_array( $arr_uids ) ) { $this->log( 'Unlock Me. Incorrect result. Wrong UID(-s).', Firewall::FIREWALL_LOG ); return false; } foreach ( $arr_uids as $arr_uid ) { $resolved_ip = Unlockout::get_resolved_ip_by( (int) $arr_uid, $user_email, $limit_time ); if ( 'expired' === $resolved_ip ) { return false; } elseif ( '' !== $resolved_ip ) { // This is not expired result and no empty one. $ips[] = $resolved_ip; } } // All is good. IP's were unblocked. if ( array() === $ips ) { return true; } // Work with IP's. $ips = array_unique( $ips ); $first_ip = $ips[0]; // Remove the user IP's from Local Blocklist. $bl = wd_di()->get( \WP_Defender\Model\Setting\Blacklist_Lockout::class ); foreach ( $ips as $ip ) { $bl->remove_from_list( $ip, 'blocklist' ); $this->log( 'Unlock Me. Success. IP ' . $ip . ' have been unblocked from the blocklist.', Firewall::FIREWALL_LOG ); } // Remove IP(s) from Active lockouts. if ( count( $ips ) > 1 ) { $models = Lockout_Ip::get_bulk( Lockout_Ip::STATUS_BLOCKED, $ips ); foreach ( $models as $model ) { $model->status = Lockout_Ip::STATUS_NORMAL; $model->save(); $this->log( 'Unlock Me. Success. IP ' . $model->ip . ' have been unblocked from Active lockouts.', Firewall::FIREWALL_LOG ); } } else { $ip = Lockout_Ip::get_unlocked_ip_by( $first_ip ); if ( '' !== $ip ) { $this->log( 'Unlock Me. Success. IP ' . $ip . ' have been unblocked from Active lockouts.', Firewall::FIREWALL_LOG ); } } // Remove IP(s) from Custom IP Blocklist. $ret = wd_di()->get( IP\Global_IP::class )->remove_from_blocklist( $ips ); if ( is_wp_error( $ret ) ) { $this->log( 'Unlock Me. Error. IP(s) ' . implode( ',', $ips ) . ' have not been unblocked from Custom IP Blocklist.', Firewall::FIREWALL_LOG ); } else { $this->log( 'Unlock Me. Success. IP(s) ' . implode( ',', $ips ) . ' have been unblocked from Custom IP Blocklist.', Firewall::FIREWALL_LOG ); } // Remove the old counter. delete_transient( $this->check_ip_by_remote_addr( $first_ip ) ); // Log the successful unlock. $user_agent = defender_get_data_from_request( 'HTTP_USER_AGENT', 's' ); $model = wd_di()->get( \WP_Defender\Model\Lockout_Log::class ); $model->ip = $first_ip; $model->user_agent = isset( $user_agent ) ? \WP_Defender\Component\User_Agent::fast_cleaning( $user_agent ) : null; $model->date = time(); $model->tried = $user ? $user->user_login : ''; $model->blog_id = get_current_blog_id(); $ip_to_country = $this->ip_to_country( $first_ip ); if ( isset( $ip_to_country['iso'] ) ) { $model->country_iso_code = $ip_to_country['iso']; } $model->type = \WP_Defender\Model\Lockout_Log::IP_UNLOCK; /* translators: %s: IP address */ $model->log = sprintf( __( '%s was unlocked via "Unlock Me (Email)"', 'defender-security' ), $first_ip ); $model->save(); // Redirect. wp_safe_redirect( Mask_Login::maybe_masked_login_url() ); exit; } /** * Display the section if: * 1) no empty IP(-s), * 2) depending on the lockout reason. * * @param string $reason The reason for the lockout. * @param array $ips The IPs involved in the lockout. * * @return bool */ public static function is_displayed( string $reason, array $ips ): bool { $excluded_reasons = apply_filters( 'wpdef_firewall_unlockout_excluded_reasons', array( 'country', 'demo', ) ); $excluded_reasons = is_array( $excluded_reasons ) ? $excluded_reasons : (array) $excluded_reasons; $is_displayed = ! in_array( $reason, $excluded_reasons, true ) && array() !== $ips; $is_displayed = apply_filters( 'wpdef_firewall_unlockout_is_displayed', $is_displayed ); return is_bool( $is_displayed ) ? $is_displayed : (bool) $is_displayed; } /** * Get the limit of failed attempts. * * @return int */ public static function get_attempt_limit(): int { $limit = apply_filters( 'wpdef_firewall_unlockout_attempt_limit', 5 ); return is_int( $limit ) ? $limit : (int) $limit; } /** * Creates a URL for unlocking based on email, user login, and UID array. * * @param string $email The user's email. * @param string $user_login The user's login name. * @param array $arr_uids Array of UIDs. * * @return string The generated URL for unlocking. */ public static function create_url( $email, $user_login, $arr_uids ): string { $string_uids = implode( '-', $arr_uids ); return add_query_arg( array( 'action' => self::SLUG_UNLOCK, // No need IP. 'hash' => hash( 'sha256', $email . AUTH_SALT ), 'login' => $user_login, 'uid' => $string_uids, ), network_site_url() ); } /** * Retrieves the title of the Unlock Me feature. * * @return string The feature title. */ public static function get_feature_title(): string { return esc_html__( 'Unlock Me', 'defender-security' ); } }