PluginProbe
Defender Security – Malware Scanner, Login Security & Firewall / trunk
Defender Security – Malware Scanner, Login Security & Firewall vtrunk
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 3.1.1 All 140 releases
defender-security / src / controller / class-password-reset.php

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

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