PluginProbe
s2Member – Excellent for All Kinds of Memberships, Content Restriction Paywalls & Member Access Subscriptions / 261011
s2Member – Excellent for All Kinds of Memberships, Content Restriction Paywalls & Member Access Subscriptions v261011
261011 261001 260927 260917 260913 260909 260829 260814 260805 110710 110731 110812 110815 110912 110913 110915 110926 110927 111002 111003 111011 111017 111029 111105 111206 All 192 releases
s2member / src / includes / classes / security-rest.inc.php

security-rest.inc.php in s2Member – Excellent for All Kinds of Memberships, Content Restriction Paywalls & Member Access Subscriptions 261011, at src/includes/classes/security-rest.inc.php

622 lines 28.7 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 <?php
2 // @codingStandardsIgnoreFile
3 /**
4 * s2Member's Security Gate for REST requests.
5 *
6 * Copyright: © 2009-2024
7 * {@link https://wpsharks.com/ WP Sharks}
8 * (coded in the Switzerland)
9 *
10 * Released under the terms of the GNU General Public License.
11 * You should have received a copy of the GNU General Public License,
12 * along with this software. In the main directory, see: /licensing/
13 * If not, see: {@link http://www.gnu.org/licenses/}.
14 *
15 * @package s2Member\Security
16 * @since 240312
17 */
18 if(!defined('WPINC')) // MUST have WordPress.
19 exit('Do not access this file directly.');
20
21 if(!class_exists('c_ws_plugin__s2member_security_rest'))
22 {
23 /**
24 * s2Member's Security Gate for REST requests.
25 *
26 * @package s2Member\Security
27 * @since 240312
28 */
29 class c_ws_plugin__s2member_security_rest
30 {
31 /**
32 * Protects the resource selected by WordPress after route matching and validation.
33 *
34 * @package s2Member\Security
35 * @since 240312
36 *
37 * @attaches-to ``add_filter('rest_request_before_callbacks');``
38 *
39 * @param mixed $response Original response from earlier filters.
40 * @param array $handler Matched WordPress REST endpoint handler.
41 * @param WP_REST_Request $request Validated REST request.
42 *
43 * @return mixed Original response, or a membership WP_Error with HTTP 403.
44 */
45 public static function security_gate($response, $handler, $request)
46 {
47 if(is_wp_error($response) || empty($handler['callback']) || !is_array($handler['callback']))
48 return $response;
49
50 //261005.2320 Use the matched controller and validated ID; route segments and query IDs can describe different resources.
51 $controller = $handler['callback'][0];
52 $id = (int)$request->get_param('id');
53 if(!$id)
54 return $response;
55
56 if($controller instanceof WP_REST_Posts_Controller)
57 {
58 $post = get_post($id);
59 $type = $post ? get_post_type_object($post->post_type) : NULL;
60 if($type && self::controller_owns_resource($type, $controller, $request))
61 return self::check_post_access($post, $response);
62 }
63 else if($controller instanceof WP_REST_Terms_Controller)
64 {
65 $term = get_term($id);
66 $taxonomy = $term && !is_wp_error($term) ? get_taxonomy($term->taxonomy) : NULL;
67 if($taxonomy && self::controller_owns_resource($taxonomy, $controller, $request))
68 return self::check_resource_access($term->taxonomy === 'category' ? 'categories' : ($term->taxonomy === 'post_tag' ? 'tags' : 'terms'), $id, $response);
69 }
70 else if($controller instanceof WP_REST_Comments_Controller && ($comment = get_comment($id)) && ($post = get_post($comment->comment_post_ID)))
71 return self::check_post_access($post, $response);
72
73 return $response;
74 }
75
76 /**
77 * Determines whether the matched REST controller owns the requested resource.
78 *
79 * @package s2Member\Security
80 * @since 261005
81 *
82 * @param WP_Post_Type|WP_Taxonomy $resource Registered post type or taxonomy.
83 * @param WP_REST_Controller $controller Matched endpoint controller.
84 * @param WP_REST_Request $request Validated REST request.
85 *
86 * @return bool True if the controller owns the resource; false otherwise.
87 */
88 private static function controller_owns_resource($resource, $controller, $request)
89 {
90 if(method_exists($resource, 'get_rest_controller'))
91 return $resource->get_rest_controller() === $controller;
92 //261005.2335 Older REST-enabled WordPress releases expose the registered base but lack these accessors.
93 if(empty($resource->show_in_rest))
94 return FALSE;
95 $namespace = !empty($resource->rest_namespace) ? $resource->rest_namespace : 'wp/v2';
96 $base = !empty($resource->rest_base) ? $resource->rest_base : $resource->name;
97 return (bool)preg_match('/^'.preg_quote('/'.$namespace.'/'.$base, '/').'\/\d+\/?$/', $request->get_route());
98 }
99
100 /**
101 * Checks REST access to a post, page, or comment parent.
102 *
103 * @package s2Member\Security
104 * @since 261005
105 *
106 * @param WP_Post $post Existing post whose membership restrictions are checked.
107 * @param mixed $response Original response to preserve when access is allowed.
108 *
109 * @return mixed Original response, or a membership WP_Error with HTTP 403.
110 */
111 private static function check_post_access($post, $response = NULL)
112 {
113 $type = $post->post_type === 'page' ? 'pages' : 'posts';
114 return self::check_resource_access($type, (int)$post->ID, $response, $post->post_type);
115 }
116
117 /**
118 * Registers REST collection and search access filters for exposed resources.
119 *
120 * @package s2Member\Security
121 * @since 261005
122 *
123 * @attaches-to ``add_action('rest_api_init');``
124 *
125 * @return void
126 */
127 public static function register_collection_filters()
128 {
129 foreach(get_post_types(array('show_in_rest' => TRUE), 'names') as $post_type)
130 add_filter('rest_'.$post_type.'_query', array(__CLASS__, 'filter_post_query'), PHP_INT_MAX, 2);
131 foreach(get_taxonomies(array('show_in_rest' => TRUE), 'names') as $taxonomy)
132 add_filter('rest_'.$taxonomy.'_query', array(__CLASS__, 'filter_term_query'), PHP_INT_MAX, 2);
133 add_filter('rest_post_search_query', array(__CLASS__, 'filter_post_query'), PHP_INT_MAX, 2);
134 add_filter('rest_term_search_query', array(__CLASS__, 'filter_term_query'), PHP_INT_MAX, 2);
135 add_filter('rest_comment_query', array(__CLASS__, 'filter_comment_query'), PHP_INT_MAX, 2);
136 }
137
138 /**
139 * Determines whether configured restrictions require a REST collection access check.
140 *
141 * @package s2Member\Security
142 * @since 261005
143 *
144 * @param bool $posts Include post/page, Specific Post/Page, and Custom Capability rules?
145 *
146 * @return bool True if an applicable restriction may deny access to collection items.
147 */
148 private static function needs_query_check($posts = FALSE)
149 {
150 $o = $GLOBALS['WS_PLUGIN__']['s2member']['o'];
151 if(empty($o['membership_options_page']))
152 return FALSE;
153 $user = wp_get_current_user();
154 for($n = $GLOBALS['WS_PLUGIN__']['s2member']['c']['levels']; $n >= 0; $n--)
155 foreach($posts ? array('posts', 'pages', 'catgs', 'ptags', 'ruris') : array('catgs', 'ptags', 'ruris') as $kind)
156 if(!empty($o['level'.$n.'_'.$kind]) && !$user->has_cap('access_s2member_level'.$n))
157 return TRUE;
158 if(!empty($o['login_redirection_override']) && !$user->has_cap('access_s2member_level0'))
159 return TRUE;
160 if($posts)
161 {
162 if((!empty($o['login_welcome_page']) || !empty($o['file_download_limit_exceeded_page'])) && !$user->has_cap('access_s2member_level0'))
163 return TRUE;
164 if(!empty($o['specific_ids']) || c_ws_plugin__s2member_utils_gets::get_unavailable_singular_ids_with_ccaps($user->ID ? $user : FALSE))
165 return TRUE;
166 }
167 return FALSE;
168 }
169
170 /**
171 * Excludes protected posts before REST collection/search pagination and counts.
172 *
173 * @package s2Member\Security
174 * @since 261005
175 *
176 * @attaches-to ``add_filter('rest_{$post_type}_query'); add_filter('rest_post_search_query');``
177 *
178 * @param array $args Prepared WP_Query arguments.
179 * @param WP_REST_Request $request Validated collection or search request.
180 *
181 * @return array Query arguments with inaccessible posts excluded.
182 */
183 public static function filter_post_query($args, $request)
184 {
185 if(!self::needs_query_check(TRUE))
186 return $args;
187 $scan = $args;
188 //261005.2324 Scan only matching candidates in bounded batches; presentation filters must not decide REST access.
189 $scan['fields'] = 'ids';
190 $scan['posts_per_page'] = 200;
191 $scan['nopaging'] = FALSE;
192 $scan['paged'] = 1;
193 $scan['orderby'] = 'ID';
194 $scan['order'] = 'ASC';
195 $scan['suppress_filters'] = TRUE;
196 $scan['no_found_rows'] = TRUE;
197 $excluded = array();
198 for($offset = 0; ; $offset += 200)
199 {
200 $scan['offset'] = $offset;
201 $ids = get_posts($scan);
202 if(!$ids)
203 break;
204 _prime_post_caches($ids, TRUE, TRUE);
205 foreach($ids as $id)
206 if(($post = get_post($id)) && is_wp_error(self::check_post_access($post)))
207 $excluded[] = (int)$id;
208 if(count($ids) < 200)
209 break;
210 }
211 return self::exclude_query_ids($args, $excluded, 'post__in', 'post__not_in');
212 }
213
214 /**
215 * Excludes protected terms from REST taxonomy collections and term search.
216 *
217 * @package s2Member\Security
218 * @since 261005
219 *
220 * @attaches-to ``add_filter('rest_{$taxonomy}_query'); add_filter('rest_term_search_query');``
221 *
222 * @param array $args Prepared WP_Term_Query arguments.
223 * @param WP_REST_Request $request Validated collection or search request.
224 *
225 * @return array Query arguments with inaccessible terms excluded.
226 */
227 public static function filter_term_query($args, $request)
228 {
229 if(!self::needs_query_check())
230 return $args;
231 $scan = $args;
232 $scan['number'] = 0;
233 $scan['offset'] = 0;
234 $scan['fields'] = 'all';
235 if(!empty($scan['post']))
236 $scan['object_ids'] = $scan['post'];
237 $terms = get_terms($scan);
238 $excluded = array();
239 if(!is_wp_error($terms))
240 foreach($terms as $term)
241 if(is_wp_error(self::check_resource_access($term->taxonomy === 'category' ? 'categories' : ($term->taxonomy === 'post_tag' ? 'tags' : 'terms'), (int)$term->term_id)))
242 $excluded[] = (int)$term->term_id;
243 return self::exclude_query_ids($args, $excluded, 'include', 'exclude');
244 }
245
246 /**
247 * Excludes comments whose parent posts are inaccessible to the current user.
248 *
249 * @package s2Member\Security
250 * @since 261005
251 *
252 * @attaches-to ``add_filter('rest_comment_query');``
253 *
254 * @param array $args Prepared WP_Comment_Query arguments.
255 * @param WP_REST_Request $request Validated comment collection request.
256 *
257 * @return array Query arguments with comments on inaccessible posts excluded.
258 */
259 public static function filter_comment_query($args, $request)
260 {
261 if(!self::needs_query_check(TRUE))
262 return $args;
263 $scan = $args;
264 $scan['number'] = 200;
265 $scan['fields'] = 'all';
266 $scan['count'] = FALSE;
267 $scan['orderby'] = 'comment_ID';
268 $scan['order'] = 'ASC';
269 $scan['hierarchical'] = FALSE;
270 $excluded = $parents = array();
271 for($offset = 0; ; $offset += 200)
272 {
273 $scan['offset'] = $offset;
274 $comments = get_comments($scan);
275 _prime_post_caches(array_unique(wp_list_pluck($comments, 'comment_post_ID')), TRUE, TRUE);
276 foreach($comments as $comment)
277 {
278 $parent = (int)$comment->comment_post_ID;
279 if(!isset($parents[$parent]))
280 $parents[$parent] = ($post = get_post($parent)) && is_wp_error(self::check_post_access($post));
281 if($parents[$parent])
282 $excluded[] = (int)$comment->comment_ID;
283 }
284 if(count($comments) < 200)
285 break;
286 }
287 return self::exclude_query_ids($args, $excluded, 'comment__in', 'comment__not_in');
288 }
289
290 /**
291 * Applies exclusions without letting an exhausted include list expand the query.
292 *
293 * @package s2Member\Security
294 * @since 261005
295 *
296 * @param array $args Prepared collection query arguments.
297 * @param int[] $excluded Resource IDs denied by membership restrictions.
298 * @param string $include_key Query argument containing explicit included IDs.
299 * @param string $exclude_key Query argument containing excluded IDs.
300 *
301 * @return array Query arguments with exclusions and a restrictive include list.
302 */
303 private static function exclude_query_ids($args, $excluded, $include_key, $exclude_key)
304 {
305 if(!$excluded)
306 return $args;
307 $args[$exclude_key] = array_values(array_unique(array_merge(isset($args[$exclude_key]) ? wp_parse_id_list($args[$exclude_key]) : array(), $excluded)));
308 if(!empty($args[$include_key]))
309 {
310 $included = array_values(array_diff(wp_parse_id_list($args[$include_key]), $args[$exclude_key]));
311 //261005.2324 Keep an exhausted include list restrictive instead of expanding it to the whole collection.
312 $args[$include_key] = $included ? $included : array(0);
313 }
314 return $args;
315 }
316
317 /**
318 * Checks the REST restriction policy against a canonical content resource.
319 *
320 * @package s2Member\Security
321 * @since 261005
322 *
323 * @param string $type Resource kind: posts, pages, categories, tags, or terms.
324 * @param int $id Existing post or term ID.
325 * @param mixed $response Original response to preserve when access is allowed.
326 * @param string $post_type Actual registered post type for post/page restrictions.
327 *
328 * @return mixed Original response, or a membership WP_Error with HTTP 403.
329 */
330 private static function check_resource_access($type, $id, $response = NULL, $post_type = '')
331 {
332 $s2_options = $GLOBALS['WS_PLUGIN__']['s2member']['o'];
333 $s2_levels = $GLOBALS['WS_PLUGIN__']['s2member']['c']['levels'];
334 $ci = $s2_options['ruris_case_sensitive'] ? '' : 'i';
335
336 $user = (is_user_logged_in() && is_object($user = wp_get_current_user()) && !empty($user->ID)) ? $user : FALSE;
337
338
339 // Skip early?
340 if (empty($s2_options['membership_options_page'])
341 || empty($id)
342 || ($type === 'pages' && (int)$s2_options['membership_options_page'] === $id)) {
343 return $response;
344 }
345
346
347 // Get the regular path for this content, to check URI restriction
348 $path = ''; // Only canonical resource URIs participate in content restrictions.
349 if (in_array($type, ['posts', 'pages'], true)) {
350 $permalink = get_permalink($id);
351 if ($permalink && !is_wp_error($permalink)) {
352 $path = c_ws_plugin__s2member_utils_urls::parse_uri($permalink);
353 }
354 } elseif ($type === 'categories') {
355 $term_link = get_term_link($id, 'category');
356 if (!is_wp_error($term_link)) {
357 $path = c_ws_plugin__s2member_utils_urls::parse_uri($term_link);
358 }
359 } elseif ($type === 'tags') {
360 $term_link = get_term_link($id, 'post_tag');
361 if (!is_wp_error($term_link)) {
362 $path = c_ws_plugin__s2member_utils_urls::parse_uri($term_link);
363 }
364 }
365
366
367 else if($type === 'terms' && ($term = get_term($id)) && !is_wp_error($term)) {
368 $term_link = get_term_link($term);
369 if (!is_wp_error($term_link)) {
370 $path = c_ws_plugin__s2member_utils_urls::parse_uri($term_link);
371 }
372 }
373
374 //261005.2320 Page exemptions cannot be inherited by terms with the same numeric ID.
375 $page_id = $type === 'pages' ? $id : 0;
376
377 // Login welcome page
378 if($s2_options['login_welcome_page']
379 && $page_id && $page_id === (int)$s2_options['login_welcome_page']
380 && c_ws_plugin__s2member_no_cache::no_cache_constants('restricted')
381 && (!$user || !$user->has_cap('access_s2member_level0'))
382 && $page_id !== (int)$s2_options['membership_options_page'])
383 return new WP_Error('rest_forbidden', __('You do not have access to this page. [sys level 0]', 's2member'), array('status' => 403));
384
385 // Login redirection override
386 else if($s2_options['login_redirection_override']
387 && ($login_redirection_uri = c_ws_plugin__s2member_login_redirects::login_redirection_uri($user, 'root-returns-false'))
388 && preg_match('/^'.preg_quote($login_redirection_uri, '/').'$/'.$ci, $path)
389 && c_ws_plugin__s2member_no_cache::no_cache_constants('restricted')
390 && (!$user || !$user->has_cap('access_s2member_level0'))
391 && $page_id !== (int)$s2_options['membership_options_page'])
392 return new WP_Error('rest_forbidden', __('You do not have access to this page. [sys level 0]', 's2member'), array('status' => 403));
393
394 // File download limit exceeded page
395 else if($s2_options['file_download_limit_exceeded_page']
396 && $page_id && $page_id === (int)$s2_options['file_download_limit_exceeded_page']
397 && c_ws_plugin__s2member_no_cache::no_cache_constants('restricted')
398 && (!$user || !$user->has_cap('access_s2member_level0'))
399 && $page_id !== (int)$s2_options['membership_options_page'])
400 return new WP_Error('rest_forbidden', __('You do not have access to this page. [sys level 0]', 's2member'), array('status' => 403));
401
402 // Do NOT protect Systematics (exceptions above)
403 if (c_ws_plugin__s2member_systematics_sp::is_systematic_use_specific_page($page_id, $path)) {
404 return $response;
405 }
406
407
408
409 // Categories & other inclusives.
410 if($type == 'categories') {
411 for($n = $s2_levels; $n >= 0; $n--)
412 {
413 // "all"
414 if($s2_options['level'.$n.'_catgs'] === 'all'
415 && c_ws_plugin__s2member_no_cache::no_cache_constants('restricted')
416 && (!$user || !$user->has_cap('access_s2member_level'.$n)))
417 return new WP_Error('rest_forbidden', sprintf(__('You do not have access to this category. [catg level %d]', 's2member'), $n), array('status' => 403));
418
419 // This specific categ
420 else if($s2_options['level'.$n.'_catgs']
421 && in_array($id, ($catgs = preg_split('/['."\r\n\t".'\s;,]+/', $s2_options['level'.$n.'_catgs'])))
422 && c_ws_plugin__s2member_no_cache::no_cache_constants('restricted')
423 && (!$user || !$user->has_cap('access_s2member_level'.$n)))
424 return new WP_Error('rest_forbidden', sprintf(__('You do not have access to this category. [catg level %d]', 's2member'), $n), array('status' => 403));
425
426 // Check parent categs
427 else if($s2_options['level'.$n.'_catgs'] /* Check Category ancestry. */)
428 foreach(preg_split('/['."\r\n\t".'\s;,]+/', $s2_options['level'.$n.'_catgs']) as $catg)
429 if($catg
430 && cat_is_ancestor_of($catg, $id)
431 && c_ws_plugin__s2member_no_cache::no_cache_constants('restricted')
432 && (!$user || !$user->has_cap('access_s2member_level'.$n)))
433 return new WP_Error('rest_forbidden', sprintf(__('You do not have access to this category. [catg level %d]', 's2member'), $n), array('status' => 403));
434 }
435 }
436
437
438 // Post/Page Tags & other inclusives.
439 else if($type == 'tags') {
440 for($n = $s2_levels; $n >= 0; $n--) // Tag Level restrictions. Go through each Level.
441 {
442 // "all"
443 if($s2_options['level'.$n.'_ptags'] === 'all'
444 && c_ws_plugin__s2member_no_cache::no_cache_constants('restricted')
445 && (!$user || !$user->has_cap('access_s2member_level'.$n)))
446 return new WP_Error('rest_forbidden', sprintf(__('You do not have access to this tag. [ptag level %d]', 's2member'), $n), array('status' => 403));
447
448 // This tag
449 else if($s2_options['level'.$n.'_ptags']
450 && ($level_ptags = preg_split('/['."\r\n\t".';,]+/', $s2_options['level'.$n.'_ptags']))
451 && ($tag = get_tag($id))
452 && !empty($tag->name)
453 && (in_array($tag->name, $level_ptags) || in_array($tag->slug, $level_ptags) || in_array((string)$tag->term_id, $level_ptags, TRUE))
454 && c_ws_plugin__s2member_no_cache::no_cache_constants('restricted')
455 && (!$user || !$user->has_cap('access_s2member_level'.$n)))
456 return new WP_Error('rest_forbidden', sprintf(__('You do not have access to this tag. [ptag level %d]', 's2member'), $n), array('status' => 403));
457 }
458 }
459
460
461 // All Posts & other inclusives.
462 else if($type == 'posts') {
463 // Post Level restrictions.
464 for($n = $s2_levels; $n >= 0; $n--)
465 {
466 if($s2_options['level'.$n.'_posts'] === 'all'
467 && c_ws_plugin__s2member_no_cache::no_cache_constants('restricted')
468 && (!$user || !$user->has_cap('access_s2member_level'.$n)))
469 return new WP_Error('rest_forbidden', sprintf(__('You do not have access to this post. [post level %d]', 's2member'), $n), array('status' => 403));
470
471 // what about custom post types?
472 else if(strpos($s2_options['level'.$n.'_posts'], 'all-') !== FALSE
473 && (in_array('all-'.$post_type, preg_split('/['."\r\n\t".'\s;,]+/', $s2_options['level'.$n.'_posts'])) || in_array('all-'.$post_type.'s', preg_split('/['."\r\n\t".'\s;,]+/', $s2_options['level'.$n.'_posts'])))
474 && c_ws_plugin__s2member_no_cache::no_cache_constants('restricted')
475 && (!$user || !$user->has_cap('access_s2member_level'.$n)))
476 return new WP_Error('rest_forbidden', sprintf(__('You do not have access to this post. [post level %d]', 's2member'), $n), array('status' => 403));
477
478 // a specific post by ID
479 else if($s2_options['level'.$n.'_posts']
480 && in_array($id, preg_split('/['."\r\n\t".'\s;,]+/', $s2_options['level'.$n.'_posts']))
481 && c_ws_plugin__s2member_no_cache::no_cache_constants('restricted')
482 && (!$user || !$user->has_cap('access_s2member_level'.$n)))
483 return new WP_Error('rest_forbidden', sprintf(__('You do not have access to this post. [post level %d]', 's2member'), $n), array('status' => 403));
484 }
485
486 // Category Level restrictions.
487 for($n = $s2_levels; $n >= 0; $n--)
488 {
489 if($s2_options['level'.$n.'_catgs'] === 'all'
490 && c_ws_plugin__s2member_no_cache::no_cache_constants('restricted')
491 && (!$user || !$user->has_cap('access_s2member_level'.$n)))
492 return new WP_Error('rest_forbidden', sprintf(__('You do not have access to this post. [catg level %d]', 's2member'), $n), array('status' => 403));
493
494 else if($s2_options['level'.$n.'_catgs']
495 && ($catgs = preg_split('/['."\r\n\t".'\s;,]+/', $s2_options['level'.$n.'_catgs']))
496 && (in_category($catgs, $id) || c_ws_plugin__s2member_utils_conds::in_descendant_category($catgs, $id))
497 && c_ws_plugin__s2member_no_cache::no_cache_constants('restricted')
498 && (!$user || !$user->has_cap('access_s2member_level'.$n)))
499 return new WP_Error('rest_forbidden', sprintf(__('You do not have access to this post. [catg level %d]', 's2member'), $n), array('status' => 403));
500 }
501
502 if(has_term('', 'post_tag', $id)) // Here we take a look to see if this Post has any Tags. If so, we need to run the full set of routines against Tags also.
503 {
504 for($n = $s2_levels; $n >= 0; $n--) // Tag Level restrictions.
505 {
506 if($s2_options['level'.$n.'_ptags'] === 'all'
507 && c_ws_plugin__s2member_no_cache::no_cache_constants('restricted')
508 && (!$user || !$user->has_cap('access_s2member_level'.$n)))
509 return new WP_Error('rest_forbidden', sprintf(__('You do not have access to this post. [ptag level %d]', 's2member'), $n), array('status' => 403));
510
511 else if($s2_options['level'.$n.'_ptags']
512 && has_tag(preg_split('/['."\r\n\t".';,]+/', $s2_options['level'.$n.'_ptags']), $id)
513 && c_ws_plugin__s2member_no_cache::no_cache_constants('restricted')
514 && (!$user || !$user->has_cap('access_s2member_level'.$n)))
515 return new WP_Error('rest_forbidden', sprintf(__('You do not have access to this post. [ptag level %d]', 's2member'), $n), array('status' => 403));
516 }
517 }
518
519 if(is_array($ccaps_req = get_post_meta($id, 's2member_ccaps_req', TRUE))
520 && !empty($ccaps_req)
521 && c_ws_plugin__s2member_no_cache::no_cache_constants('restricted'))
522 {
523 foreach($ccaps_req as $ccap) // The ``$user`` MUST satisfy ALL Custom Capability requirements. Stored as an array of Custom Capabilities.
524 if(strlen($ccap)
525 && (!$user || !$user->has_cap('access_s2member_ccap_'.$ccap)) /* Does this ``$user``, have this Custom Capability? */)
526 return new WP_Error('rest_forbidden', sprintf(__('You do not have access to this post. [ccap %s]', 's2member'), $ccap), array('status' => 403));
527 }
528
529 if($s2_options['specific_ids']
530 && in_array($id, preg_split('/['."\r\n\t".'\s;,]+/', $s2_options['specific_ids']))
531 && c_ws_plugin__s2member_no_cache::no_cache_constants('restricted')
532 && !c_ws_plugin__s2member_sp_access::sp_access($id, 'read-only'))
533 return new WP_Error('rest_forbidden', sprintf(__('You do not have access to this post. [sp %d]', 's2member'), $id), array('status' => 403));
534
535 }
536
537
538 // All Pages & other inclusives.
539 else if($type == 'pages') {
540 for($n = $s2_levels; $n >= 0; $n--) // Page Level restrictions. Go through each Level.
541 {
542 // "all"
543 if($s2_options['level'.$n.'_pages'] === 'all'
544 && c_ws_plugin__s2member_no_cache::no_cache_constants('restricted')
545 && (!$user || !$user->has_cap('access_s2member_level'.$n)))
546 return new WP_Error('rest_forbidden', sprintf(__('You do not have access to this page. [page level %d]', 's2member'), $n), array('status' => 403));
547
548 //261006.0145 The Posts restriction setting also accepts "all-page" and "all-pages" to protect every page.
549 else if(strpos($s2_options['level'.$n.'_posts'], 'all-') !== FALSE
550 && (in_array('all-page', preg_split('/['."\r\n\t".'\s;,]+/', $s2_options['level'.$n.'_posts'])) || in_array('all-pages', preg_split('/['."\r\n\t".'\s;,]+/', $s2_options['level'.$n.'_posts'])))
551 && c_ws_plugin__s2member_no_cache::no_cache_constants('restricted')
552 && (!$user || !$user->has_cap('access_s2member_level'.$n)))
553 return new WP_Error('rest_forbidden', sprintf(__('You do not have access to this page. [page level %d]', 's2member'), $n), array('status' => 403));
554
555 // Current page ID
556 else if($s2_options['level'.$n.'_pages']
557 && in_array($id, preg_split('/['."\r\n\t".'\s;,]+/', $s2_options['level'.$n.'_pages']))
558 && c_ws_plugin__s2member_no_cache::no_cache_constants('restricted')
559 && (!$user || !$user->has_cap('access_s2member_level'.$n)))
560 return new WP_Error('rest_forbidden', sprintf(__('You do not have access to this page. [page level %d]', 's2member'), $n), array('status' => 403));
561 }
562
563 // Tag restriction
564 if(has_term('', 'post_tag', $id)) // Here we take a look to see if this Page has any Tags. If so, we need to run the full set of routines against Tags also.
565 {
566 for($n = $s2_levels; $n >= 0; $n--) // Tag Level restrictions (possibly through Page Tagger). Go through each Level.
567 {
568 // "all" tags
569 if($s2_options['level'.$n.'_ptags'] === 'all'
570 && c_ws_plugin__s2member_no_cache::no_cache_constants('restricted')
571 && (!$user || !$user->has_cap('access_s2member_level'.$n)))
572 return new WP_Error('rest_forbidden', sprintf(__('You do not have access to this page. [ptag level %d]', 's2member'), $n), array('status' => 403));
573
574 // Has a restricted tag
575 else if($s2_options['level'.$n.'_ptags']
576 && has_tag(preg_split('/['."\r\n\t".';,]+/', $s2_options['level'.$n.'_ptags']), $id)
577 && c_ws_plugin__s2member_no_cache::no_cache_constants('restricted')
578 && (!$user || !$user->has_cap('access_s2member_level'.$n)))
579 return new WP_Error('rest_forbidden', sprintf(__('You do not have access to this page. [ptag level %d]', 's2member'), $n), array('status' => 403));
580 }
581 }
582
583 // Custom Capabilities
584 if(is_array($ccaps_req = get_post_meta($id, 's2member_ccaps_req', TRUE))
585 && !empty($ccaps_req)
586 && c_ws_plugin__s2member_no_cache::no_cache_constants('restricted'))
587 {
588 foreach($ccaps_req as $ccap) // The ``$user`` MUST satisfy ALL Custom Capability requirements. Stored as an array of Custom Capabilities.
589 if(strlen($ccap)
590 && (!$user || !$user->has_cap('access_s2member_ccap_'.$ccap)) /* Does this ``$user``, have this Custom Capability? */)
591 return new WP_Error('rest_forbidden', sprintf(__('You do not have access to this page. [ccap %s]', 's2member'), $ccap), array('status' => 403));
592 }
593
594 // Specific Page restriction
595 if($s2_options['specific_ids']
596 && in_array($id, preg_split('/['."\r\n\t".'\s;,]+/', $s2_options['specific_ids']))
597 && c_ws_plugin__s2member_no_cache::no_cache_constants('restricted')
598 && !c_ws_plugin__s2member_sp_access::sp_access($id, 'read-only'))
599 return new WP_Error('rest_forbidden', sprintf(__('You do not have access to this page. [sp %d]', 's2member'), $id), array('status' => 403));
600
601 }
602
603
604 // Also check URIs & other inclusives.
605 for($n = $s2_levels; $n >= 0; $n--) // URIs. Go through each Level.
606 {
607 // URI restriction
608 if($s2_options['level'.$n.'_ruris']) // URIs configured at this Level?
609 foreach(preg_split('/['."\r\n\t".']+/', c_ws_plugin__s2member_ruris::fill_ruri_level_access_rc_vars($s2_options['level'.$n.'_ruris'], $user)) as $str) {
610 if($str
611 && preg_match('/'.preg_quote($str, '/').'/'.$ci, $path)
612 && c_ws_plugin__s2member_no_cache::no_cache_constants('restricted')
613 && (!$user || !$user->has_cap('access_s2member_level'.$n)))
614 return new WP_Error('rest_forbidden', sprintf(__('You do not have access to this content. [ruri level %d]', 's2member'), $n), array('status' => 403));
615 }
616 }
617
618 return $response;
619 }
620 }
621 }
622