PluginProbe
Defender Security – Malware Scanner, Login Security & Firewall / 5.3.1
Defender Security – Malware Scanner, Login Security & Firewall v5.3.1
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 5.3.1, at src/model/class-lockout-ip.php

335 lines 8.0 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 * @var int
70 * @defender_property
71 */
72 public $lock_time;
73 /**
74 * Todo: need to use this column less. The lock_time column is used for both lockouts.
75 *
76 * @var int
77 * @defender_property
78 */
79 public $lock_time_404;
80 /**
81 * Table column for attempt.
82 *
83 * @var int
84 * @defender_property
85 */
86 public $attempt;
87 /**
88 * Table column for 404 attempt.
89 *
90 * @var int
91 * @defender_property
92 */
93 public $attempt_404;
94 /**
95 * Table column for meta data.
96 * TODO: maybe add a new column for type of login-/404-lockouts and remove lock_time_404 & attempt_404 columns?
97 *
98 * @var array
99 * @defender_property
100 */
101 public $meta = array();
102
103 /**
104 * Get the record by IP, if it not appears, then create one.
105 *
106 * @param string $ip The IP address to search for.
107 * @param null|string $status The status of the record. If 'unban', it will be converted to STATUS_BLOCKED.
108 * If null, it will not filter by status.
109 * @param boolean $all Whether to return all records that match the IP address.
110 * If true, it will return an array of records.
111 * If false, it will return the first record that matches the IP address.
112 *
113 * @return object|null|array The record that matches the IP address.
114 * If $all is true, it will return an array of records.
115 * If no record is found, it will return null.
116 */
117 public static function get( $ip, $status = null, $all = false ) {
118 $model = Array_Cache::get( $ip, 'ip_lockout' );
119 if ( is_object( $model ) ) {
120 return $model;
121 }
122 $orm = self::get_orm();
123 $builder = $orm->get_repository( self::class )
124 ->where( 'ip', $ip );
125 if ( null !== $status ) {
126 $status = 'unban' === $status ? self::STATUS_BLOCKED : self::STATUS_NORMAL;
127 $builder->where( 'status', $status );
128 }
129
130 if ( true === $all ) {
131 return $builder->get();
132 }
133
134 $model = $builder->first();
135
136 if ( ! is_object( $model ) ) {
137 $model = new Lockout_Ip();
138 $model->ip = $ip;
139 $model->attempt = 0;
140 $model->status = self::STATUS_NORMAL;
141 $model->lockout_message = '';
142 $model->release_time = 0;
143 // @since 3.7.0 The lock_time column is used for both lockouts.
144 $model->lock_time = time();
145 $model->lock_time_404 = 0;
146 $model->attempt_404 = 0;
147 $orm->save( $model );
148 }
149
150 Array_Cache::set( $ip, $model, 'ip_lockout' );
151
152 return $model;
153 }
154
155 /**
156 * Get the first IP.
157 *
158 * @param string $ip The IP address to search for.
159 *
160 * @return null|Model
161 */
162 public static function is_blocklisted_ip( $ip ): ?Model {
163 $orm = self::get_orm();
164
165 return $orm->get_repository( self::class )
166 ->select( 'ip,status' )
167 ->where( 'ip', $ip )
168 ->where( 'status', self::STATUS_BLOCKED )
169 ->first();
170 }
171
172 /**
173 * Maybe unblock IP?
174 *
175 * @param string $ip The IP address to search for.
176 *
177 * @return string
178 */
179 public static function get_unlocked_ip_by( $ip ) {
180 $orm = self::get_orm();
181 $model = $orm->get_repository( self::class )
182 ->where( 'ip', $ip )
183 ->first();
184
185 if ( is_object( $model ) ) {
186 $model->status = self::STATUS_NORMAL;
187 $orm->save( $model );
188
189 return $model->ip;
190 }
191
192 return '';
193 }
194
195 /**
196 * Retrieves bulk IPs based on the provided status, IPs, and limit.
197 *
198 * @param string $status The status of the IPs to retrieve.
199 * @param array|null $ips An array of IPs to retrieve. If null, retrieves all IPs with the given status.
200 * @param int|string|null $limit The maximum number of IPs to retrieve. If null, retrieves all IPs.
201 *
202 * @return array An array of IP models.
203 */
204 public static function get_bulk( string $status, $ips = null, $limit = null ) {
205 $orm = self::get_orm();
206 $builder = $orm->get_repository( self::class );
207 if ( null === $ips ) {
208 $builder->where( 'status', $status );
209 }
210 if ( null !== $ips ) {
211 $builder->where( 'ip', 'in', $ips );
212 }
213 if ( null !== $limit ) {
214 $builder->limit( $limit );
215 }
216
217 return $builder->get();
218 }
219
220 /**
221 * Get the access status of this IP.
222 *
223 * @return array
224 */
225 public function get_access_status(): array {
226 $settings = wd_di()->get( Blacklist_Lockout::class );
227 if (
228 ! in_array( $this->ip, $settings->get_list( 'blocklist' ), true )
229 && ! in_array( $this->ip, $settings->get_list( 'allowlist' ), true )
230 ) {
231 return array( 'na' );
232 }
233
234 $result = array();
235 if ( in_array( $this->ip, $settings->get_list( 'blocklist' ), true ) ) {
236 $result[] = 'banned';
237 }
238 if ( in_array( $this->ip, $settings->get_list( 'allowlist' ), true ) ) {
239 $result[] = 'allowlist';
240 }
241
242 return $result;
243 }
244
245 /**
246 * Returns the text representation of the access status based on the given status code.
247 *
248 * @param string $status The status code to determine the access status text for.
249 * Possible values are: 'banned', 'allowlist', 'na'.
250 *
251 * @return string The text representation of the access status.
252 * Returns an empty string if the status code is not recognized.
253 */
254 public function get_access_status_text( string $status ): string {
255 switch ( $status ) {
256 case 'banned':
257 return esc_html__( 'Banned', 'defender-security' );
258 case 'allowlist':
259 return esc_html__( 'In Allowlist', 'defender-security' );
260 case 'na':
261 return esc_html__( 'Not banned or in allowlist', 'defender-security' );
262 default:
263 return '';
264 }
265 }
266
267 /**
268 * Get locked IPs.
269 *
270 * @return array
271 */
272 public static function query_locked_ip(): array {
273 $orm = self::get_orm();
274 $time = new DateTime( 'now', wp_timezone() );
275
276 return $orm->get_repository( self::class )
277 ->select( 'id,ip,status' )
278 ->where( 'status', self::STATUS_BLOCKED )
279 ->where( 'release_time', '>', $time->getTimestamp() )
280 ->group_by( 'ip' )
281 ->order_by( 'lock_time', 'desc' )
282 ->get_results();
283 }
284
285 /**
286 * Checks if the current object is locked.
287 *
288 * @return bool Returns false if the object is not locked, true otherwise.
289 */
290 public function is_locked(): bool {
291 if ( self::STATUS_BLOCKED === $this->status ) {
292 $time = new DateTime( 'now', wp_timezone() );
293 if ( $this->release_time < $time->getTimestamp() ) {
294 // Unlock it and clear the metadata.
295 $this->attempt = 0;
296 $this->meta = array(
297 'nf' => array(),
298 'login' => array(),
299 );
300 $this->status = self::STATUS_NORMAL;
301 $this->save();
302
303 return false;
304 } else {
305 return true;
306 }
307 }
308
309 return false;
310 }
311
312 /**
313 * Return remaining release time.
314 *
315 * @return int Remaining release time.
316 */
317 public function remaining_release_time(): int {
318 $time = new DateTime( 'now', wp_timezone() );
319
320 return $this->release_time - $time->getTimestamp();
321 }
322
323 /**
324 * Remove all records.
325 *
326 * @return bool|int
327 * @since 3.3.0
328 */
329 public static function truncate() {
330 $orm = self::get_orm();
331
332 return $orm->get_repository( self::class )->truncate();
333 }
334 }
335