PluginProbe
Passster – Password Protect Pages and Content / 4.3.8
Passster – Password Protect Pages and Content v4.3.8
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 4.2.16 All 47 releases
content-protector / inc / class-ps-rest-handler.php

class-ps-rest-handler.php in Passster – Password Protect Pages and Content 4.3.8, at inc/class-ps-rest-handler.php

179 lines 5.5 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
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 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 ) {
39
40 // If a previous authentication check was applied,
41 // pass that result along without modification.
42 if ( true === $result || is_wp_error( $result ) ) {
43 return $result;
44 }
45
46 // Allow Passster public endpoints (unlock flow must work for guests).
47 $request_uri = isset( $_SERVER['REQUEST_URI'] ) ? sanitize_text_field( wp_unslash( $_SERVER['REQUEST_URI'] ) ) : '';
48 $request_path = (string) wp_parse_url( $request_uri, PHP_URL_PATH );
49 $rest_route_param = isset( $_GET['rest_route'] ) ? sanitize_text_field( wp_unslash( $_GET['rest_route'] ) ) : '';
50 $public_passster_paths = array(
51 '/passster/v1/nonces',
52 '/passster/v1/unlock',
53 '/passster/v1/hash',
54 '/passster/v1/captcha',
55 '/passster/v1/logout',
56 );
57 foreach ( $public_passster_paths as $path ) {
58 if ( strpos( $request_path, $path ) !== false || strpos( $rest_route_param, $path ) !== false ) {
59 return $result;
60 }
61 }
62
63 // Check if request is coming from a frontend page builder.
64 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' ) ) ) {
65 return $result;
66 }
67
68 // Global protection activated?
69 $settings = get_option( 'passster' );
70 $protection_enabled = $settings['activate_global_protection'] ?? false;
71
72 // Check if access is allowed.
73 $valid = false;
74
75 if ( ! empty( $settings['global_protection_id'] ) ) {
76 $page_id = esc_attr( $settings['global_protection_id'] );
77 $atts = array( 'password' => get_post_meta( $page_id, 'passster_password', true ) );
78 $valid = PS_Conditional::is_valid( $atts );
79 }
80
81 if ( $protection_enabled && ! $valid && ! current_user_can( 'manage_options' ) ) {
82 return new \WP_Error(
83 'rest_not_logged_in',
84 __( 'You are not allowed to access this content. Please authenticate with a password first.', 'content-protector' ),
85 array( 'status' => 401 )
86 );
87 }
88
89 return $result;
90 }
91
92 /**
93 * Filter REST API response to hide sensitive password data from unauthenticated users
94 *
95 * @param \WP_REST_Response $response The response object.
96 * @param \WP_Post $post The post object.
97 * @param \WP_REST_Request $request The request object.
98 * @return \WP_REST_Response
99 */
100 public function filter_rest_response( $response, $post, $request ) {
101 // Only filter for unauthenticated users or users without proper permissions
102 if ( is_user_logged_in() && current_user_can( 'manage_options' ) ) {
103 return $response;
104 }
105
106 // Get the response data
107 $data = $response->get_data();
108
109 // List of sensitive meta fields that should be hidden
110 $sensitive_fields = array(
111 'passster_password',
112 'passster_passwords',
113 'passster_password_list',
114 'passster_password_lists',
115 );
116
117 // Remove sensitive fields from meta if they exist
118 if ( isset( $data['meta'] ) && is_array( $data['meta'] ) ) {
119 foreach ( $sensitive_fields as $field ) {
120 if ( isset( $data['meta'][ $field ] ) ) {
121 unset( $data['meta'][ $field ] );
122 }
123 }
124 }
125
126 if ( $post && class_exists( 'passster\PS_Category_Lock' ) && PS_Category_Lock::get_instance()->get_active_category_lock( $post->ID ) ) {
127 if ( isset( $data['content']['rendered'] ) ) {
128 $data['content']['rendered'] = '';
129 }
130 if ( isset( $data['excerpt']['rendered'] ) ) {
131 $data['excerpt']['rendered'] = '';
132 }
133 }
134
135 // Update the response data
136 $response->set_data( $data );
137
138 return $response;
139 }
140
141 public function register_nonce_routes() {
142
143 register_rest_route(
144 'passster/v1',
145 '/nonces',
146 array(
147 'methods' => 'POST',
148 'callback' => array( $this, 'get_all_nonces' ),
149 'permission_callback' => '__return_true',
150 )
151 );
152 }
153 public function get_all_nonces() {
154 $user_id = wp_validate_auth_cookie( '', 'logged_in' );
155
156 if ( $user_id ) {
157 wp_set_current_user( $user_id );
158 }
159
160 $response = new \WP_REST_Response(
161 array(
162 'nonce' => wp_create_nonce( 'ps-password-nonce' ),
163 'hash_nonce' => wp_create_nonce( 'ps-hash-nonce' ),
164 'logout_nonce' => wp_create_nonce( 'ps-logout-nonce' ),
165 )
166 );
167
168 // Prevent caching of nonces - they are user-session specific.
169 $response->header( 'Cache-Control', 'no-store, no-cache, must-revalidate, max-age=0, private' );
170 $response->header( 'Pragma', 'no-cache' );
171 $response->header( 'Expires', '0' );
172 $response->header( 'X-LiteSpeed-Cache-Control', 'no-cache' );
173 $response->header( 'X-Accel-Expires', '0' ); // Nginx
174 $response->header( 'Surrogate-Control', 'no-store' ); // Varnish/CDN
175
176 return $response;
177 }
178 }
179