PluginProbe
Defender Security – Malware Scanner, Login Security & Firewall / 6.2.4
Defender Security – Malware Scanner, Login Security & Firewall v6.2.4
6.3.0 6.2.3 6.2.4 6.2.0 6.2.1 6.2.2 6.1.0 5.3.1 5.4.0 5.4.1 5.5.0 5.5.1 5.6.0 5.6.1 5.6.2 5.7.0 5.7.1 5.7.2 5.8.0 5.8.1 5.9.0 6.0.0 6.0.1 3.0.1 3.1.0 All 141 releases
defender-security / src / model / class-lockout-ip.php

class-lockout-ip.php in Defender Security – Malware Scanner, Login Security & Firewall 6.2.4, at src/model/class-lockout-ip.php

338 lines 8.1 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 <?php
2 /**
3 * Handles interactions with the database table for lockout IPs.
4 *
5 * @package WP_Defender\Model
6 */
7
8 namespace WP_Defender\Model;
9
10 use DateTime;
11 use WP_Defender\DB;
12 use Calotes\Base\Model;
13 use Calotes\Helper\Array_Cache;
14 use WP_Defender\Model\Setting\Blacklist_Lockout;
15
16 /**
17 * Model for the lockout IP table.
18 */
19 class Lockout_Ip extends DB {
20
21 public const STATUS_BLOCKED = 'blocked', STATUS_NORMAL = 'normal';
22
23 /**
24 * Table name.
25 *
26 * @var string
27 */
28 protected $table = 'defender_lockout';
29
30 /**
31 * Primary key column.
32 *
33 * @var int
34 * @defender_property
35 */
36 public $id;
37 /**
38 * Table column for IP address.
39 *
40 * @var string
41 * @defender_property
42 */
43 public $ip;
44 /**
45 * Table column for status.
46 *
47 * @var string
48 * @defender_property
49 */
50 public $status;
51 /**
52 * Table column for lockout message.
53 *
54 * @var string
55 * @defender_property
56 */
57 public $lockout_message;
58 /**
59 * Table column for release time.
60 *
61 * @var int
62 * @defender_property
63 */
64 public $release_time;
65 /**
66 * Table column for lock time.
67 *
68 * @since 3.7.0 Used as timestamp for Login/404 lockouts.
69 * @since 5.4.0 Used as timestamp for Malicious Bot lockouts too.
70 * @since 5.6.0 Used as timestamp for Fake Bot lockouts too.
71 *
72 * @var int
73 * @defender_property
74 */
75 public $lock_time;
76 /**
77 * Todo: need to use this column less. The lock_time column is used for Login/404/Malicious Bot lockouts/Fake Bot lockouts.
78 *
79 * @var int
80 * @defender_property
81 */
82 public $lock_time_404;
83 /**
84 * Table column for attempt.
85 *
86 * @var int
87 * @defender_property
88 */
89 public $attempt;
90 /**
91 * Table column for 404 attempt.
92 *
93 * @var int
94 * @defender_property
95 */
96 public $attempt_404;
97 /**
98 * Table column for meta data.
99 * TODO: maybe add a new column for type of login-/404-lockouts and remove lock_time_404 & attempt_404 columns?
100 *
101 * @var array
102 * @defender_property
103 */
104 public $meta = array();
105
106 /**
107 * Get the record by IP, if it not appears, then create one.
108 *
109 * @param string $ip The IP address to search for.
110 * @param null|string $status The status of the record. If 'unban', it will be converted to STATUS_BLOCKED.
111 * If null, it will not filter by status.
112 * @param boolean $all Whether to return all records that match the IP address.
113 * If true, it will return an array of records.
114 * If false, it will return the first record that matches the IP address.
115 *
116 * @return object|null|array The record that matches the IP address.
117 * If $all is true, it will return an array of records.
118 * If no record is found, it will return null.
119 */
120 public static function get( $ip, $status = null, $all = false ) {
121 $model = Array_Cache::get( $ip, 'ip_lockout' );
122 if ( is_object( $model ) ) {
123 return $model;
124 }
125 $orm = self::get_orm();
126 $builder = $orm->get_repository( self::class )
127 ->where( 'ip', $ip );
128 if ( null !== $status ) {
129 $status = 'unban' === $status ? self::STATUS_BLOCKED : self::STATUS_NORMAL;
130 $builder->where( 'status', $status );
131 }
132
133 if ( true === $all ) {
134 return $builder->get();
135 }
136
137 $model = $builder->first();
138
139 if ( ! is_object( $model ) ) {
140 $model = new Lockout_Ip();
141 $model->ip = $ip;
142 $model->attempt = 0;
143 $model->status = self::STATUS_NORMAL;
144 $model->lockout_message = '';
145 $model->release_time = 0;
146 // @since 3.7.0 The lock_time column is used for both lockouts.
147 $model->lock_time = time();
148 $model->lock_time_404 = 0;
149 $model->attempt_404 = 0;
150 $orm->save( $model );
151 }
152
153 Array_Cache::set( $ip, $model, 'ip_lockout' );
154
155 return $model;
156 }
157
158 /**
159 * Get the first IP.
160 *
161 * @param string $ip The IP address to search for.
162 *
163 * @return null|Model
164 */
165 public static function is_blocklisted_ip( $ip ): ?Model {
166 $orm = self::get_orm();
167
168 return $orm->get_repository( self::class )
169 ->select( 'ip,status' )
170 ->where( 'ip', $ip )
171 ->where( 'status', self::STATUS_BLOCKED )
172 ->first();
173 }
174
175 /**
176 * Maybe unblock IP?
177 *
178 * @param string $ip The IP address to search for.
179 *
180 * @return string
181 */
182 public static function get_unlocked_ip_by( $ip ) {
183 $orm = self::get_orm();
184 $model = $orm->get_repository( self::class )
185 ->where( 'ip', $ip )
186 ->first();
187
188 if ( is_object( $model ) ) {
189 $model->status = self::STATUS_NORMAL;
190 $orm->save( $model );
191
192 return $model->ip;
193 }
194
195 return '';
196 }
197
198 /**
199 * Retrieves bulk IPs based on the provided status, IPs, and limit.
200 *
201 * @param string $status The status of the IPs to retrieve.
202 * @param array|null $ips An array of IPs to retrieve. If null, retrieves all IPs with the given status.
203 * @param int|null $limit The maximum number of IPs to retrieve. If null, retrieves all IPs.
204 *
205 * @return array An array of IP models.
206 */
207 public static function get_bulk( string $status, $ips = null, $limit = null ) {
208 $orm = self::get_orm();
209 $builder = $orm->get_repository( self::class );
210 if ( null === $ips ) {
211 $builder->where( 'status', $status );
212 }
213 if ( null !== $ips ) {
214 $builder->where( 'ip', 'in', $ips );
215 }
216 if ( null !== $limit ) {
217 $builder->limit( $limit );
218 }
219
220 return $builder->get();
221 }
222
223 /**
224 * Get the access status of this IP.
225 *
226 * @return array
227 */
228 public function get_access_status(): array {
229 $settings = wd_di()->get( Blacklist_Lockout::class );
230 if (
231 ! in_array( $this->ip, $settings->get_list( 'blocklist' ), true )
232 && ! in_array( $this->ip, $settings->get_list( 'allowlist' ), true )
233 ) {
234 return array( 'na' );
235 }
236
237 $result = array();
238 if ( in_array( $this->ip, $settings->get_list( 'blocklist' ), true ) ) {
239 $result[] = 'banned';
240 }
241 if ( in_array( $this->ip, $settings->get_list( 'allowlist' ), true ) ) {
242 $result[] = 'allowlist';
243 }
244
245 return $result;
246 }
247
248 /**
249 * Returns the text representation of the access status based on the given status code.
250 *
251 * @param string $status The status code to determine the access status text for.
252 * Possible values are: 'banned', 'allowlist', 'na'.
253 *
254 * @return string The text representation of the access status.
255 * Returns an empty string if the status code is not recognized.
256 */
257 public function get_access_status_text( string $status ): string {
258 switch ( $status ) {
259 case 'banned':
260 return esc_html__( 'Banned', 'defender-security' );
261 case 'allowlist':
262 return esc_html__( 'In Allowlist', 'defender-security' );
263 case 'na':
264 return esc_html__( 'Not banned or in allowlist', 'defender-security' );
265 default:
266 return '';
267 }
268 }
269
270 /**
271 * Get locked IPs.
272 *
273 * @return array
274 */
275 public static function query_locked_ip(): array {
276 $orm = self::get_orm();
277 $time = new DateTime( 'now', wp_timezone() );
278
279 return $orm->get_repository( self::class )
280 ->select( 'id,ip,status' )
281 ->where( 'status', self::STATUS_BLOCKED )
282 ->where( 'release_time', '>', $time->getTimestamp() )
283 ->group_by( 'ip' )
284 ->order_by( 'lock_time', 'desc' )
285 ->get_results();
286 }
287
288 /**
289 * Checks if the current object is locked.
290 *
291 * @return bool Returns false if the object is not locked, true otherwise.
292 */
293 public function is_locked(): bool {
294 if ( self::STATUS_BLOCKED === $this->status ) {
295 $time = new DateTime( 'now', wp_timezone() );
296 if ( $this->release_time < $time->getTimestamp() ) {
297 // Unlock it and clear the metadata.
298 $this->attempt = 0;
299 $this->meta = array(
300 'nf' => array(),
301 'login' => array(),
302 );
303 $this->status = self::STATUS_NORMAL;
304 $this->save();
305
306 return false;
307 } else {
308 return true;
309 }
310 }
311
312 return false;
313 }
314
315 /**
316 * Return remaining release time.
317 *
318 * @return int Remaining release time.
319 */
320 public function remaining_release_time(): int {
321 $time = new DateTime( 'now', wp_timezone() );
322
323 return $this->release_time - $time->getTimestamp();
324 }
325
326 /**
327 * Remove all records.
328 *
329 * @return bool|int
330 * @since 3.3.0
331 */
332 public static function truncate() {
333 $orm = self::get_orm();
334
335 return $orm->get_repository( self::class )->truncate();
336 }
337 }
338