| @@ -55,8 +55,24 @@ | ||
| 55 | 55 | } |
| 56 | 56 | } |
| 57 | 57 | |
| 58 | 58 | |
| 59 | +/** | |
| 60 | + * Whether Profile Builder should load style-block-themes-front-end.css. | |
| 61 | + */ | |
| 62 | +function wppb_should_load_block_theme_stylesheet() { | |
| 63 | + $is_block_theme_context = version_compare( get_bloginfo( 'version' ), '5.9', '>=' ) | |
| 64 | + && function_exists( 'wp_is_block_theme' ) | |
| 65 | + && wp_is_block_theme(); | |
| 66 | + | |
| 67 | + /** | |
| 68 | + * Filter whether to load the block theme front-end stylesheet. | |
| 69 | + * | |
| 70 | + * @param bool $load_block_theme_stylesheet True when WordPress is 5.9+ and the active theme is a block theme. | |
| 71 | + */ | |
| 72 | + return (bool) apply_filters( 'wppb_load_block_theme_stylesheet', $is_block_theme_context ); | |
| 73 | +} | |
| 74 | + | |
| 59 | 75 | function wppb_add_plugin_stylesheet() { |
| 60 | 76 | $wppb_generalSettings = get_option( 'wppb_general_settings' ); |
| 61 | 77 | |
| 62 | 78 | if ( ( file_exists( WPPB_PLUGIN_DIR . '/assets/css/style-front-end.css' ) ) && ( isset( $wppb_generalSettings['extraFieldsLayout'] ) && ( $wppb_generalSettings['extraFieldsLayout'] == 'default' ) ) ){ |
| @@ -70,19 +86,15 @@ | ||
| 70 | 86 | wp_enqueue_style( 'wppb_stylesheet_rtl' ); |
| 71 | 87 | } |
| 72 | 88 | } |
| 73 | 89 | |
| 74 | - // load stylesheet for the Default Form Style if the active WP Theme is a Block Theme (Block Themes were introduced in WordPress since the 5.9 release) | |
| 75 | - if ( version_compare( get_bloginfo( 'version' ), '5.9', '>=' ) && function_exists( 'wp_is_block_theme' ) && wp_is_block_theme() ) { | |
| 76 | - | |
| 90 | + if ( wppb_should_load_block_theme_stylesheet() ) { | |
| 77 | 91 | $active_design = function_exists( 'wppb_get_active_form_design' ) ? wppb_get_active_form_design() : 'form-style-default'; |
| 78 | 92 | |
| 79 | - // load stylesheet only if the active Form Design is the Default Style | |
| 80 | 93 | if ( $active_design === 'form-style-default' && file_exists( WPPB_PLUGIN_DIR . 'assets/css/style-block-themes-front-end.css' ) ) { |
| 81 | 94 | wp_register_style( 'wppb_block_themes_front_end_stylesheet', WPPB_PLUGIN_URL . 'assets/css/style-block-themes-front-end.css', array(), PROFILE_BUILDER_VERSION ); |
| 82 | 95 | wp_enqueue_style( 'wppb_block_themes_front_end_stylesheet' ); |
| 83 | 96 | } |
| 84 | - | |
| 85 | 97 | } |
| 86 | 98 | } |
| 87 | 99 | |
| 88 | 100 | |
| @@ -467,8 +479,9 @@ | ||
| 467 | 479 | |
| 468 | 480 | if ( ( 'wppb-epf-cpt' == $post_type ) || ( 'wppb-rf-cpt' == $post_type ) || ( 'wppb-ul-cpt' == $post_type ) ){ |
| 469 | 481 | wp_enqueue_style( 'wppb-back-end-style', WPPB_PLUGIN_URL . 'assets/css/style-back-end.css', false, PROFILE_BUILDER_VERSION ); |
| 470 | 482 | wp_enqueue_script( 'wppb-epf-rf', WPPB_PLUGIN_URL . 'assets/js/jquery-epf-rf.js', array(), PROFILE_BUILDER_VERSION, true ); |
| 483 | + wp_localize_script( 'wppb-epf-rf', 'wppbEpfRf', array( 'nonce' => wp_create_nonce( 'wppb-epf-rf-id-change' ) ) ); | |
| 471 | 484 | } |
| 472 | 485 | else if( 'wppb-roles-editor' == $post_type ){ |
| 473 | 486 | wp_enqueue_style( 'wppb-back-end-style', WPPB_PLUGIN_URL . 'assets/css/style-back-end.css', array(), PROFILE_BUILDER_VERSION ); |
| 474 | 487 | } |
| @@ -827,8 +840,22 @@ | ||
| 827 | 840 | return apply_filters( 'wppb_user_meta_exists_meta_name', $wpdb->get_row( $wpdb->prepare( "SELECT * FROM $wpdb->usermeta WHERE user_id = %d AND meta_key = %s", $id, $meta_name ) ), $id, $meta_name ); |
| 828 | 841 | } |
| 829 | 842 | |
| 830 | 843 | |
| 844 | +/** | |
| 845 | + * Sanitize a URL from request input. Non-string values (e.g. arrays from bracket notation) return ''. | |
| 846 | + * | |
| 847 | + * @param mixed $url Candidate URL. | |
| 848 | + * @return string | |
| 849 | + */ | |
| 850 | +function wppb_sanitize_request_url( $url ) { | |
| 851 | + if ( ! is_string( $url ) || $url === '' ) { | |
| 852 | + return ''; | |
| 853 | + } | |
| 854 | + | |
| 855 | + return esc_url_raw( wp_unslash( $url ) ); | |
| 856 | +} | |
| 857 | + | |
| 831 | 858 | // function to check if there is a need to add the http:// prefix |
| 832 | 859 | function wppb_check_missing_http( $redirectLink ) { |
| 833 | 860 | return preg_match( '#^(?:[a-z\d]+(?:-+[a-z\d]+)*\.)+[a-z]+(?::\d+)?(?:/|$)#i', $redirectLink ); |
| 834 | 861 | } |
| @@ -1108,8 +1135,52 @@ | ||
| 1108 | 1135 | return ''; |
| 1109 | 1136 | } |
| 1110 | 1137 | |
| 1111 | 1138 | |
| 1139 | +/** | |
| 1140 | + * Returns the AJAX actions that check the login/checkout credentials before the real form submission is sent. | |
| 1141 | + * | |
| 1142 | + * Those requests run the whole authentication stack, so our CAPTCHA check runs too and the token gets spent | |
| 1143 | + * with the CAPTCHA provider, but they never log the user in - the browser still submits the form afterwards | |
| 1144 | + * with the very same token. CAPTCHA tokens are single use, so verifying one a second time comes back as a | |
| 1145 | + * duplicate and the login fails. For these actions we remember the successful verification and reuse it once, | |
| 1146 | + * when the actual form submission arrives. | |
| 1147 | + */ | |
| 1148 | +function wppb_get_captcha_prevalidation_actions() { | |
| 1149 | + return apply_filters( 'wppb_captcha_prevalidation_actions', array( | |
| 1150 | + 'pms_validate_checkout', // Paid Member Subscriptions checkout validation | |
| 1151 | + 'wordfence_ls_authenticate', // Wordfence Login Security login pre-flight, used to decide if it needs to ask for a 2FA code | |
| 1152 | + ) ); | |
| 1153 | +} | |
| 1154 | + | |
| 1155 | +/* Whether the current request is one of the CAPTCHA pre-validation AJAX calls above */ | |
| 1156 | +function wppb_is_captcha_prevalidation_request() { | |
| 1157 | + if ( ! wp_doing_ajax() || empty( $_POST['action'] ) || ! is_string( $_POST['action'] ) ) /* phpcs:ignore WordPress.Security.NonceVerification.Missing */ | |
| 1158 | + return false; | |
| 1159 | + | |
| 1160 | + return in_array( sanitize_text_field( $_POST['action'] ), wppb_get_captcha_prevalidation_actions(), true ); /* phpcs:ignore WordPress.Security.NonceVerification.Missing */ | |
| 1161 | +} | |
| 1162 | + | |
| 1163 | +/** | |
| 1164 | + * Drops pre-validated CAPTCHA tokens that were never claimed by a form submission. | |
| 1165 | + * | |
| 1166 | + * A pre-validation that is not followed by a submission (wrong password, abandoned login, bots) leaves its | |
| 1167 | + * entry behind, so without this the option would keep growing on sites where every login is pre-validated. | |
| 1168 | + */ | |
| 1169 | +function wppb_prune_captcha_prevalidations( $saved ) { | |
| 1170 | + if ( ! is_array( $saved ) ) | |
| 1171 | + return array(); | |
| 1172 | + | |
| 1173 | + $lifetime = apply_filters( 'wppb_captcha_prevalidation_lifetime', 15 * MINUTE_IN_SECONDS ); | |
| 1174 | + | |
| 1175 | + foreach ( $saved as $token => $validated_at ) { | |
| 1176 | + if ( ! is_int( $validated_at ) || ( time() - $validated_at ) > $lifetime ) | |
| 1177 | + unset( $saved[ $token ] ); | |
| 1178 | + } | |
| 1179 | + | |
| 1180 | + return $saved; | |
| 1181 | +} | |
| 1182 | + | |
| 1112 | 1183 | /* Function for displaying reCAPTCHA error on Login and Recover Password forms */ |
| 1113 | 1184 | function wppb_recaptcha_field_error($field_title='') { |
| 1114 | 1185 | |
| 1115 | 1186 | $recaptcha_field = wppb_get_recaptcha_field(); |
| @@ -1457,8 +1528,87 @@ | ||
| 1457 | 1528 | $redirect_url = ( wppb_check_missing_http( $redirect_url ) ? wppb_add_missing_http( $redirect_url ) : $redirect_url ); |
| 1458 | 1529 | } |
| 1459 | 1530 | |
| 1460 | 1531 | return $redirect_url; |
| 1532 | +} | |
| 1533 | + | |
| 1534 | +/** | |
| 1535 | + * Bind an autologin nonce to a user ID server-side (one-time use). | |
| 1536 | + * | |
| 1537 | + * @param int $user_id User ID to log in. | |
| 1538 | + * @param string $nonce Autologin nonce. | |
| 1539 | + */ | |
| 1540 | +function wppb_store_autologin_user( $user_id, $nonce ) { | |
| 1541 | + $user_id = absint( $user_id ); | |
| 1542 | + | |
| 1543 | + if ( ! $user_id || empty( $nonce ) ) { | |
| 1544 | + return; | |
| 1545 | + } | |
| 1546 | + | |
| 1547 | + set_transient( 'wppb_autologin_' . md5( $nonce ), $user_id, 2 * MINUTE_IN_SECONDS ); | |
| 1548 | +} | |
| 1549 | + | |
| 1550 | +/** | |
| 1551 | + * Resolve the user bound to an autologin nonce. | |
| 1552 | + * | |
| 1553 | + * @param string $nonce Autologin nonce. | |
| 1554 | + * @param bool $consume Whether to delete the stored mapping. | |
| 1555 | + * | |
| 1556 | + * @return int User ID, or 0 when not found. | |
| 1557 | + */ | |
| 1558 | +function wppb_get_autologin_user_id( $nonce, $consume = true ) { | |
| 1559 | + if ( empty( $nonce ) ) { | |
| 1560 | + return 0; | |
| 1561 | + } | |
| 1562 | + | |
| 1563 | + $key = 'wppb_autologin_' . md5( $nonce ); | |
| 1564 | + $user_id = absint( get_transient( $key ) ); | |
| 1565 | + | |
| 1566 | + if ( $user_id && $consume ) { | |
| 1567 | + delete_transient( $key ); | |
| 1568 | + } | |
| 1569 | + | |
| 1570 | + return $user_id; | |
| 1571 | +} | |
| 1572 | + | |
| 1573 | +/** | |
| 1574 | + * Build autologin query args for a user. | |
| 1575 | + * | |
| 1576 | + * @param int $user_id User ID to log in. | |
| 1577 | + * | |
| 1578 | + * @return array Query args for add_query_arg(). | |
| 1579 | + */ | |
| 1580 | +function wppb_get_autologin_query_args( $user_id ) { | |
| 1581 | + $user_id = absint( $user_id ); | |
| 1582 | + $nonce = wp_create_nonce( 'autologin-' . $user_id . '-' . (int) ( time() / 60 ) ); | |
| 1583 | + | |
| 1584 | + wppb_store_autologin_user( $user_id, $nonce ); | |
| 1585 | + | |
| 1586 | + return array( | |
| 1587 | + 'autologin' => 'true', | |
| 1588 | + '_wpnonce' => $nonce, | |
| 1589 | + ); | |
| 1590 | +} | |
| 1591 | + | |
| 1592 | +/** | |
| 1593 | + * Verify an autologin nonce for the given user ID. | |
| 1594 | + * | |
| 1595 | + * @param string $nonce Autologin nonce. | |
| 1596 | + * @param int $user_id User ID bound to the nonce. | |
| 1597 | + * | |
| 1598 | + * @return bool | |
| 1599 | + */ | |
| 1600 | +function wppb_verify_autologin_nonce( $nonce, $user_id ) { | |
| 1601 | + $user_id = absint( $user_id ); | |
| 1602 | + | |
| 1603 | + if ( ! $user_id || empty( $nonce ) ) { | |
| 1604 | + return false; | |
| 1605 | + } | |
| 1606 | + | |
| 1607 | + $nonce_action = 'autologin-' . $user_id . '-'; | |
| 1608 | + | |
| 1609 | + return wp_verify_nonce( $nonce, $nonce_action . (int) ( time() / 60 ) ) | |
| 1610 | + || wp_verify_nonce( $nonce, $nonce_action . (int) ( time() / 60 - 1 ) ); | |
| 1461 | 1611 | } |
| 1462 | 1612 | |
| 1463 | 1613 | /** |
| 1464 | 1614 | * Function that builds the redirect |