PluginProbe
UsersWP – Front-end login form, User Registration, User Profile & Members Directory plugin for WP / 1.2.73
UsersWP – Front-end login form, User Registration, User Profile & Members Directory plugin for WP v1.2.73
1.2.73 1.2.72 1.2.71 1.2.70 1.2.69 1.2.68 1.2.67 1.2.66 1.2.65 1.2.64 1.2.63 trunk 1.0.10 1.0.11 1.0.12 1.0.13 1.0.14 1.0.15 1.0.16 1.0.17 1.0.18 1.0.19 1.0.20 1.0.21 1.0.22 All 173 releases
← All changes | includes/class-forms.php +388 -63 1.2.631.2.73 View file →
@@ -360,22 +360,20 @@
360 360 if ( ! is_user_logged_in() ) {
361 361 return false;
362 362 }
363 363
364 - if ( empty( $_POST['uwp_reset_nonce'] ) || ! wp_verify_nonce( $_POST['uwp_reset_nonce'], 'uwp_reset_nonce_' . $type ) ) {
365 - return;
366 - }
367 -
368 364 if ( is_admin() && defined( 'IS_PROFILE_PAGE' ) && IS_PROFILE_PAGE ) {
369 365 $user_id = get_current_user_id();
370 - // If is another user's profile page
371 - } elseif ( is_admin() && ! empty( $_GET['user_id'] ) && is_numeric( $_GET['user_id'] ) ) {
366 + } elseif ( is_admin() && current_user_can( 'manage_options' ) && ! empty( $_GET['user_id'] ) && is_numeric( $_GET['user_id'] ) ) {
372 367 $user_id = absint( $_GET['user_id'] );
373 - // Otherwise something is wrong.
374 368 } else {
375 369 $user_id = get_current_user_id();
376 370 }
377 371
372 + if ( empty( $_POST['uwp_reset_nonce'] ) || ! wp_verify_nonce( $_POST['uwp_reset_nonce'], 'uwp_reset_nonce_' . $type . '_' . $user_id ) ) {
373 + return;
374 + }
375 +
378 376 $errors = new WP_Error();
379 377 if ( empty( $user_id ) ) {
380 378 $errors->add( 'something_wrong', __( 'Something went wrong. Please try again.', 'userswp' ) );
381 379 }
@@ -1177,8 +1175,11 @@
1177 1175 global $wp2fa;
1178 1176 if ( wp_doing_ajax() && isset( $wp2fa ) && ! empty( $wp2fa ) ) {
1179 1177 remove_action( 'wp_login', array( $wp2fa->login, 'wp_login' ), 20 );
1180 1178 }
1179 + if ( wp_doing_ajax() && class_exists( '\WP2FA\Authenticator\Login' ) ) {
1180 + remove_action( 'wp_login', array( 'WP2FA\Authenticator\Login', 'wp_login' ), 20 );
1181 + }
1181 1182
1182 1183 $user = wp_signon(
1183 1184 array(
1184 1185 'user_login' => $result['username'],
@@ -1187,10 +1188,14 @@
1187 1188 )
1188 1189 );
1189 1190
1190 1191 add_action( 'authenticate', 'gglcptch_login_check', 21, 1 );
1192 + if ( wp_doing_ajax() && class_exists( '\WP2FA\Authenticator\Login' ) ) {
1193 + add_action( 'wp_login', array( 'WP2FA\Authenticator\Login', 'wp_login' ), 20, 2 );
1194 + }
1191 1195
1192 - if ( wp_doing_ajax() && ! is_wp_error( $user ) && isset( $wp2fa ) && ! empty( $wp2fa ) ) {
1196 + $wp2fa_available = ( isset( $wp2fa ) && ! empty( $wp2fa ) ) || class_exists( '\WP2FA\Authenticator\Login' );
1197 + if ( wp_doing_ajax() && ! is_wp_error( $user ) && $wp2fa_available ) {
1193 1198
1194 1199 $two_fa = $this->check_2fa( $user );
1195 1200 if ( isset( $two_fa ) && ! empty( $two_fa ) ) {
1196 1201 if ( is_wp_error( $two_fa ) ) {
@@ -1211,8 +1216,20 @@
1211 1216 }
1212 1217 }
1213 1218 }
1214 1219
1220 + if ( wp_doing_ajax() && is_wp_error( $user ) && $this->wordfence_2fa_available() ) {
1221 + $wfls_2fa = $this->check_wordfence_2fa( $user, $result );
1222 + if ( ! empty( $wfls_2fa ) ) {
1223 + wp_send_json_success(
1224 + array(
1225 + 'html' => $wfls_2fa,
1226 + 'is_2fa' => true,
1227 + )
1228 + );
1229 + }
1230 + }
1231 +
1215 1232 if ( is_wp_error( $user ) ) {
1216 1233 $message = aui()->alert(
1217 1234 array(
1218 1235 'type' => 'error',
@@ -1280,9 +1297,12 @@
1280 1297
1281 1298 return $errors;
1282 1299 }
1283 1300
1284 - $provider = \WP2FA\Authenticator\Login::get_available_providers_for_user( $user );
1301 + $provider = $this->get_wp2fa_provider_for_user( $user );
1302 + if ( empty( $provider ) ) {
1303 + return;
1304 + }
1285 1305
1286 1306 ob_start();
1287 1307 ?>
1288 1308
@@ -1333,9 +1353,9 @@
1333 1353 echo aui()->input(
1334 1354 array( // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped
1335 1355 'type' => 'tel',
1336 1356 'id' => 'authcode',
1337 - 'name' => 'wp-2fa-email-code',
1357 + 'name' => 'authcode',
1338 1358 'placeholder' => esc_attr__( 'Verification Code', 'userswp' ),
1339 1359 'value' => '',
1340 1360 'label' => esc_html__( 'Verification Code', 'userswp' ),
1341 1361 'extra_attributes' => array(
@@ -1370,9 +1390,9 @@
1370 1390 </form>
1371 1391 </div>
1372 1392
1373 1393 <?php
1374 - $codes_remaining = \WP2FA\Authenticator\Backup_Codes::codes_remaining_for_user( $user );
1394 + $codes_remaining = $this->get_wp2fa_backup_codes_remaining( $user );
1375 1395 if ( isset( $codes_remaining ) && $codes_remaining > 0 ) {
1376 1396 ?>
1377 1397 <div class="uwp-2fa-methods-wrap" style="display:none;">
1378 1398 <form name="validate_2fa_backup_codes_form" id="validate_2fa_backup_codes_form"
@@ -1428,9 +1448,247 @@
1428 1448
1429 1449 return ob_get_clean();
1430 1450 }
1431 1451
1452 + /**
1453 + * Checks if the Wordfence Login Security module (2FA) is available.
1454 + *
1455 + * @since 1.2.5
1456 + * @package userswp
1457 + *
1458 + * @return bool
1459 + */
1460 + public function wordfence_2fa_available() {
1461 + return class_exists( '\WordfenceLS\Controller_Users' ) && class_exists( '\WordfenceLS\Controller_TOTP' );
1462 + }
1463 +
1464 + /**
1465 + * Checks whether Wordfence's 2FA requires a verification code for the
1466 + * failed login attempt and, if so, returns the markup for the code entry form.
1467 + *
1468 + * @since 1.2.5
1469 + * @package userswp
1470 + *
1471 + * @param WP_Error $error The error returned by wp_signon().
1472 + * @param array $result The validated login fields (username/password).
1473 + *
1474 + * @return string|void The 2FA form markup, or nothing if not applicable.
1475 + */
1476 + public function check_wordfence_2fa( $error, $result ) {
1477 + if ( 1 == uwp_get_option( 'disable_wordfence_2fa' ) ) {
1478 + return;
1479 + }
1480 +
1481 + if ( ! $this->wordfence_2fa_available() ) {
1482 + return;
1483 + }
1484 +
1485 + if ( ! is_wp_error( $error ) || 'wfls_twofactor_required' !== $error->get_error_code() ) {
1486 + return;
1487 + }
1488 +
1489 + $username = ! empty( $result['username'] ) ? $result['username'] : '';
1490 + if ( empty( $username ) ) {
1491 + return;
1492 + }
1493 +
1494 + $user = is_email( $username ) ? get_user_by( 'email', $username ) : get_user_by( 'login', $username );
1495 + if ( ! $user ) {
1496 + return;
1497 + }
1498 +
1499 + if ( ! \WordfenceLS\Controller_Users::shared()->has_2fa_active( $user ) ) {
1500 + return;
1501 + }
1502 +
1503 + if ( \WordfenceLS\Controller_Users::shared()->has_remembered_2fa( $user ) ) {
1504 + return;
1505 + }
1506 +
1507 + $login_nonce = wp_create_nonce( 'uwp-wfls-2fa-' . $user->ID );
1508 +
1509 + ob_start();
1510 + ?>
1511 +
1512 + <div class="uwp-2fa-methods-wrap">
1513 + <form name="validate_2fa_form" id="validate_2fa_form" class="validate_2fa_form" action="" method="post"
1514 + autocomplete="off">
1515 + <input type="hidden" name="provider" id="provider" value="wordfence"/>
1516 + <input type="hidden" name="uwp-auth-id" id="uwp-auth-id" value="<?php echo esc_attr( $user->ID ); ?>"/>
1517 + <input type="hidden" name="wp-auth-nonce" id="wp-auth-nonce"
1518 + value="<?php echo esc_attr( $login_nonce ); ?>"/>
1519 +
1520 + <p><?php esc_html_e( 'Please enter the authentication code from your two-factor authentication app, or a recovery code, to login:', 'userswp' ); ?></p>
1521 +
1522 + <?php
1523 + echo aui()->input(
1524 + array( // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped
1525 + 'type' => 'text',
1526 + 'id' => 'authcode',
1527 + 'name' => 'authcode',
1528 + 'placeholder' => esc_attr__( 'Authentication Code', 'userswp' ),
1529 + 'value' => '',
1530 + 'label' => esc_html__( 'Authentication Code', 'userswp' ),
1531 + 'extra_attributes' => array(
1532 + 'autocomplete' => 'one-time-code',
1533 + ),
1534 + )
1535 + );
1536 +
1537 + echo aui()->button(
1538 + array( // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped
1539 + 'type' => 'submit',
1540 + 'class' => 'btn btn-primary btn-block text-uppercase uwp-2fa-submit',
1541 + 'name' => 'submit',
1542 + 'icon' => '',
1543 + 'content' => esc_html__( 'Log In', 'userswp' ),
1544 + )
1545 + );
1546 + ?>
1547 + </form>
1548 + </div>
1549 +
1550 + <?php
1551 + return ob_get_clean();
1552 + }
1553 +
1554 + public function get_wp2fa_provider_for_user( $user ) {
1555 + if ( class_exists( '\WP2FA\Authenticator\Login' ) && method_exists( '\WP2FA\Authenticator\Login', 'get_available_providers_for_user' ) ) {
1556 + $provider = \WP2FA\Authenticator\Login::get_available_providers_for_user( $user );
1557 + if ( is_array( $provider ) ) {
1558 + $provider = key( $provider );
1559 + }
1560 +
1561 + return $provider;
1562 + }
1563 +
1564 + if ( class_exists( '\WP2FA\Admin\Helpers\User_Helper' ) && method_exists( '\WP2FA\Admin\Helpers\User_Helper', 'get_enabled_method_for_user' ) ) {
1565 + return \WP2FA\Admin\Helpers\User_Helper::get_enabled_method_for_user( $user );
1566 + }
1567 +
1568 + return '';
1569 + }
1570 +
1571 + public function get_wp2fa_backup_codes_remaining( $user ) {
1572 + if ( class_exists( '\WP2FA\Methods\Backup_Codes' ) && method_exists( '\WP2FA\Methods\Backup_Codes', 'codes_remaining_for_user' ) ) {
1573 + return \WP2FA\Methods\Backup_Codes::codes_remaining_for_user( $user );
1574 + }
1575 +
1576 + if ( class_exists( '\WP2FA\Authenticator\Backup_Codes' ) && method_exists( '\WP2FA\Authenticator\Backup_Codes', 'codes_remaining_for_user' ) ) {
1577 + return \WP2FA\Authenticator\Backup_Codes::codes_remaining_for_user( $user );
1578 + }
1579 +
1580 + return 0;
1581 + }
1582 +
1583 + public function validate_wp2fa_totp_authentication( $user ) {
1584 + if ( class_exists( '\WP2FA\Methods\TOTP' ) && method_exists( '\WP2FA\Methods\TOTP', 'validate_totp_authentication' ) ) {
1585 + return \WP2FA\Methods\TOTP::validate_totp_authentication( $user );
1586 + }
1587 +
1588 + if ( class_exists( '\WP2FA\Authenticator\Login' ) && method_exists( '\WP2FA\Authenticator\Login', 'validate_totp_authentication' ) ) {
1589 + return \WP2FA\Authenticator\Login::validate_totp_authentication( $user );
1590 + }
1591 +
1592 + return false;
1593 + }
1594 +
1595 + public function validate_wp2fa_email_authentication( $user ) {
1596 + if ( class_exists( '\WP2FA\Authenticator\Login' ) && method_exists( '\WP2FA\Authenticator\Login', 'validate_email_authentication' ) ) {
1597 + return \WP2FA\Authenticator\Login::validate_email_authentication( $user );
1598 + }
1599 +
1600 + if ( class_exists( '\WP2FA\Authenticator\Authentication' ) && method_exists( '\WP2FA\Authenticator\Authentication', 'validate_token' ) && isset( $_REQUEST['authcode'] ) ) {
1601 + return \WP2FA\Authenticator\Authentication::validate_token( $user, sanitize_text_field( wp_unslash( $_REQUEST['authcode'] ) ) );
1602 + }
1603 +
1604 + return false;
1605 + }
1606 +
1607 + public function validate_wp2fa_backup_codes( $user ) {
1608 + if ( class_exists( '\WP2FA\Methods\Backup_Codes' ) && method_exists( '\WP2FA\Methods\Backup_Codes', 'validate_backup_codes' ) ) {
1609 + return \WP2FA\Methods\Backup_Codes::validate_backup_codes( $user );
1610 + }
1611 +
1612 + if ( class_exists( '\WP2FA\Authenticator\Backup_Codes' ) && method_exists( '\WP2FA\Authenticator\Backup_Codes', 'validate_backup_codes' ) ) {
1613 + return \WP2FA\Authenticator\Backup_Codes::validate_backup_codes( $user );
1614 + }
1615 +
1616 + return false;
1617 + }
1618 +
1619 + /**
1620 + * Validates the Wordfence 2FA code submitted from the uwp-2fa form and,
1621 + * if valid, completes the login by setting the auth cookie.
1622 + *
1623 + * @since 1.2.5
1624 + * @package userswp
1625 + *
1626 + * @param WP_User $user The user attempting to complete 2FA login.
1627 + *
1628 + * @return void
1629 + */
1630 + public function process_login_wordfence_2fa( $user ) {
1631 + if ( ! $this->wordfence_2fa_available() ) {
1632 + $message = aui()->alert(
1633 + array(
1634 + 'type' => 'error',
1635 + 'content' => __( 'Invalid request! Please try again.', 'userswp' ),
1636 + )
1637 + );
1638 +
1639 + wp_send_json_error( array( 'message' => $message ) );
1640 + }
1641 +
1642 + $nonce = ( isset( $_POST['wp-auth-nonce'] ) ) ? sanitize_textarea_field( wp_unslash( $_POST['wp-auth-nonce'] ) ) : '';
1643 +
1644 + if ( ! wp_verify_nonce( $nonce, 'uwp-wfls-2fa-' . $user->ID ) ) {
1645 + $message = aui()->alert(
1646 + array(
1647 + 'type' => 'error',
1648 + 'content' => __( 'Invalid request! Please try again.', 'userswp' ),
1649 + )
1650 + );
1651 +
1652 + wp_send_json_error( array( 'message' => $message ) );
1653 + }
1654 +
1655 + $code = isset( $_POST['authcode'] ) ? sanitize_text_field( wp_unslash( $_POST['authcode'] ) ) : '';
1656 +
1657 + if ( empty( $code ) || true !== \WordfenceLS\Controller_TOTP::shared()->validate_2fa( $user, $code ) ) {
1658 + do_action( 'wp_login_failed', $user->user_login );
1659 +
1660 + $message = aui()->alert(
1661 + array(
1662 + 'type' => 'error',
1663 + 'content' => __( 'Invalid verification code.', 'userswp' ),
1664 + )
1665 + );
1666 +
1667 + wp_send_json_error( array( 'message' => $message ) );
1668 + }
1669 +
1670 + $remember = ( isset( $_REQUEST['rememberme'] ) ) ? filter_var( $_REQUEST['rememberme'], FILTER_VALIDATE_BOOLEAN ) : false;
1671 +
1672 + // Complete the login the same way wp_signon() would have, now that 2FA has been verified.
1673 + wp_set_auth_cookie( $user->ID, $remember );
1674 + wp_set_current_user( $user->ID );
1675 +
1676 + do_action( 'wp_login', $user->user_login, $user );
1677 +
1678 + $message = aui()->alert(
1679 + array(
1680 + 'type' => 'success',
1681 + 'content' => __( 'Validation successful. Redirecting...', 'userswp' ),
1682 + )
1683 + );
1684 +
1685 + wp_send_json_success( array( 'message' => $message ) );
1686 + }
1687 +
1432 1688 public function process_login_2fa() {
1689 + global $wp2fa;
1690 +
1433 1691 if ( ! isset( $_POST['uwp-auth-id'], $_POST['wp-auth-nonce'] ) ) {
1434 1692 return;
1435 1693 }
1436 1694
@@ -1435,46 +1693,70 @@
1435 1693 }
1436 1694
1437 1695 $auth_id = (int) $_POST['uwp-auth-id'];
1438 1696 $user = get_userdata( $auth_id );
1697 +
1439 1698 if ( ! $user ) {
1440 1699 $message = aui()->alert(
1441 - array(
1700 + array(
1442 1701 'type' => 'error',
1443 1702 'content' => __( 'Invalid user data. Please try again.', 'userswp' ),
1444 - )
1703 + )
1445 1704 );
1446 1705
1447 1706 wp_send_json_error( array( 'message' => $message ) );
1448 1707 }
1449 1708
1450 - global $wp2fa;
1709 + if ( isset( $_POST['provider'] ) ) { // phpcs:ignore WordPress.Security.NonceVerification.Missing
1710 + $provider = sanitize_textarea_field( wp_unslash( $_POST['provider'] ) ); // phpcs:ignore WordPress.Security.NonceVerification.Missing
1711 + } else {
1712 + $provider = '';
1713 + }
1451 1714
1715 + if ( 'wordfence' === $provider ) {
1716 + $this->process_login_wordfence_2fa( $user );
1717 +
1718 + return;
1719 + }
1720 +
1452 1721 $nonce = ( isset( $_POST['wp-auth-nonce'] ) ) ? sanitize_textarea_field( wp_unslash( $_POST['wp-auth-nonce'] ) ) : '';
1722 +
1453 1723 if ( true !== \WP2FA\Authenticator\Login::verify_login_nonce( $user->ID, $nonce ) ) {
1454 -
1455 1724 $message = aui()->alert(
1456 - array(
1725 + array(
1457 1726 'type' => 'error',
1458 1727 'content' => __( 'Invalid request! Please try again.', 'userswp' ),
1459 - )
1728 + )
1460 1729 );
1461 1730
1462 1731 wp_send_json_error( array( 'message' => $message ) );
1463 1732 }
1464 1733
1465 - if ( isset( $_POST['provider'] ) ) {
1466 - $provider = sanitize_textarea_field( wp_unslash( $_POST['provider'] ) );
1467 - $providers = \WP2FA\Authenticator\Login::get_available_providers_for_user( $user );
1468 - if ( isset( $providers[ $provider ] ) ) {
1469 - $provider = $providers[ $provider ];
1470 - } elseif ( isset( $provider ) ) {
1471 - $provider = $provider;
1472 - } else {
1473 - $provider = $provider;
1734 + $error = '';
1735 +
1736 + try {
1737 + $is_enabled = \WP2FA\Admin\Controllers\Settings::is_provider_enabled_for_role( \WP2FA\Admin\Helpers\User_Helper::get_user_role( $user ), $provider );
1738 +
1739 + if ( ! $is_enabled ) {
1740 + $error = __( 'Invalid 2FA provider for user.', 'userswp' );
1474 1741 }
1742 + } catch ( \Exception $e ) {
1743 + $error = $e->getMessage();
1475 1744 }
1476 1745
1746 + if ( $error ) {
1747 + do_action( 'wp_login_failed', $user->user_login );
1748 +
1749 + $message = aui()->alert(
1750 + array(
1751 + 'type' => 'error',
1752 + 'content' => $error
1753 + )
1754 + );
1755 +
1756 + wp_send_json_error( array( 'message' => $message ) );
1757 + }
1758 +
1477 1759 // If this is an email login, or if the user failed validation previously, lets send the code to the user.
1478 1760 if ( 'email' === $provider && true !== \WP2FA\Authenticator\Login::pre_process_email_authentication( $user ) ) {
1479 1761
1480 1762 }
@@ -1479,17 +1761,16 @@
1479 1761
1480 1762 }
1481 1763
1482 1764 // Validate TOTP.
1483 - if ( 'totp' === $provider && true !== \WP2FA\Authenticator\Login::validate_totp_authentication( $user ) ) {
1484 -
1765 + if ( 'totp' === $provider && true !== $this->validate_wp2fa_totp_authentication( $user ) ) {
1485 1766 do_action( 'wp_login_failed', $user->user_login );
1486 1767
1487 1768 $message = aui()->alert(
1488 - array(
1769 + array(
1489 1770 'type' => 'error',
1490 1771 'content' => __( 'Invalid verification code.', 'userswp' ),
1491 - )
1772 + )
1492 1773 );
1493 1774
1494 1775 wp_send_json_error( array( 'message' => $message ) );
1495 1776 }
@@ -1494,27 +1775,26 @@
1494 1775 wp_send_json_error( array( 'message' => $message ) );
1495 1776 }
1496 1777
1497 1778 // Validate Email.
1498 - if ( 'email' === $provider && true !== \WP2FA\Authenticator\Login::validate_email_authentication( $user ) ) {
1499 -
1779 + if ( 'email' === $provider && true !== $this->validate_wp2fa_email_authentication( $user ) ) {
1500 1780 do_action( 'wp_login_failed', $user->user_login );
1501 1781
1502 1782 if ( isset( $_REQUEST['wp-2fa-email-code-resend'] ) && 1 == $_REQUEST['wp-2fa-email-code-resend'] ) {
1503 1783 $message = aui()->alert(
1504 - array(
1784 + array(
1505 1785 'type' => 'info',
1506 1786 'content' => __( 'A new code has been sent.', 'userswp' ),
1507 - )
1787 + )
1508 1788 );
1509 1789
1510 1790 wp_send_json_error( array( 'message' => $message ) );
1511 1791 } else {
1512 1792 $message = aui()->alert(
1513 - array(
1793 + array(
1514 1794 'type' => 'error',
1515 1795 'content' => __( 'Invalid verification code.', 'userswp' ),
1516 - )
1796 + )
1517 1797 );
1518 1798
1519 1799 wp_send_json_error( array( 'message' => $message ) );
1520 1800 }
@@ -1520,17 +1800,16 @@
1520 1800 }
1521 1801 }
1522 1802
1523 1803 // Backup Codes.
1524 - if ( 'backup_codes' === $provider && true !== \WP2FA\Authenticator\Login::validate_backup_codes( $user ) ) {
1525 -
1804 + if ( 'backup_codes' === $provider && true !== $this->validate_wp2fa_backup_codes( $user ) ) {
1526 1805 do_action( 'wp_login_failed', $user->user_login );
1527 1806
1528 1807 $message = aui()->alert(
1529 - array(
1808 + array(
1530 1809 'type' => 'error',
1531 1810 'content' => __( 'Invalid backup code.', 'userswp' ),
1532 - )
1811 + )
1533 1812 );
1534 1813
1535 1814 wp_send_json_error( array( 'message' => $message ) );
1536 1815 }
@@ -1538,8 +1817,9 @@
1538 1817 \WP2FA\Authenticator\Login::delete_login_nonce( $user->ID );
1539 1818
1540 1819 $rememberme = false;
1541 1820 $remember = ( isset( $_REQUEST['rememberme'] ) ) ? filter_var( $_REQUEST['rememberme'], FILTER_VALIDATE_BOOLEAN ) : '';
1821 +
1542 1822 if ( ! empty( $remember ) ) {
1543 1823 $rememberme = true;
1544 1824 }
1545 1825
@@ -1546,13 +1826,17 @@
1546 1826 wp_set_auth_cookie( $user->ID, $rememberme );
1547 1827
1548 1828 do_action( 'two_factor_user_authenticated', $user );
1549 1829
1830 + if ( defined( 'WP_2FA_PREFIX' ) ) {
1831 + do_action( WP_2FA_PREFIX . 'user_authenticated', $user );
1832 + }
1833 +
1550 1834 $message = aui()->alert(
1551 - array(
1835 + array(
1552 1836 'type' => 'success',
1553 1837 'content' => __( 'Validation successful. Redirecting...', 'userswp' ),
1554 - )
1838 + )
1555 1839 );
1556 1840
1557 1841 wp_send_json_success( array( 'message' => $message ) );
1558 1842 }
@@ -1649,9 +1933,12 @@
1649 1933 }
1650 1934
1651 1935 do_action( 'uwp_after_validate', $result, 'forgot', $data );
1652 1936
1653 - $user_data = get_user_by( 'email', $data['email'] );
1937 + $login_or_email = trim( $data['email'] );
1938 + $user_data = is_email( $login_or_email )
1939 + ? get_user_by( 'email', $login_or_email )
1940 + : get_user_by( 'login', $login_or_email );
1654 1941
1655 1942 // if no user we fake it and bail
1656 1943 if ( ! $user_data ) {
1657 1944 $args = apply_filters(
@@ -1657,9 +1944,9 @@
1657 1944 $args = apply_filters(
1658 1945 'uwp_forgot_error_message',
1659 1946 array(
1660 1947 'type' => 'error',
1661 - 'content' => __( 'Invalid email or user doesn\'t exists.', 'userswp' ),
1948 + 'content' => __( 'Invalid username/email or user doesn\'t exist.', 'userswp' ),
1662 1949 )
1663 1950 );
1664 1951
1665 1952 $message = aui()->alert( $args );
@@ -1709,9 +1996,8 @@
1709 1996 }
1710 1997
1711 1998 $as_password = apply_filters( 'uwp_forgot_message_as_password', false );
1712 1999
1713 - global $wpdb, $wp_hasher;
1714 2000 $reset_link = '';
1715 2001
1716 2002 if ( $as_password ) {
1717 2003 $new_pass = wp_generate_password( 12, false );
@@ -1723,17 +2009,21 @@
1723 2009 $message .= '<p>' . sprintf( __( 'Username: %s', 'userswp' ), $user_data->user_login ) . '</p>';
1724 2010 $message .= '<p>' . sprintf( __( 'Password: %s', 'userswp' ), $new_pass ) . '</p>';
1725 2011
1726 2012 } else {
1727 - $key = wp_generate_password( 20, false );
1728 - do_action( 'retrieve_password_key', $user_data->user_login, $key );
2013 + // Use WordPress core to generate, hash (wp_fast_hash in WP 6.8+), and store the reset key.
2014 + // This ensures compatibility with check_password_reset_key() on all WP versions.
2015 + $key = get_password_reset_key( $user_data );
1729 2016
1730 - if ( empty( $wp_hasher ) ) {
1731 - require_once ABSPATH . 'wp-includes/class-phpass.php';
1732 - $wp_hasher = new PasswordHash( 8, true );
2017 + if ( is_wp_error( $key ) ) {
2018 + if ( wp_doing_ajax() ) {
2019 + wp_send_json_error( $key->get_error_message() );
2020 + } else {
2021 + $uwp_notices[] = array( 'forgot' => aui()->alert( array( 'type' => 'error', 'content' => $key->get_error_message() ) ) );
2022 + return;
2023 + }
1733 2024 }
1734 - $hashed = $wp_hasher->HashPassword( $key );
1735 - $wpdb->update( $wpdb->users, array( 'user_activation_key' => time() . ':' . $hashed ), array( 'user_login' => $user_data->user_login ) ); // phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery, WordPress.DB.DirectDatabaseQuery.NoCaching
2025 +
1736 2026 $message = '<p>' . __( 'You have requested to reset your password for the following account:', 'userswp' ) . '</p>';
1737 2027 $message .= home_url( '/' ) . '</p>';
1738 2028 $message .= '<p>' . sprintf( __( 'Username: %s', 'userswp' ), $user_data->user_login ) . '</p>';
1739 2029 $message .= '<p>' . __( 'If this was by mistake, just ignore this email and nothing will happen.', 'userswp' ) . '</p>';
@@ -2026,8 +2316,21 @@
2026 2316 unset( $uploads_result[ $upload_file_key ] );
2027 2317 }
2028 2318 }
2029 2319
2320 + global $wpdb;
2321 + $file_field_names = $wpdb->get_col(
2322 + $wpdb->prepare(
2323 + "SELECT htmlvar_name FROM " . uwp_get_table_prefix() . "uwp_form_fields WHERE form_type = %s AND field_type IN ('file','image')",
2324 + 'account'
2325 + )
2326 + );
2327 + foreach ( $file_field_names as $file_field_name ) {
2328 + if ( isset( $result[ $file_field_name ] ) && ! isset( $uploads_result[ $file_field_name ] ) ) {
2329 + unset( $result[ $file_field_name ] );
2330 + }
2331 + }
2332 +
2030 2333 $result = array_merge( $result, $uploads_result );
2031 2334
2032 2335 $args = array(
2033 2336 'ID' => get_current_user_id(),
@@ -2315,22 +2618,35 @@
2315 2618 $value = uwp_get_usermeta( $user_id, $htmlvar );
2316 2619
2317 2620 uwp_update_usermeta( $user_id, $htmlvar, '' );
2318 2621
2319 - if ( $value ) {
2622 + if ( $value && validate_file( $value ) === 0 ) {
2320 2623 $uploads = wp_upload_dir();
2321 2624 $upload_path = $uploads['basedir'];
2322 - $unlink_file = untrailingslashit( $upload_path ) . '/' . ltrim( $value, '/' );
2323 2625
2324 - if ( is_file( $unlink_file ) && file_exists( $unlink_file ) ) {
2325 - @unlink( $unlink_file );
2626 + if ( strpos( $value, 'http://' ) === 0 || strpos( $value, 'https://' ) === 0 ) {
2627 + // Get the relative url.
2628 + $value = uwp_get_file_relative_url( $value );
2629 + }
2326 2630
2631 + $unlink_file = untrailingslashit( $upload_path ) . '/' . trim( $value, '/\\' );
2632 +
2633 + // Canonicalize and enforce containment inside the uploads directory before deleting.
2634 + $real_upload_path = realpath( $upload_path );
2635 + $real_unlink_file = realpath( $unlink_file );
2636 +
2637 + if ( $real_upload_path && $real_unlink_file && is_file( $real_unlink_file )
2638 + && strpos( $real_unlink_file, $real_upload_path . DIRECTORY_SEPARATOR ) === 0 ) {
2639 + wp_delete_file( $real_unlink_file );
2640 +
2327 2641 // For avatar/banner, also remove the original (non-thumb) file.
2328 2642 if ( $type ) {
2329 - $unlink_ori_file = str_replace( '_uwp_' . $type . '_thumb' . '.', '.', $unlink_file );
2643 + $unlink_ori_file = str_replace( '_uwp_' . $type . '_thumb' . '.', '.', $real_unlink_file );
2644 + $real_unlink_ori_file = realpath( $unlink_ori_file );
2330 2645
2331 - if ( is_file( $unlink_ori_file ) && file_exists( $unlink_ori_file ) ) {
2332 - @unlink( $unlink_ori_file );
2646 + if ( $real_unlink_ori_file && is_file( $real_unlink_ori_file )
2647 + && strpos( $real_unlink_ori_file, $real_upload_path . DIRECTORY_SEPARATOR ) === 0 ) {
2648 + wp_delete_file( $real_unlink_ori_file );
2333 2649 }
2334 2650 }
2335 2651 }
2336 2652 }
@@ -3813,17 +4129,26 @@
3813 4129 $site_title = uwp_get_form_label( $field );
3814 4130 $required_msg = ( ! empty( $field->is_required ) && $field->required_msg != '') ? __( stripslashes( $field->required_msg ), 'userswp' ) : '';
3815 4131 $validation_text = ! empty( $field->validation_msg ) ? __( stripslashes( $field->validation_msg ), 'userswp' ) : '';
3816 4132
4133 + $is_forgot_email = ( $form_type === 'forgot' && $field->htmlvar_name === 'email' );
4134 + $input_type = $is_forgot_email ? 'text' : 'email';
4135 + if ( $is_forgot_email ) {
4136 + $site_title = __( 'Username or Email', 'userswp' );
4137 + $placeholder = $site_title . ( ! empty( $field->is_required ) ? ' *' : '' );
4138 + } else {
4139 + $placeholder = uwp_get_field_placeholder( $field );
4140 + }
4141 +
3817 4142 if ( $design_style ) {
3818 4143 $required = ! empty( $field->is_required ) ? ' <span class="text-danger">*</span>' : '';
3819 4144
3820 4145 echo aui()->input(
3821 4146 array( // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped
3822 - 'type' => 'email',
4147 + 'type' => $input_type,
3823 4148 'id' => esc_attr( $field->htmlvar_name ),
3824 4149 'name' => esc_attr( $field->htmlvar_name ),
3825 - 'placeholder' => esc_attr( uwp_get_field_placeholder( $field ) ),
4150 + 'placeholder' => esc_attr( $placeholder ),
3826 4151 'title' => esc_html( $site_title ),
3827 4152 'value' => esc_attr( wp_unslash( $value ) ),
3828 4153 'required' => (bool) $field->is_required,
3829 4154 'validation_text' => $validation_text != '' ? esc_attr( $validation_text ) : esc_attr( $required_msg ),
@@ -3859,9 +4184,9 @@
3859 4184
3860 4185 <input name="<?php echo esc_attr( $field->htmlvar_name ); ?>"
3861 4186 class="<?php echo esc_attr( $field->css_class ); ?> uwp_textfield <?php echo esc_attr( $bs_form_control ); ?>"
3862 4187 id="<?php echo esc_attr( $field->htmlvar_name ); ?>"
3863 - placeholder="<?php echo esc_attr( uwp_get_field_placeholder( $field ) ); ?>"
4188 + placeholder="<?php echo esc_attr( $placeholder ); ?>"
3864 4189 value="<?php echo esc_attr( stripslashes( $value ) ); ?>"
3865 4190 title="<?php echo esc_attr( $site_title ); ?>"
3866 4191 <?php
3867 4192 if ( $field->is_required == 1 ) {
@@ -3867,9 +4192,9 @@
3867 4192 if ( $field->is_required == 1 ) {
3868 4193 echo 'required="required"';
3869 4194 }
3870 4195 ?>
3871 - type="email"
4196 + type="<?php echo esc_attr( $input_type ); ?>"
3872 4197 />
3873 4198 <span class="uwp_message_note"><?php echo wp_kses_post( uwp_get_field_description( $field ) ); ?></span>
3874 4199 <?php if ( $field->is_required ) { ?>
3875 4200 <span class="uwp_message_error invalid-feedback"><?php echo esc_html__( stripslashes( $field->required_msg ), 'userswp' ); ?></span>