| @@ -10,8 +10,9 @@ | ||
| 10 | 10 | use WP_User; |
| 11 | 11 | use WP_Error; |
| 12 | 12 | use Calotes\Helper\HTTP; |
| 13 | 13 | use WP_Defender\Component; |
| 14 | +use WP_Defender\Integrations\Woocommerce; | |
| 14 | 15 | use WP_Defender\Model\Setting\Password_Reset; |
| 15 | 16 | use WP_Defender\Component\Security_Tweaks\Servers\Server; |
| 16 | 17 | use WP_Defender\Model\Setting\Password_Protection as Password_Protection_Settings; |
| 17 | 18 | |
| @@ -60,11 +61,11 @@ | ||
| 60 | 61 | * @return string |
| 61 | 62 | */ |
| 62 | 63 | public function get_submitted_password() { |
| 63 | 64 | $password = ''; |
| 64 | - foreach ( array( 'pwd', 'pass1', 'password', 'edd_user_pass' ) as $key ) { | |
| 65 | + foreach ( array( 'pwd', 'pass1', 'password', 'edd_user_pass', 'password_1', 'account_password' ) as $key ) { | |
| 65 | 66 | $submitted_pass = HTTP::post( $key ); |
| 66 | - if ( ! empty( $submitted_pass ) ) { | |
| 67 | + if ( '' !== $submitted_pass ) { | |
| 67 | 68 | $password = $submitted_pass; |
| 68 | 69 | break; |
| 69 | 70 | } |
| 70 | 71 | } |
| @@ -86,12 +87,9 @@ | ||
| 86 | 87 | $url = $this->pwned_api . $subhash; |
| 87 | 88 | $args = array( |
| 88 | 89 | 'method' => 'GET', |
| 89 | 90 | 'headers' => array( |
| 90 | - 'user-agent' => sprintf( | |
| 91 | - 'Mozilla/5.0 (compatible; WPMU DEV Defender/%1$s; +https://wpmudev.com)', | |
| 92 | - DEFENDER_VERSION | |
| 93 | - ), | |
| 91 | + 'user-agent' => defender_get_own_user_agent(), | |
| 94 | 92 | ), |
| 95 | 93 | ); |
| 96 | 94 | |
| 97 | 95 | $request = wp_remote_request( $url, $args ); |
| @@ -126,14 +124,14 @@ | ||
| 126 | 124 | if ( is_multisite() && is_super_admin( $user->ID ) ) { |
| 127 | 125 | return false; |
| 128 | 126 | } |
| 129 | 127 | |
| 130 | - if ( empty( $user->roles ) ) { | |
| 128 | + if ( ! isset( $user->roles ) || array() === $user->roles ) { | |
| 131 | 129 | // WP_User does have data about roles sometimes, e.g. on the Profile page, so we get it from userdata. |
| 132 | 130 | $user_id = $user->ID; |
| 133 | 131 | if ( ! is_multisite() ) { |
| 134 | 132 | $user_meta = get_userdata( $user_id ); |
| 135 | - if ( empty( $user_meta->roles ) ) { | |
| 133 | + if ( ! isset( $user_meta->roles ) || array() === $user_meta->roles ) { | |
| 136 | 134 | // User should have roles. |
| 137 | 135 | $this->log( sprintf( "User ID: %d doesn't have roles.", $user_id ), self::PASSWORD_LOG ); |
| 138 | 136 | |
| 139 | 137 | return false; |
| @@ -140,9 +138,9 @@ | ||
| 140 | 138 | } |
| 141 | 139 | $user_roles = $user_meta->roles; |
| 142 | 140 | } else { |
| 143 | 141 | $arr_user_blogs = get_blogs_of_user( $user_id ); |
| 144 | - if ( empty( $arr_user_blogs ) ) { | |
| 142 | + if ( ! is_array( $arr_user_blogs ) || array() === $arr_user_blogs ) { | |
| 145 | 143 | // User should be associated with some site. |
| 146 | 144 | $this->log( sprintf( 'User ID: %d is not associated with any site.', $user_id ), self::PASSWORD_LOG ); |
| 147 | 145 | |
| 148 | 146 | return false; |
| @@ -148,9 +146,9 @@ | ||
| 148 | 146 | return false; |
| 149 | 147 | } |
| 150 | 148 | $user_blog_id = array_key_first( $arr_user_blogs ); |
| 151 | 149 | $user = new WP_User( $user_id, '', $user_blog_id ); |
| 152 | - if ( empty( $user->roles ) ) { | |
| 150 | + if ( ! isset( $user->roles ) || array() === $user->roles ) { | |
| 153 | 151 | $this->log( sprintf( "User ID: %d doesn't have roles on MU.", $user->ID ), self::PASSWORD_LOG ); |
| 154 | 152 | |
| 155 | 153 | return false; |
| 156 | 154 | } |
| @@ -159,9 +157,10 @@ | ||
| 159 | 157 | } else { |
| 160 | 158 | $user_roles = $user->roles; |
| 161 | 159 | } |
| 162 | 160 | |
| 163 | - return ! empty( array_intersect( $selected_user_roles, $user_roles ) ); | |
| 161 | + $common_roles = array_intersect( $selected_user_roles, $user_roles ); | |
| 162 | + return array() !== $common_roles; | |
| 164 | 163 | } |
| 165 | 164 | |
| 166 | 165 | /** |
| 167 | 166 | * Get reset password redirect URL. |
| @@ -167,24 +166,32 @@ | ||
| 167 | 166 | * Get reset password redirect URL. |
| 168 | 167 | * |
| 169 | 168 | * @param WP_User $user The user for whom to generate the URL. |
| 170 | 169 | * |
| 171 | - * @return string|null | |
| 170 | + * @return string | |
| 172 | 171 | */ |
| 173 | - public function get_reset_password_redirect_url( $user ) { | |
| 174 | - $url = null; | |
| 175 | - | |
| 172 | + public function get_reset_password_redirect_url( $user ): string { | |
| 173 | + $url = ''; | |
| 176 | 174 | $key = get_password_reset_key( $user ); |
| 177 | 175 | if ( ! is_wp_error( $key ) ) { |
| 178 | - $url = add_query_arg( | |
| 179 | - array( | |
| 180 | - 'action' => 'rp', | |
| 181 | - 'key' => $key, | |
| 182 | - 'login' => $user->user_login, | |
| 183 | - ), | |
| 184 | - wp_login_url() | |
| 185 | - ); | |
| 186 | - // Extra hosting checks. | |
| 176 | + if ( wd_di()->get( Woocommerce::class )->is_wc_login_context() && function_exists( 'wc_lostpassword_url' ) ) { | |
| 177 | + $url = add_query_arg( | |
| 178 | + array( | |
| 179 | + 'key' => $key, | |
| 180 | + 'login' => $user->user_login, | |
| 181 | + ), | |
| 182 | + wc_lostpassword_url() | |
| 183 | + ); | |
| 184 | + } else { | |
| 185 | + $url = add_query_arg( | |
| 186 | + array( | |
| 187 | + 'action' => 'rp', | |
| 188 | + 'key' => $key, | |
| 189 | + 'login' => $user->user_login, | |
| 190 | + ), | |
| 191 | + wp_login_url() | |
| 192 | + ); | |
| 193 | + } | |
| 187 | 194 | $this->hosting_compatibility( $key, $user->user_login ); |
| 188 | 195 | } |
| 189 | 196 | |
| 190 | 197 | return $url; |
| @@ -190,26 +197,8 @@ | ||
| 190 | 197 | return $url; |
| 191 | 198 | } |
| 192 | 199 | |
| 193 | 200 | /** |
| 194 | - * Reset password redirect. | |
| 195 | - * | |
| 196 | - * @param string|null $url The URL to redirect to. | |
| 197 | - * @param bool $safe Whether to use safe redirect. | |
| 198 | - * | |
| 199 | - * @return void | |
| 200 | - */ | |
| 201 | - public function reset_password_redirect( $url, $safe = false ) { | |
| 202 | - if ( empty( $url ) ) { | |
| 203 | - return; | |
| 204 | - } | |
| 205 | - $url = esc_url_raw( $url ); | |
| 206 | - header( 'Cache-Control: no-store, no-cache' ); | |
| 207 | - $safe ? wp_safe_redirect( $url ) : wp_redirect( $url ); // phpcs:ignore WordPress.Security.SafeRedirect.wp_redirect_wp_redirect | |
| 208 | - exit(); | |
| 209 | - } | |
| 210 | - | |
| 211 | - /** | |
| 212 | 201 | * Sets a cookie notice. |
| 213 | 202 | * |
| 214 | 203 | * @param string $name The name of the cookie. |
| 215 | 204 | * @param string $value The value of the cookie. |
| @@ -216,9 +205,19 @@ | ||
| 216 | 205 | * @param int $time The expiration time of the cookie. |
| 217 | 206 | */ |
| 218 | 207 | public function set_cookie_notice( $name, $value, $time ) { |
| 219 | 208 | if ( ! isset( $_COOKIE[ $name ] ) ) { |
| 220 | - setcookie( $name, $value, $time, '/' ); | |
| 209 | + setcookie( | |
| 210 | + $name, | |
| 211 | + $value, | |
| 212 | + array( | |
| 213 | + 'expires' => $time, | |
| 214 | + 'path' => '/', | |
| 215 | + 'secure' => is_ssl(), | |
| 216 | + 'httponly' => true, | |
| 217 | + 'samesite' => 'Lax', | |
| 218 | + ) | |
| 219 | + ); | |
| 221 | 220 | } |
| 222 | 221 | } |
| 223 | 222 | |
| 224 | 223 | /** |
| @@ -231,10 +230,20 @@ | ||
| 231 | 230 | * @return void |
| 232 | 231 | * @since 4.3.0 Removed the $time parameter. |
| 233 | 232 | */ |
| 234 | 233 | public function remove_cookie_notice( string $name ): void { |
| 235 | - if ( isset( $_COOKIE[ $name ] ) ) { | |
| 236 | - setcookie( $name, '', time() - YEAR_IN_SECONDS, '/' ); | |
| 234 | + if ( isset( $_COOKIE[ $name ] ) && ! headers_sent() ) { | |
| 235 | + setcookie( | |
| 236 | + $name, | |
| 237 | + '', | |
| 238 | + array( | |
| 239 | + 'expires' => time() - YEAR_IN_SECONDS, | |
| 240 | + 'path' => '/', | |
| 241 | + 'secure' => is_ssl(), | |
| 242 | + 'httponly' => true, | |
| 243 | + 'samesite' => 'Lax', | |
| 244 | + ) | |
| 245 | + ); | |
| 237 | 246 | } |
| 238 | 247 | $this->remove_extra_cookies(); |
| 239 | 248 | } |
| 240 | 249 | |
| @@ -252,9 +261,10 @@ | ||
| 252 | 261 | |
| 253 | 262 | $changed = (int) get_user_meta( $user->ID, 'wd_last_password_change', true ); |
| 254 | 263 | |
| 255 | 264 | if ( ! $changed ) { |
| 256 | - return (int) strtotime( $user->user_registered ); | |
| 265 | + $changed = strtotime( $user->user_registered ); | |
| 266 | + $changed = ! is_int( $changed ) ? (int) $changed : $changed; | |
| 257 | 267 | } |
| 258 | 268 | |
| 259 | 269 | return $changed; |
| 260 | 270 | } |
| @@ -304,9 +314,9 @@ | ||
| 304 | 314 | /** |
| 305 | 315 | * Removes extra cookies set during the password reset process. |
| 306 | 316 | */ |
| 307 | 317 | protected function remove_extra_cookies() { |
| 308 | - if ( isset( $_COOKIE[ 'wp-resetpass-' . COOKIEHASH ] ) && 'flywheel' === Server::get_current_server() ) { | |
| 318 | + if ( isset( $_COOKIE[ 'wp-resetpass-' . COOKIEHASH ] ) && 'flywheel' === Server::get_current_server() && ! headers_sent() ) { | |
| 309 | 319 | setcookie( |
| 310 | 320 | 'wp-resetpass-' . COOKIEHASH, |
| 311 | 321 | '', |
| 312 | 322 | time() - YEAR_IN_SECONDS, |
| @@ -385,9 +395,32 @@ | ||
| 385 | 395 | * @since 2.5.6 |
| 386 | 396 | */ |
| 387 | 397 | do_action( 'wd_forced_reset_password_url', $url, $action ); |
| 388 | 398 | // Redirect to the reset password page. |
| 389 | - $this->reset_password_redirect( $url ); | |
| 399 | + if ( '' !== $url ) { | |
| 400 | + $url = esc_url_raw( $url ); | |
| 401 | + header( 'Cache-Control: no-store, no-cache' ); | |
| 402 | + wp_safe_redirect( $url ); | |
| 403 | + exit(); | |
| 404 | + } | |
| 405 | + } | |
| 406 | + | |
| 407 | + /** | |
| 408 | + * Add WooCommerce error message for password reset warnings. | |
| 409 | + * | |
| 410 | + * @param string $wc_message WooCommerce default error message. | |
| 411 | + * @return string WooCommerce error message. | |
| 412 | + */ | |
| 413 | + public function add_woocommerce_error_message( $wc_message ) { | |
| 414 | + if ( isset( $_COOKIE['display_pwned_password_warning'] ) && function_exists( 'wc_print_notice' ) ) { | |
| 415 | + wc_print_notice( $this->password_protection_model->pwned_actions['force_change_message'], 'error' ); | |
| 416 | + $this->remove_cookie_notice( 'display_pwned_password_warning' ); | |
| 417 | + } | |
| 418 | + if ( isset( $_COOKIE['display_reset_password_warning'] ) && function_exists( 'wc_print_notice' ) ) { | |
| 419 | + wc_print_notice( $this->model->message, 'error' ); | |
| 420 | + $this->remove_cookie_notice( 'display_reset_password_warning' ); | |
| 421 | + } | |
| 422 | + return $wc_message; | |
| 390 | 423 | } |
| 391 | 424 | |
| 392 | 425 | /** |
| 393 | 426 | * Verify if password is weak. |