Skip to content

Commit 99882fa

Browse files
Merge pull request #608 from CleanTalk/checkers__keep_ip_meta_data.ag
New. User/Comment checker. New class LoginIPKeeper implemented to keep spammer IP address even if spammer session is expired.
2 parents 3778b1d + b7e9ed9 commit 99882fa

9 files changed

Lines changed: 187 additions & 66 deletions

File tree

cleantalk.php

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -166,6 +166,24 @@
166166
$apbct->setConnectionReports();
167167
// SFW update sentinel
168168
$apbct->setSFWUpdateSentinel();
169+
// User IP Keeper - used for checkers
170+
$apbct->setLoginIPKeeper();
171+
172+
add_action('wp_login', 'apbct_wp_login_actions', 10, 2);
173+
174+
/**
175+
* Actions for hook 'wp-login'.
176+
* @param $user_login
177+
* @param $wp_user
178+
*
179+
* @return void
180+
*/
181+
function apbct_wp_login_actions($_user_login, $wp_user)
182+
{
183+
global $apbct;
184+
$apbct->login_ip_keeper->addUserIP($wp_user);
185+
apbct_add_admin_ip_to_swf_whitelist($wp_user);
186+
}
169187

170188
// Disabling comments
171189
if ( $apbct->settings['comments__disable_comments__all'] || $apbct->settings['comments__disable_comments__posts'] || $apbct->settings['comments__disable_comments__pages'] || $apbct->settings['comments__disable_comments__media'] ) {

inc/cleantalk-common.php

Lines changed: 0 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -76,9 +76,6 @@ function apbct_array($array)
7676
// It uses for BuddyPress registrations to avoid double checks
7777
$ct_negative_comment = null;
7878

79-
80-
add_action('wp_login', 'apbct_add_admin_ip_to_swf_whitelist', 10, 2);
81-
8279
/**
8380
* Public action 'plugins_loaded' - Loads locale, see http://codex.wordpress.org/Function_Reference/load_plugin_textdomain
8481
*/

lib/Cleantalk/ApbctWP/FindSpam/ListTable/BadUsers.php

Lines changed: 13 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -69,6 +69,7 @@ public function prepare_items() // phpcs:ignore PSR1.Methods.CamelCapsMethodNam
6969
*/
7070
public function column_ct_username($item) // phpcs:ignore PSR1.Methods.CamelCapsMethodName.NotCamelCaps
7171
{
72+
global $apbct;
7273
$user_obj = isset($item['ct_user_obj']) ? $item['ct_user_obj'] : null;
7374
if (is_null($user_obj)) {
7475
return '';
@@ -95,22 +96,19 @@ public function column_ct_username($item) // phpcs:ignore PSR1.Methods.CamelCaps
9596
$column_content .= '<br/>';
9697

9798
// IP
98-
$user_meta = get_user_meta($user_obj->ID, 'session_tokens', true);
99-
if ( ! empty($user_meta) && is_array($user_meta) ) {
100-
$user_meta = array_values($user_meta);
101-
if ( ! empty($user_meta[0]['ip']) ) {
102-
$ip = $user_meta[0]['ip'];
103-
$column_content .= "<a href='user-edit.php?user_id=$user_obj->ID'>$ip</a>"
104-
. (! $this->apbct->white_label
105-
? "<a href='https://cleantalk.org/blacklists/$ip ' target='_blank'>"
106-
. "&nbsp;<img src='" . APBCT_URL_PATH . "/inc/images/new_window.gif' alt='Ico: open in new window' border='0' style='float:none' />"
107-
. "</a>"
108-
: '');
109-
} else {
110-
$column_content .= esc_html__('No IP address', 'cleantalk-spam-protect');
111-
}
99+
$ip_from_keeper = $apbct->login_ip_keeper->getIP($user_obj->ID);
100+
$ip_from_keeper = null !== $ip_from_keeper
101+
? $ip_from_keeper
102+
: null;
103+
if ( !empty($ip_from_keeper) ) {
104+
$column_content .= "<a href='user-edit.php?user_id=$user_obj->ID'>$ip_from_keeper</a>"
105+
. (! $this->apbct->white_label
106+
? "<a href='https://cleantalk.org/blacklists/$ip_from_keeper ' target='_blank'>"
107+
. "&nbsp;<img src='" . APBCT_URL_PATH . "/inc/images/new_window.gif' alt='Ico: open in new window' border='0' style='float:none' />"
108+
. "</a>"
109+
: '');
112110
} else {
113-
$column_content .= esc_html__('No IP address', 'cleantalk-spam-protect');
111+
$column_content .= esc_html__('No IP adress', 'cleantalk-spam-protect');
114112
}
115113

116114
$actions = array(

lib/Cleantalk/ApbctWP/FindSpam/ListTable/Users.php

Lines changed: 12 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -97,6 +97,7 @@ public function column_cb($item) // phpcs:ignore PSR1.Methods.CamelCapsMethodNam
9797
*/
9898
public function column_ct_username($item) // phpcs:ignore PSR1.Methods.CamelCapsMethodName.NotCamelCaps
9999
{
100+
global $apbct;
100101
$user_obj = $item['ct_username'];
101102
$email = $user_obj->user_email;
102103

@@ -120,20 +121,17 @@ public function column_ct_username($item) // phpcs:ignore PSR1.Methods.CamelCaps
120121
$column_content .= '<br/>';
121122

122123
// IP
123-
$user_meta = get_user_meta($user_obj->ID, 'session_tokens', true);
124-
if ( ! empty($user_meta) && is_array($user_meta) ) {
125-
$user_meta = array_values($user_meta);
126-
if ( ! empty($user_meta[0]['ip']) ) {
127-
$ip = $user_meta[0]['ip'];
128-
$column_content .= "<a href='user-edit.php?user_id=$user_obj->ID'>$ip</a>"
129-
. (! $this->apbct->white_label
130-
? "<a href='https://cleantalk.org/blacklists/$ip ' target='_blank'>"
131-
. "&nbsp;<img src='" . APBCT_URL_PATH . "/inc/images/new_window.gif' alt='Ico: open in new window' border='0' style='float:none' />"
132-
. "</a>"
133-
: '');
134-
} else {
135-
$column_content .= esc_html__('No IP adress', 'cleantalk-spam-protect');
136-
}
124+
$ip_from_keeper = $apbct->login_ip_keeper->getIP($user_obj->ID);
125+
$ip_from_keeper = null !== $ip_from_keeper
126+
? $ip_from_keeper
127+
: null;
128+
if ( !empty($ip_from_keeper) ) {
129+
$column_content .= "<a href='user-edit.php?user_id=$user_obj->ID'>$ip_from_keeper</a>"
130+
. (! $this->apbct->white_label
131+
? "<a href='https://cleantalk.org/blacklists/$ip_from_keeper ' target='_blank'>"
132+
. "&nbsp;<img src='" . APBCT_URL_PATH . "/inc/images/new_window.gif' alt='Ico: open in new window' border='0' style='float:none' />"
133+
. "</a>"
134+
: '');
137135
} else {
138136
$column_content .= esc_html__('No IP adress', 'cleantalk-spam-protect');
139137
}

lib/Cleantalk/ApbctWP/FindSpam/ListTable/UsersScan.php

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -92,7 +92,7 @@ public static function getExtraTableNavInsertDeleteUsers()
9292
if ( defined('APBCT_IS_LOCALHOST') && APBCT_IS_LOCALHOST ) {
9393
$out .= '<div class="ctlk---red bar" style="padding: 10px">';
9494
$out .= '<span>These actions available only for test purpose and buttons are visible only in local env:</span>';
95-
$out .= '<button type="button" class="button button-small action ct_insert_users" style="margin:0 5px">Insert 500 users</button>';
95+
$out .= '<button type="button" class="button button-small action ct_insert_users" style="margin:0 5px">Insert 50 users</button>';
9696
$out .= '<button type="button" class="button button-small action ct_insert_users__delete" style="margin:0 5px">Delete inserted users</button>';
9797
$out .= '</div>';
9898
}
Lines changed: 57 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,57 @@
1+
<?php
2+
3+
namespace Cleantalk\ApbctWP\FindSpam;
4+
5+
use Cleantalk\Common\Helper;
6+
use Cleantalk\Common\TT;
7+
8+
/**
9+
* Class LoginIPKeeper
10+
*
11+
* This class is responsible for managing user IP data, storing it in WordPress options,
12+
* and performing operations such as adding, retrieving, and rotating user IP records.
13+
*/
14+
class LoginIPKeeper
15+
{
16+
/**
17+
* @var string WordPress option name for storing user IP data.
18+
*/
19+
private static $wp_meta_name = '_cleantalk_ip_keeper_data';
20+
21+
/**
22+
* Save the provided in user's IP and last login from session_tokens.
23+
*
24+
* @param \WP_User $wp_user
25+
*
26+
* @return void
27+
*/
28+
public function addUserIP($wp_user)
29+
{
30+
// run record adding to user meta
31+
if ($wp_user instanceof \WP_User && 0 !== $wp_user->ID) {
32+
$session_tokens = get_user_meta($wp_user->ID, 'session_tokens', true);
33+
$data = reset($session_tokens);
34+
if ($data) {
35+
$ip = TT::getArrayValueAsString($data, 'ip');
36+
if ( Helper::ipValidate($ip) ) {
37+
update_user_meta($wp_user->ID, self::$wp_meta_name, $ip);
38+
}
39+
}
40+
}
41+
}
42+
43+
/**
44+
* Retrieves data from user meta of a user by his user ID.
45+
*
46+
* @param int|string $user_id User ID to search for.
47+
*
48+
* @return string|null The selected record property value of user meta data.
49+
* @psalm-suppress PossiblyUnusedMethod
50+
*/
51+
public function getIP($user_id)
52+
{
53+
$user_id = TT::toInt($user_id);
54+
$ip = get_user_meta($user_id, self::$wp_meta_name, true);
55+
return Helper::ipValidate($ip) ? $ip : null;
56+
}
57+
}

lib/Cleantalk/ApbctWP/FindSpam/UsersChecker.php

Lines changed: 17 additions & 33 deletions
Original file line numberDiff line numberDiff line change
@@ -162,16 +162,19 @@ private static function removeSkipRoles(array $users, array $skip_roles)
162162
*/
163163
private static function removeUsersWithoutIPEmail(array $users)
164164
{
165+
global $apbct;
165166
foreach ($users as $index => $user) {
166167
if ( (bool)get_user_meta($user->ID, 'ct_bad') === true ) {
167168
delete_user_meta($user->ID, 'ct_marked_as_spam');
168169
unset($users[$index]);
169170
continue;
170171
}
171172

172-
$user_meta = self::getUserMeta($user->ID);
173-
$ip_of_user_meta = TT::getArrayValueAsString($user_meta, 'ip');
174-
$user_ip = ! empty($ip_of_user_meta) ? trim($ip_of_user_meta) : false;
173+
$ip_from_keeper = $apbct->login_ip_keeper->getIP($user->ID);
174+
$ip_from_keeper = null !== $ip_from_keeper
175+
? $ip_from_keeper
176+
: false;
177+
$user_ip = $ip_from_keeper;
175178
$user_email = ! empty($user->user_email) ? trim($user->user_email) : false;
176179

177180
// Validate IP and Email
@@ -488,6 +491,7 @@ private static function getLogData()
488491
*/
489492
public static function ctGetCsvFile()
490493
{
494+
global $apbct;
491495
AJAXService::checkNonceRestrictingNonAdmins('security');
492496

493497
$text = 'login,email,ip' . PHP_EOL;
@@ -505,18 +509,15 @@ public static function ctGetCsvFile()
505509

506510
$u = get_users($params);
507511
foreach ( $u as $iValue ) {
508-
// gain IP from meta session_tokens
509-
$ip_of_user_meta = 'N/A';
510-
$user_meta_session_tokens = get_user_meta($iValue->ID, 'session_tokens', true);
511-
if (!empty($user_meta_session_tokens) && is_array($user_meta_session_tokens)) {
512-
$user_meta_array = reset($user_meta_session_tokens);
513-
$user_meta_array = !empty($user_meta_array) && is_array($user_meta_array) ? $user_meta_array : false;
514-
$ip_of_user_meta = $user_meta_array ? TT::getArrayValueAsString($user_meta_array, 'ip') : $ip_of_user_meta;
515-
}
512+
// gain IP from keeper
513+
$ip_from_keeper = $apbct->login_ip_keeper->getIP($iValue->ID);
514+
$ip_from_keeper = null !== $ip_from_keeper
515+
? $ip_from_keeper
516+
: 'N/A';
516517

517518
$text .= $iValue->user_login . ',';
518519
$text .= $iValue->data->user_email . ',';
519-
$text .= $ip_of_user_meta;
520+
$text .= $ip_from_keeper;
520521
$text .= PHP_EOL;
521522
}
522523

@@ -535,7 +536,7 @@ public static function ctAjaxInsertUsers()
535536
{
536537
AJAXService::checkNonceRestrictingNonAdmins('security');
537538

538-
global $wpdb;
539+
global $wpdb, $apbct;
539540

540541
//* TEST DELETION
541542
if ( ! empty(Post::get('delete')) ) {
@@ -555,7 +556,7 @@ public static function ctAjaxInsertUsers()
555556
}
556557

557558
// TEST INSERTION
558-
$to_insert = 500;
559+
$to_insert = 50;
559560
$query = 'SELECT network FROM `' . APBCT_TBL_FIREWALL_DATA . '` LIMIT ' . $to_insert . ';';
560561

561562
$result = $wpdb->get_results(
@@ -589,7 +590,7 @@ public static function ctAjaxInsertUsers()
589590
}
590591

591592
update_user_meta($curr_user->ID, 'session_tokens', array($rnd => array('ip' => $ips[$i])));
592-
593+
$apbct->login_ip_keeper->addUserIP($curr_user);
593594
if ( is_int($user_id) ) {
594595
$inserted++;
595596
}
@@ -606,7 +607,7 @@ public static function ctAjaxDeleteAllUsers($count_all = 0)
606607
{
607608
AJAXService::checkNonceRestrictingNonAdmins('security');
608609

609-
global $wpdb;
610+
global $wpdb, $apbct;
610611

611612
$r = self::getCountSpammers();
612613

@@ -729,23 +730,6 @@ public static function getCountBadUsers()
729730
return (int) $count_bad;
730731
}
731732

732-
/**
733-
* @param $user_id
734-
*
735-
* @return array
736-
*/
737-
public static function getUserMeta($user_id)
738-
{
739-
$user_meta = get_user_meta($user_id, 'session_tokens', true);
740-
741-
if ( is_array($user_meta) ) {
742-
$user_meta = array_values($user_meta);
743-
return reset($user_meta);
744-
}
745-
746-
return array();
747-
}
748-
749733
/**
750734
* All users checking
751735
*

lib/Cleantalk/ApbctWP/State.php

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44

55
use AllowDynamicProperties;
66
use ArrayObject;
7+
use Cleantalk\ApbctWP\FindSpam\LoginIPKeeper;
78
use Cleantalk\ApbctWP\Firewall\SFWUpdateSentinel;
89

910
/**
@@ -236,6 +237,9 @@ class State extends \Cleantalk\Common\State
236237
'wl_support_faq' => 'https://wordpress.org/plugins/cleantalk-spam-protect/faq/',
237238
'wl_support_url' => 'https://wordpress.org/support/plugin/cleantalk-spam-protect',
238239
'wl_support_email' => 'support@cleantalk.org',
240+
241+
//IP keeper data
242+
'ip_keeper_data' => array()
239243
);
240244

241245
/**
@@ -379,6 +383,11 @@ class State extends \Cleantalk\Common\State
379383
*/
380384
public $sfw_update_sentinel;
381385

386+
/**
387+
* @var LoginIPKeeper
388+
*/
389+
public $login_ip_keeper;
390+
382391
private $auto_save_defaults_list = array();
383392

384393
public $errors;
@@ -993,6 +1002,11 @@ public function setSFWUpdateSentinel()
9931002
$this->sfw_update_sentinel = new SFWUpdateSentinel();
9941003
}
9951004

1005+
public function setLoginIPKeeper()
1006+
{
1007+
$this->login_ip_keeper = new \Cleantalk\ApbctWP\FindSpam\LoginIPKeeper();
1008+
}
1009+
9961010
/**
9971011
* Get connection reports object. Init one if the connection_reports attribute
9981012
* is empty or not an object of ConnectionReports
Lines changed: 55 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,55 @@
1+
<?php
2+
3+
use PHPUnit\Framework\TestCase;
4+
use Cleantalk\ApbctWP\FindSpam\LoginIPKeeper;
5+
6+
class LoginIPKeeperTest extends TestCase
7+
{
8+
/**
9+
* @var LoginIPKeeper
10+
*/
11+
private $loginIPKeeper;
12+
13+
protected function setUp(): void
14+
{
15+
$this->loginIPKeeper = new LoginIPKeeper();
16+
}
17+
18+
public function testAddRecord()
19+
{
20+
// Create a WordPress user
21+
$user_id = wp_create_user('testuser', 'password', 'testuser@example.com');
22+
$wp_user = get_user_by('id', $user_id);
23+
24+
// Set session tokens
25+
$session_tokens = [
26+
'token1' => [
27+
'ip' => '192.168.1.1',
28+
],
29+
];
30+
update_user_meta($wp_user->ID, 'session_tokens', $session_tokens);
31+
32+
// Call the method
33+
$this->loginIPKeeper->addUserIP($wp_user);
34+
35+
// Assert that the meta record was updated
36+
$ip = get_user_meta($wp_user->ID, '_cleantalk_ip_keeper_data', true);
37+
38+
$this->assertEquals('192.168.1.1', $ip);
39+
}
40+
41+
public function testGetMetaRecordValue()
42+
{
43+
// Create a WordPress user
44+
$user_id = wp_create_user('testuser2', 'password', 'testuser2@example.com');
45+
46+
// Set meta data
47+
update_user_meta($user_id, '_cleantalk_ip_keeper_data', '192.168.1.1');
48+
49+
// Call the method
50+
$result = $this->loginIPKeeper->getIP($user_id);
51+
52+
// Assert the result
53+
$this->assertEquals('192.168.1.1', $result);
54+
}
55+
}

0 commit comments

Comments
 (0)