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.74 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 All 174 releases
← All changes | includes/class-forms.php +383 -56 1.2.641.2.73 View file →
@@ -1175,8 +1175,11 @@
1175 1175 global $wp2fa;
1176 1176 if ( wp_doing_ajax() && isset( $wp2fa ) && ! empty( $wp2fa ) ) {
1177 1177 remove_action( 'wp_login', array( $wp2fa->login, 'wp_login' ), 20 );
1178 1178 }
1179 + if ( wp_doing_ajax() && class_exists( '\WP2FA\Authenticator\Login' ) ) {
1180 + remove_action( 'wp_login', array( 'WP2FA\Authenticator\Login', 'wp_login' ), 20 );
1181 + }
1179 1182
1180 1183 $user = wp_signon(
1181 1184 array(
1182 1185 'user_login' => $result['username'],
@@ -1185,10 +1188,14 @@
1185 1188 )
1186 1189 );
1187 1190
1188 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 + }
1189 1195
1190 - 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 ) {
1191 1198
1192 1199 $two_fa = $this->check_2fa( $user );
1193 1200 if ( isset( $two_fa ) && ! empty( $two_fa ) ) {
1194 1201 if ( is_wp_error( $two_fa ) ) {
@@ -1209,8 +1216,20 @@
1209 1216 }
1210 1217 }
1211 1218 }
1212 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 +
1213 1232 if ( is_wp_error( $user ) ) {
1214 1233 $message = aui()->alert(
1215 1234 array(
1216 1235 'type' => 'error',
@@ -1278,9 +1297,12 @@
1278 1297
1279 1298 return $errors;
1280 1299 }
1281 1300
1282 - $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 + }
1283 1305
1284 1306 ob_start();
1285 1307 ?>
1286 1308
@@ -1331,9 +1353,9 @@
1331 1353 echo aui()->input(
1332 1354 array( // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped
1333 1355 'type' => 'tel',
1334 1356 'id' => 'authcode',
1335 - 'name' => 'wp-2fa-email-code',
1357 + 'name' => 'authcode',
1336 1358 'placeholder' => esc_attr__( 'Verification Code', 'userswp' ),
1337 1359 'value' => '',
1338 1360 'label' => esc_html__( 'Verification Code', 'userswp' ),
1339 1361 'extra_attributes' => array(
@@ -1368,9 +1390,9 @@
1368 1390 </form>
1369 1391 </div>
1370 1392
1371 1393 <?php
1372 - $codes_remaining = \WP2FA\Authenticator\Backup_Codes::codes_remaining_for_user( $user );
1394 + $codes_remaining = $this->get_wp2fa_backup_codes_remaining( $user );
1373 1395 if ( isset( $codes_remaining ) && $codes_remaining > 0 ) {
1374 1396 ?>
1375 1397 <div class="uwp-2fa-methods-wrap" style="display:none;">
1376 1398 <form name="validate_2fa_backup_codes_form" id="validate_2fa_backup_codes_form"
@@ -1426,9 +1448,247 @@
1426 1448
1427 1449 return ob_get_clean();
1428 1450 }
1429 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 +
1430 1688 public function process_login_2fa() {
1689 + global $wp2fa;
1690 +
1431 1691 if ( ! isset( $_POST['uwp-auth-id'], $_POST['wp-auth-nonce'] ) ) {
1432 1692 return;
1433 1693 }
1434 1694
@@ -1433,46 +1693,70 @@
1433 1693 }
1434 1694
1435 1695 $auth_id = (int) $_POST['uwp-auth-id'];
1436 1696 $user = get_userdata( $auth_id );
1697 +
1437 1698 if ( ! $user ) {
1438 1699 $message = aui()->alert(
1439 - array(
1700 + array(
1440 1701 'type' => 'error',
1441 1702 'content' => __( 'Invalid user data. Please try again.', 'userswp' ),
1442 - )
1703 + )
1443 1704 );
1444 1705
1445 1706 wp_send_json_error( array( 'message' => $message ) );
1446 1707 }
1447 1708
1448 - 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 + }
1449 1714
1715 + if ( 'wordfence' === $provider ) {
1716 + $this->process_login_wordfence_2fa( $user );
1717 +
1718 + return;
1719 + }
1720 +
1450 1721 $nonce = ( isset( $_POST['wp-auth-nonce'] ) ) ? sanitize_textarea_field( wp_unslash( $_POST['wp-auth-nonce'] ) ) : '';
1722 +
1451 1723 if ( true !== \WP2FA\Authenticator\Login::verify_login_nonce( $user->ID, $nonce ) ) {
1452 -
1453 1724 $message = aui()->alert(
1454 - array(
1725 + array(
1455 1726 'type' => 'error',
1456 1727 'content' => __( 'Invalid request! Please try again.', 'userswp' ),
1457 - )
1728 + )
1458 1729 );
1459 1730
1460 1731 wp_send_json_error( array( 'message' => $message ) );
1461 1732 }
1462 1733
1463 - if ( isset( $_POST['provider'] ) ) {
1464 - $provider = sanitize_textarea_field( wp_unslash( $_POST['provider'] ) );
1465 - $providers = \WP2FA\Authenticator\Login::get_available_providers_for_user( $user );
1466 - if ( isset( $providers[ $provider ] ) ) {
1467 - $provider = $providers[ $provider ];
1468 - } elseif ( isset( $provider ) ) {
1469 - $provider = $provider;
1470 - } else {
1471 - $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' );
1472 1741 }
1742 + } catch ( \Exception $e ) {
1743 + $error = $e->getMessage();
1473 1744 }
1474 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 +
1475 1759 // If this is an email login, or if the user failed validation previously, lets send the code to the user.
1476 1760 if ( 'email' === $provider && true !== \WP2FA\Authenticator\Login::pre_process_email_authentication( $user ) ) {
1477 1761
1478 1762 }
@@ -1477,17 +1761,16 @@
1477 1761
1478 1762 }
1479 1763
1480 1764 // Validate TOTP.
1481 - if ( 'totp' === $provider && true !== \WP2FA\Authenticator\Login::validate_totp_authentication( $user ) ) {
1482 -
1765 + if ( 'totp' === $provider && true !== $this->validate_wp2fa_totp_authentication( $user ) ) {
1483 1766 do_action( 'wp_login_failed', $user->user_login );
1484 1767
1485 1768 $message = aui()->alert(
1486 - array(
1769 + array(
1487 1770 'type' => 'error',
1488 1771 'content' => __( 'Invalid verification code.', 'userswp' ),
1489 - )
1772 + )
1490 1773 );
1491 1774
1492 1775 wp_send_json_error( array( 'message' => $message ) );
1493 1776 }
@@ -1492,27 +1775,26 @@
1492 1775 wp_send_json_error( array( 'message' => $message ) );
1493 1776 }
1494 1777
1495 1778 // Validate Email.
1496 - if ( 'email' === $provider && true !== \WP2FA\Authenticator\Login::validate_email_authentication( $user ) ) {
1497 -
1779 + if ( 'email' === $provider && true !== $this->validate_wp2fa_email_authentication( $user ) ) {
1498 1780 do_action( 'wp_login_failed', $user->user_login );
1499 1781
1500 1782 if ( isset( $_REQUEST['wp-2fa-email-code-resend'] ) && 1 == $_REQUEST['wp-2fa-email-code-resend'] ) {
1501 1783 $message = aui()->alert(
1502 - array(
1784 + array(
1503 1785 'type' => 'info',
1504 1786 'content' => __( 'A new code has been sent.', 'userswp' ),
1505 - )
1787 + )
1506 1788 );
1507 1789
1508 1790 wp_send_json_error( array( 'message' => $message ) );
1509 1791 } else {
1510 1792 $message = aui()->alert(
1511 - array(
1793 + array(
1512 1794 'type' => 'error',
1513 1795 'content' => __( 'Invalid verification code.', 'userswp' ),
1514 - )
1796 + )
1515 1797 );
1516 1798
1517 1799 wp_send_json_error( array( 'message' => $message ) );
1518 1800 }
@@ -1518,17 +1800,16 @@
1518 1800 }
1519 1801 }
1520 1802
1521 1803 // Backup Codes.
1522 - if ( 'backup_codes' === $provider && true !== \WP2FA\Authenticator\Login::validate_backup_codes( $user ) ) {
1523 -
1804 + if ( 'backup_codes' === $provider && true !== $this->validate_wp2fa_backup_codes( $user ) ) {
1524 1805 do_action( 'wp_login_failed', $user->user_login );
1525 1806
1526 1807 $message = aui()->alert(
1527 - array(
1808 + array(
1528 1809 'type' => 'error',
1529 1810 'content' => __( 'Invalid backup code.', 'userswp' ),
1530 - )
1811 + )
1531 1812 );
1532 1813
1533 1814 wp_send_json_error( array( 'message' => $message ) );
1534 1815 }
@@ -1536,8 +1817,9 @@
1536 1817 \WP2FA\Authenticator\Login::delete_login_nonce( $user->ID );
1537 1818
1538 1819 $rememberme = false;
1539 1820 $remember = ( isset( $_REQUEST['rememberme'] ) ) ? filter_var( $_REQUEST['rememberme'], FILTER_VALIDATE_BOOLEAN ) : '';
1821 +
1540 1822 if ( ! empty( $remember ) ) {
1541 1823 $rememberme = true;
1542 1824 }
1543 1825
@@ -1544,13 +1826,17 @@
1544 1826 wp_set_auth_cookie( $user->ID, $rememberme );
1545 1827
1546 1828 do_action( 'two_factor_user_authenticated', $user );
1547 1829
1830 + if ( defined( 'WP_2FA_PREFIX' ) ) {
1831 + do_action( WP_2FA_PREFIX . 'user_authenticated', $user );
1832 + }
1833 +
1548 1834 $message = aui()->alert(
1549 - array(
1835 + array(
1550 1836 'type' => 'success',
1551 1837 'content' => __( 'Validation successful. Redirecting...', 'userswp' ),
1552 - )
1838 + )
1553 1839 );
1554 1840
1555 1841 wp_send_json_success( array( 'message' => $message ) );
1556 1842 }
@@ -1647,9 +1933,12 @@
1647 1933 }
1648 1934
1649 1935 do_action( 'uwp_after_validate', $result, 'forgot', $data );
1650 1936
1651 - $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 );
1652 1941
1653 1942 // if no user we fake it and bail
1654 1943 if ( ! $user_data ) {
1655 1944 $args = apply_filters(
@@ -1655,9 +1944,9 @@
1655 1944 $args = apply_filters(
1656 1945 'uwp_forgot_error_message',
1657 1946 array(
1658 1947 'type' => 'error',
1659 - 'content' => __( 'Invalid email or user doesn\'t exists.', 'userswp' ),
1948 + 'content' => __( 'Invalid username/email or user doesn\'t exist.', 'userswp' ),
1660 1949 )
1661 1950 );
1662 1951
1663 1952 $message = aui()->alert( $args );
@@ -1707,9 +1996,8 @@
1707 1996 }
1708 1997
1709 1998 $as_password = apply_filters( 'uwp_forgot_message_as_password', false );
1710 1999
1711 - global $wpdb, $wp_hasher;
1712 2000 $reset_link = '';
1713 2001
1714 2002 if ( $as_password ) {
1715 2003 $new_pass = wp_generate_password( 12, false );
@@ -1721,17 +2009,21 @@
1721 2009 $message .= '<p>' . sprintf( __( 'Username: %s', 'userswp' ), $user_data->user_login ) . '</p>';
1722 2010 $message .= '<p>' . sprintf( __( 'Password: %s', 'userswp' ), $new_pass ) . '</p>';
1723 2011
1724 2012 } else {
1725 - $key = wp_generate_password( 20, false );
1726 - 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 );
1727 2016
1728 - if ( empty( $wp_hasher ) ) {
1729 - require_once ABSPATH . 'wp-includes/class-phpass.php';
1730 - $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 + }
1731 2024 }
1732 - $hashed = $wp_hasher->HashPassword( $key );
1733 - $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 +
1734 2026 $message = '<p>' . __( 'You have requested to reset your password for the following account:', 'userswp' ) . '</p>';
1735 2027 $message .= home_url( '/' ) . '</p>';
1736 2028 $message .= '<p>' . sprintf( __( 'Username: %s', 'userswp' ), $user_data->user_login ) . '</p>';
1737 2029 $message .= '<p>' . __( 'If this was by mistake, just ignore this email and nothing will happen.', 'userswp' ) . '</p>';
@@ -2024,8 +2316,21 @@
2024 2316 unset( $uploads_result[ $upload_file_key ] );
2025 2317 }
2026 2318 }
2027 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 +
2028 2333 $result = array_merge( $result, $uploads_result );
2029 2334
2030 2335 $args = array(
2031 2336 'ID' => get_current_user_id(),
@@ -2313,22 +2618,35 @@
2313 2618 $value = uwp_get_usermeta( $user_id, $htmlvar );
2314 2619
2315 2620 uwp_update_usermeta( $user_id, $htmlvar, '' );
2316 2621
2317 - if ( $value ) {
2622 + if ( $value && validate_file( $value ) === 0 ) {
2318 2623 $uploads = wp_upload_dir();
2319 2624 $upload_path = $uploads['basedir'];
2320 - $unlink_file = untrailingslashit( $upload_path ) . '/' . ltrim( $value, '/' );
2321 2625
2322 - if ( is_file( $unlink_file ) && file_exists( $unlink_file ) ) {
2323 - @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 + }
2324 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 +
2325 2641 // For avatar/banner, also remove the original (non-thumb) file.
2326 2642 if ( $type ) {
2327 - $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 );
2328 2645
2329 - if ( is_file( $unlink_ori_file ) && file_exists( $unlink_ori_file ) ) {
2330 - @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 );
2331 2649 }
2332 2650 }
2333 2651 }
2334 2652 }
@@ -3811,17 +4129,26 @@
3811 4129 $site_title = uwp_get_form_label( $field );
3812 4130 $required_msg = ( ! empty( $field->is_required ) && $field->required_msg != '') ? __( stripslashes( $field->required_msg ), 'userswp' ) : '';
3813 4131 $validation_text = ! empty( $field->validation_msg ) ? __( stripslashes( $field->validation_msg ), 'userswp' ) : '';
3814 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 +
3815 4142 if ( $design_style ) {
3816 4143 $required = ! empty( $field->is_required ) ? ' <span class="text-danger">*</span>' : '';
3817 4144
3818 4145 echo aui()->input(
3819 4146 array( // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped
3820 - 'type' => 'email',
4147 + 'type' => $input_type,
3821 4148 'id' => esc_attr( $field->htmlvar_name ),
3822 4149 'name' => esc_attr( $field->htmlvar_name ),
3823 - 'placeholder' => esc_attr( uwp_get_field_placeholder( $field ) ),
4150 + 'placeholder' => esc_attr( $placeholder ),
3824 4151 'title' => esc_html( $site_title ),
3825 4152 'value' => esc_attr( wp_unslash( $value ) ),
3826 4153 'required' => (bool) $field->is_required,
3827 4154 'validation_text' => $validation_text != '' ? esc_attr( $validation_text ) : esc_attr( $required_msg ),
@@ -3857,9 +4184,9 @@
3857 4184
3858 4185 <input name="<?php echo esc_attr( $field->htmlvar_name ); ?>"
3859 4186 class="<?php echo esc_attr( $field->css_class ); ?> uwp_textfield <?php echo esc_attr( $bs_form_control ); ?>"
3860 4187 id="<?php echo esc_attr( $field->htmlvar_name ); ?>"
3861 - placeholder="<?php echo esc_attr( uwp_get_field_placeholder( $field ) ); ?>"
4188 + placeholder="<?php echo esc_attr( $placeholder ); ?>"
3862 4189 value="<?php echo esc_attr( stripslashes( $value ) ); ?>"
3863 4190 title="<?php echo esc_attr( $site_title ); ?>"
3864 4191 <?php
3865 4192 if ( $field->is_required == 1 ) {
@@ -3865,9 +4192,9 @@
3865 4192 if ( $field->is_required == 1 ) {
3866 4193 echo 'required="required"';
3867 4194 }
3868 4195 ?>
3869 - type="email"
4196 + type="<?php echo esc_attr( $input_type ); ?>"
3870 4197 />
3871 4198 <span class="uwp_message_note"><?php echo wp_kses_post( uwp_get_field_description( $field ) ); ?></span>
3872 4199 <?php if ( $field->is_required ) { ?>
3873 4200 <span class="uwp_message_error invalid-feedback"><?php echo esc_html__( stripslashes( $field->required_msg ), 'userswp' ); ?></span>