PluginProbe
Passster – Password Protect Pages and Content / 4.3.16
Passster – Password Protect Pages and Content v4.3.16
4.3.16 4.3.15 4.3.14 4.3.12 4.3.13 4.3.11 4.3.10 4.3.9 4.3.8 4.3.7 4.3.6 4.3.5 trunk 3.5.4 3.5.5.2 3.5.5.8 3.5.5.9 4.0 4.1.4 4.2.10 4.2.11 4.2.12 4.2.13 4.2.14 4.2.15 All 48 releases
← All changes | inc/class-ps-rest-handler.php +173 -71 4.2.144.3.16 View file →
@@ -1,71 +1,173 @@
1 -<?php
2 -
3 -namespace passster;
4 -
5 -class PS_Rest_Handler {
6 -
7 - /**
8 - * Contains instance or null
9 - *
10 - * @var object|null
11 - */
12 - private static $instance = null;
13 -
14 - /**
15 - * Returns instance of PS_Rest_Handler.
16 - *
17 - * @return object
18 - */
19 - public static function get_instance() {
20 -
21 - if ( null === self::$instance ) {
22 - self::$instance = new self();
23 - }
24 -
25 - return self::$instance;
26 - }
27 -
28 - /**
29 - * Constructor for PS_Rest_Handler
30 - */
31 - public function __construct() {
32 - add_filter( 'rest_authentication_errors', array( $this, 'restrict_rest_access' ) );
33 - }
34 -
35 - public function restrict_rest_access( $result ) {
36 -
37 - // If a previous authentication check was applied,
38 - // pass that result along without modification.
39 - if ( true === $result || is_wp_error( $result ) ) {
40 - return $result;
41 - }
42 -
43 - // Check if request is coming from a frontend page builder.
44 - if ( current_user_can( 'administrator' ) && ( is_plugin_active( 'elementor/elementor.php' ) || is_plugin_active( 'livecanvas/livecanvas-plugin-index.php' ) || is_plugin_active( 'divi-builder/divi-builder.php' ) || is_plugin_active( 'oxygen/functions.php' ) || is_plugin_active( 'pagelayer/pagelayer.php' ) ) ) {
45 - return $result;
46 - }
47 -
48 - // Global protection activated?
49 - $settings = get_option( 'passster' );
50 - $protection_enabled = $settings['activate_global_protection'] ?? false;
51 -
52 - // Check if access is allowed.
53 - $valid = false;
54 -
55 - if ( ! empty( $settings['global_protection_id'] ) ) {
56 - $page_id = esc_attr( $settings['global_protection_id'] );
57 - $atts = array( 'password' => get_post_meta( $page_id, 'passster_password', true ) );
58 - $valid = PS_Conditional::is_valid( $atts );
59 - }
60 -
61 - if ( $protection_enabled && ! $valid && ! is_user_logged_in() ) {
62 - return new \WP_Error(
63 - 'rest_not_logged_in',
64 - __( 'You are not allowed to access this content. Please authenticate with a password first.' ),
65 - array( 'status' => 401 )
66 - );
67 - }
68 -
69 - return $result;
70 - }
71 -}
1 +<?php
2 +
3 +namespace passster;
4 +
5 +class PS_Rest_Handler {
6 +
7 + /**
8 + * Contains instance or null
9 + *
10 + * @var object|null
11 + */
12 + private static $instance = null;
13 +
14 + /**
15 + * Returns instance of PS_Rest_Handler.
16 + *
17 + * @return object
18 + */
19 + public static function get_instance() {
20 +
21 + if ( null === self::$instance ) {
22 + self::$instance = new self();
23 + }
24 +
25 + return self::$instance;
26 + }
27 +
28 + /**
29 + * Constructor for PS_Rest_Handler
30 + */
31 + public function __construct() {
32 + add_filter( 'rest_pre_dispatch', array( $this, 'restrict_rest_access' ), 10, 3 );
33 + add_filter( 'rest_prepare_post', array( $this, 'filter_rest_response' ), 10, 3 );
34 +
35 + add_action( 'rest_api_init', array( $this, 'register_nonce_routes' ) );
36 + }
37 +
38 + public function restrict_rest_access( $result, $server, $request ) {
39 +
40 + // If a previous plugin already short-circuited dispatch,
41 + // pass that result along without modification.
42 + if ( null !== $result ) {
43 + return $result;
44 + }
45 +
46 + // Allow Passster public endpoints (unlock flow must work for guests).
47 + $public_passster_paths = array(
48 + '/passster/v1/nonces',
49 + '/passster/v1/unlock',
50 + '/passster/v1/hash',
51 + '/passster/v1/captcha',
52 + '/passster/v1/logout',
53 + );
54 + if ( in_array( $request->get_route(), $public_passster_paths, true ) ) {
55 + return null;
56 + }
57 +
58 + // Check if request is coming from a frontend page builder.
59 + if ( current_user_can( 'manage_options' ) && ( is_plugin_active( 'elementor/elementor.php' ) || is_plugin_active( 'livecanvas/livecanvas-plugin-index.php' ) || is_plugin_active( 'divi-builder/divi-builder.php' ) || is_plugin_active( 'oxygen/functions.php' ) || is_plugin_active( 'pagelayer/pagelayer.php' ) ) ) {
60 + return null;
61 + }
62 +
63 + // Global protection activated?
64 + $settings = get_option( 'passster' );
65 + $protection_enabled = $settings['activate_global_protection'] ?? false;
66 +
67 + // Check if access is allowed.
68 + $valid = false;
69 +
70 + if ( ! empty( $settings['global_protection_id'] ) ) {
71 + $page_id = esc_attr( $settings['global_protection_id'] );
72 + $atts = array( 'password' => get_post_meta( $page_id, 'passster_password', true ) );
73 + $valid = PS_Conditional::is_valid( $atts );
74 + }
75 +
76 + if ( $protection_enabled && ! $valid && ! current_user_can( 'manage_options' ) ) {
77 + return new \WP_Error(
78 + 'rest_not_logged_in',
79 + __( 'You are not allowed to access this content. Please authenticate with a password first.', 'content-protector' ),
80 + array( 'status' => 401 )
81 + );
82 + }
83 +
84 + return null;
85 + }
86 +
87 + /**
88 + * Filter REST API response to hide sensitive password data from unauthenticated users
89 + *
90 + * @param \WP_REST_Response $response The response object.
91 + * @param \WP_Post $post The post object.
92 + * @param \WP_REST_Request $request The request object.
93 + * @return \WP_REST_Response
94 + */
95 + public function filter_rest_response( $response, $post, $request ) {
96 + // Only filter for unauthenticated users or users without proper permissions
97 + if ( is_user_logged_in() && current_user_can( 'manage_options' ) ) {
98 + return $response;
99 + }
100 +
101 + // Get the response data
102 + $data = $response->get_data();
103 +
104 + // List of sensitive meta fields that should be hidden
105 + $sensitive_fields = array(
106 + 'passster_password',
107 + 'passster_passwords',
108 + 'passster_password_list',
109 + 'passster_password_lists',
110 + );
111 +
112 + // Remove sensitive fields from meta if they exist
113 + if ( isset( $data['meta'] ) && is_array( $data['meta'] ) ) {
114 + foreach ( $sensitive_fields as $field ) {
115 + if ( isset( $data['meta'][ $field ] ) ) {
116 + unset( $data['meta'][ $field ] );
117 + }
118 + }
119 + }
120 +
121 + if ( $post && class_exists( 'passster\PS_Category_Lock' ) && PS_Category_Lock::get_instance()->get_active_category_lock( $post->ID ) ) {
122 + if ( isset( $data['content']['rendered'] ) ) {
123 + $data['content']['rendered'] = '';
124 + }
125 + if ( isset( $data['excerpt']['rendered'] ) ) {
126 + $data['excerpt']['rendered'] = '';
127 + }
128 + }
129 +
130 + // Update the response data
131 + $response->set_data( $data );
132 +
133 + return $response;
134 + }
135 +
136 + public function register_nonce_routes() {
137 +
138 + register_rest_route(
139 + 'passster/v1',
140 + '/nonces',
141 + array(
142 + 'methods' => 'POST',
143 + 'callback' => array( $this, 'get_all_nonces' ),
144 + 'permission_callback' => '__return_true',
145 + )
146 + );
147 + }
148 + public function get_all_nonces() {
149 + $user_id = wp_validate_auth_cookie( '', 'logged_in' );
150 +
151 + if ( $user_id ) {
152 + wp_set_current_user( $user_id );
153 + }
154 +
155 + $response = new \WP_REST_Response(
156 + array(
157 + 'nonce' => wp_create_nonce( 'ps-password-nonce' ),
158 + 'hash_nonce' => wp_create_nonce( 'ps-hash-nonce' ),
159 + 'logout_nonce' => wp_create_nonce( 'ps-logout-nonce' ),
160 + )
161 + );
162 +
163 + // Prevent caching of nonces - they are user-session specific.
164 + $response->header( 'Cache-Control', 'no-store, no-cache, must-revalidate, max-age=0, private' );
165 + $response->header( 'Pragma', 'no-cache' );
166 + $response->header( 'Expires', '0' );
167 + $response->header( 'X-LiteSpeed-Cache-Control', 'no-cache' );
168 + $response->header( 'X-Accel-Expires', '0' ); // Nginx
169 + $response->header( 'Surrogate-Control', 'no-store' ); // Varnish/CDN
170 +
171 + return $response;
172 + }
173 +}