PluginProbe
Defender Security – Malware Scanner, Login Security & Firewall / 5.8.1
Defender Security – Malware Scanner, Login Security & Firewall v5.8.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 / controller / class-password-reset.php

class-password-reset.php in Defender Security – Malware Scanner, Login Security & Firewall 5.8.1, at src/controller/class-password-reset.php

357 lines 9.3 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 <?php
2 /**
3 * Handles password reset operations.
4 *
5 * @package WP_Defender\Controller
6 */
7
8 namespace WP_Defender\Controller;
9
10 use WP_User;
11 use WP_Error;
12 use Exception;
13 use WP_Defender\Event;
14 use Calotes\Component\Request;
15 use Calotes\Component\Response;
16 use WP_Defender\Traits\Formats;
17 use WP_Defender\Component\Config\Config_Hub_Helper;
18
19 /**
20 * Handles password reset operations.
21 */
22 class Password_Reset extends Event {
23
24 use Formats;
25
26 /**
27 * The model for handling the data.
28 *
29 * @var \WP_Defender\Model\Setting\Password_Reset
30 */
31 protected $model;
32
33 /**
34 * Service for handling logic.
35 *
36 * @var \WP_Defender\Component\Password_Protection
37 */
38 protected $service;
39
40 /**
41 * Default message.
42 *
43 * @var string
44 */
45 public $default_msg;
46
47 /**
48 * Initializes the model and service, registers routes, and sets up scheduled events if the model is active.
49 */
50 public function __construct() {
51 $this->model = $this->get_model();
52 $this->service = wd_di()->get( \WP_Defender\Component\Password_Protection::class );
53 $default_values = $this->model->get_default_values();
54 $this->default_msg = $default_values['message'];
55 add_filter( 'wp_defender_advanced_tools_data', array( $this, 'script_data' ) );
56 $this->register_routes();
57 if ( $this->model->is_active() ) {
58 // Update site url on sub-site when MaskLogin is disabled.
59 if (
60 is_multisite() && ! is_main_site()
61 && ! wd_di()->get( \WP_Defender\Model\Setting\Mask_Login::class )->is_active()
62 ) {
63 add_filter( 'network_site_url', array( $this, 'filter_site_url' ), 100, 2 );
64 }
65 add_action( 'validate_password_reset', array( $this, 'handle_reset_check_password' ), 10, 2 );
66 add_action( 'profile_update', array( $this, 'handle_update_user' ), 10, 2 );
67 add_action( 'password_reset', array( $this, 'handle_password_reset' ), 10 );
68 add_action( 'wp_authenticate_user', array( $this, 'handle_login_password' ), 999, 2 );
69 // No use 'user_profile_update_errors' because there aren't checks for password resetting for logged user in.
70 }
71 }
72
73 /**
74 * Filters the site URL for password resetting on sub-sites.
75 *
76 * @param string $url The original site URL.
77 * @param string $path The path to append to the site URL.
78 *
79 * @return string The modified site URL.
80 */
81 public function filter_site_url( string $url, string $path ) {
82 $action = defender_get_data_from_request( 'action', 'g' );
83 if ( $path && is_string( $path )
84 && in_array( $action, array( 'rp', 'resetpass' ), true )
85 && false !== stristr( $url, 'wp-login.php' )
86 ) {
87 return get_option( 'siteurl' ) . '/' . ltrim( $path, '/' );
88 }
89
90 return $url;
91 }
92
93 /**
94 * Get model.
95 *
96 * @return \WP_Defender\Model\Setting\Password_Reset
97 */
98 private function get_model() {
99 if ( is_object( $this->model ) ) {
100 return $this->model;
101 }
102
103 return new \WP_Defender\Model\Setting\Password_Reset();
104 }
105
106 /**
107 * Handle password update on login.
108 *
109 * @param WP_User|WP_Error $user WP_User object or WP_Error.
110 * @param string $password Password plain string.
111 *
112 * @return WP_User|WP_Error Return user object or error object.
113 */
114 public function handle_login_password( $user, $password ) {
115 if ( is_wp_error( $user ) || ! $user instanceof WP_User ) {
116 return $user;
117 }
118 if ( ! is_string( $password ) || '' === trim( $password ) ) {
119 return new WP_Error(
120 'defender_invalid_password',
121 esc_html__( 'Invalid user data.', 'defender-security' )
122 );
123 }
124 $this->service->do_force_reset( $user, $password );
125
126 return $user;
127 }
128
129 /**
130 * Handle password update on password reset.
131 *
132 * @param WP_Error $errors Error object.
133 * @param WP_Error|WP_User $user WP_User object or WP_Error.
134 *
135 * @return null|WP_Error
136 */
137 public function handle_reset_check_password( WP_Error $errors, $user ) {
138 if ( is_wp_error( $user ) ) {
139 return;
140 }
141
142 if ( ! $this->service->is_enabled_by_user_role( $user, $this->model->user_roles ) ) {
143 return;
144 }
145
146 // Check if display_reset_password_warning cookie enabled then show warning message on reset password page.
147 if ( isset( $_COOKIE['display_reset_password_warning'] ) ) {
148 $message = ! isset( $this->model->message ) || ! is_string( $this->model->message ) || '' === trim( $this->model->message )
149 ? $this->default_msg
150 : $this->model->message;
151 $errors->add( 'defender_password_reset', $message );
152 // Remove the one time cookie notice once it's displayed.
153 $this->service->remove_cookie_notice( 'display_reset_password_warning' );
154
155 return $errors;
156 }
157
158 $login_password = $this->service->get_submitted_password();
159 $user_id = isset( $user->ID ) ? $user->ID : 0;
160 $user_id = is_int( $user_id ) ? $user_id : (int) $user_id;
161 if (
162 0 < $user_id
163 && is_string( $login_password ) && '' !== trim( $login_password )
164 && wp_check_password( $login_password, get_userdata( $user_id )->user_pass, $user_id )
165 ) {
166 $message = wp_kses(
167 esc_html__( 'This password has been used already. Please choose a different one.', 'defender-security' ),
168 array( 'strong' => array() )
169 );
170 $errors->add( 'defender_password_reset', $message );
171
172 return $errors;
173 }
174
175 return $errors;
176 }
177
178 /**
179 * Update the time when a user resets their password.
180 *
181 * @param WP_User $user User object.
182 *
183 * @return void
184 */
185 public function handle_password_reset( WP_User $user ): void {
186 $this->service->handle_password_updated( $user );
187 }
188
189 /**
190 * Update password data when a user object is set or updated.
191 *
192 * @param int $user_id User ID.
193 * @param WP_User $old_user_data Old user data.
194 *
195 * @return void
196 */
197 public function handle_update_user( int $user_id, WP_User $old_user_data ) {
198 $user = get_userdata( $user_id );
199
200 if ( $user->user_pass === $old_user_data->user_pass ) {
201 return;
202 }
203
204 $this->service->handle_password_updated( $user );
205 }
206
207 /**
208 * Provide data to the frontend via localized script.
209 *
210 * @param array $data Data collection is ready to passed.
211 *
212 * @return array Modified data array with added this controller data.
213 */
214 public function script_data( array $data ): array {
215 $data['password_reset'] = $this->data_frontend();
216
217 return $data;
218 }
219
220 /**
221 * Save settings.
222 *
223 * @param Request $request The request object containing new settings data.
224 *
225 * @return Response
226 * @defender_route
227 */
228 public function save_settings( Request $request ): Response {
229 $data = $request->get_data_by_model( $this->model );
230 $this->model->import( $data );
231 if ( $this->model->validate() ) {
232 $this->model->save();
233 Config_Hub_Helper::set_clear_active_flag();
234
235 $response = array(
236 'message' => esc_html__( 'Your settings have been updated.', 'defender-security' ),
237 'auto_close' => true,
238 );
239
240 return new Response( true, array_merge( $response, $this->data_frontend() ) );
241 }
242
243 return new Response(
244 false,
245 array(
246 'message' => $this->model->get_formatted_errors(),
247 )
248 );
249 }
250
251 /**
252 * Save settings.
253 *
254 * @param Request $request The request object containing new settings data.
255 *
256 * @return Response
257 * @defender_route
258 */
259 public function toggle_reset( Request $request ) {
260 $response = array();
261 $data = $request->get_data_by_model( $this->model );
262 if ( isset( $data['expire_force'] ) && true === $data['expire_force'] ) {
263 $data['force_time'] = time();
264 $response = array(
265 'message' => esc_html__(
266 'Selected user roles are required to reset their password upon next login.',
267 'defender-security'
268 ),
269 );
270 } else {
271 $response['message'] = esc_html__( 'Force Reset Password has been disabled.', 'defender-security' );
272 }
273 $this->model->import( $data );
274 if ( $this->model->validate() ) {
275 $this->model->save();
276 Config_Hub_Helper::set_clear_active_flag();
277
278 return new Response( true, array_merge( $response, $this->data_frontend() ) );
279 }
280
281 return new Response(
282 false,
283 array(
284 'message' => $this->model->get_formatted_errors(),
285 )
286 );
287 }
288
289 /**
290 * Removes settings for all submodules.
291 */
292 public function remove_settings() {
293 }
294
295 /**
296 * Delete all the data & the cache.
297 */
298 public function remove_data(): void {
299 delete_metadata( 'user', null, 'wd_last_password_change', null, true );
300 }
301
302 /**
303 * Provides data for the frontend.
304 *
305 * @return array An array of data for the frontend.
306 */
307 public function data_frontend(): array {
308 $model = $this->get_model();
309
310 return array_merge(
311 array(
312 'model' => $model->export(),
313 'all_roles' => wp_list_pluck( get_editable_roles(), 'name' ),
314 'reset_last' => ! isset( $model->force_time ) || ! is_int( $model->force_time ) || 0 >= $model->force_time
315 ? ''
316 : $this->format_date_time( $model->force_time ),
317 'default_message' => $this->default_msg,
318 ),
319 $this->dump_routes_and_nonces()
320 );
321 }
322
323 /**
324 * Imports data into the model.
325 *
326 * @param array $data Data to be imported into the model.
327 *
328 * @throws Exception If table is not defined.
329 */
330 public function import_data( array $data ) {
331 $model = $this->get_model();
332
333 $model->import( $data );
334 if ( $model->validate() ) {
335 $model->save();
336 }
337 }
338
339 /**
340 * Converts the current object state to an array.
341 *
342 * @return array The array representation of the object.
343 */
344 public function to_array(): array {
345 return array();
346 }
347
348 /**
349 * Exports strings.
350 *
351 * @return array An array of strings.
352 */
353 public function export_strings() {
354 return array();
355 }
356 }
357