id summary reporter owner description type status priority milestone component version severity resolution keywords cc focuses 38276 """Is thing public/accessible"" API" jdgrimes "'''Question:''' How do you check if a non-logged-in user is allowed to view something in WordPress? (Posts specifically, and also in general?) '''Answer:''' Within WordPress there is no API for checking whether a non-logged-in user is allowed to view a post or other object. == Capabilities API There is the role and capabilities API, but only logged-in users will have a role or any capabilities. A visitor to the site who is not logged-in will thus not have even the `read` capability. `WP_User::has_cap( 'read' )`, and thus `current_user_can( 'read' )`, will return `false`, while `user_can()` will peremptorily return `false` if the user ID passed does not exist before even calling `WP_User::has_cap()`. ''Thus, while we can check if a logged-in/existing user can view a post via the capability API, we cannot use it to check whether a non-logged-in user can view the post.'' == Underlying Philosophy The underlying philosophy that is used within WordPress is that objects such as posts are public by default. '''Everything is accessible to everyone unless specifically restricted.''' This is, in a word, a blacklist-style approach. The object is public, unless it blacklists/restricts itself in some way. When an object is public, the capabilities API, and the concept of capabilities in general, no longer applies as to whether that object is accessible. The object is accessible by default. The question becomes “is this post publicly visible?” rather than “does this user have the ability to view this post?"" The capabilities API, on the other hand, operates on a whitelist philosophy—nobody is allowed to do anything (and thus nobody has any capabilities), unless specifically granted permission. These two different approaches make sense in different scenarios. However, it starts to get sticky when the two intersect. And that is exactly what happens with the `read` capability: we're now checking the whitelist to check a blacklist. And when the user isn't logged in, the capability isn't in the whitelist, which causes everything to get blacklisted—they don't have the capabilities to read any posts at all. == Why doesn't this explode? Obviously, WordPress has gotten along quite well up to this point without everything flying apart though. Non-logged-in users ''can'' view public posts—otherwise you and I wouldn't be able to see about 25% of the web right now. So yes, of course non-logged-in users do get to view those posts. They are public, they aren't restricted. But they get to view them despite not having the `read` capability. In other words, usually this doesn't cause any issues. That's why nobody has brought it up before (that I know of—probably they have). Why? Because usually posts are retrieved for display via `WP_Query`, and it bypasses the `read` capability. Instead, it works from the philosophy of public by default discussed above, and internally handles all of the logic for checking if the post is publicly visible or restricted from the current user. So, any time that you are using `WP_Query`, you will automatically get all of the posts, minus those that were restricted in some way (blacklisting, as it were). == Okay, so then who cares? Good question. After all, shouldn't we always be using `WP_Query` to retrieve posts? === People not using `WP_Query` Well, yes. But, what if we aren't retrieving a post? What if we have a plugin where we are storing some information that relates to a post in a separate table, and we want to display that information publicly on the site. But because that information references the post in a potentially identifying manner, we can't show it to users who can't view the post. I'd say that's a perfectly valid use-case. So how would we check if the current user can see the information for a particular post? I know what you were about to suggest: ""Use `current_user_can()`!"" But yeah, now you know why that won't work: that's a capability check, and non-logged-in users will not have any capabilities. So non-logged-in users wouldn't be able to view the information for any of the posts, even those that they can view publicly on the site. Now, you might say, ""Just check if the post is public."" Yes, and thanks for telling my how to do that. Should I use the `just_check_if_the_post_is_public()` function? :-) That's what this ticket is about. === People adding accessibility restrictions Another reason to care about this is that it means that when somebody wants to restrict the visibility of posts they have to hook-in multiple places: in the capabilities API for the `read` cap, and for the post retrieval logic in `WP_Query`. That is not the main focus of this ticket, and it may not really be practical to solve at all. However, I'm not sure how many developers realize that they need to consider both of these things. == So what do you propose? In this ticket, I'm '''not''' really suggesting that we: - throw out the `read` capability. (Back-compat nightmare.) - modify the caps API to handle non-logged-in users differently. (Fundamental change in an API, probably not practical.) I ''am'' suggesting that there is a need for a new API, to formally provide a means of determining whether a post (or any object) is publicly accessible. This API would include a function like `is_thing_accessible( $thing_id )` (and perhaps `is_thing_accessible_for_user( $user_id, $thing_id )`). == Is that ''really'' needed? Now, I know that some might suggest that this really isn't needed, because you can just check if the post has a public status. That is true in theory, but in practice things are more complex. First, let me reiterate that what I'm proposing isn't just for posts, it is for any objects, of which posts are one, comments another, users another. Secondly, using posts as an example,a plugin can add extra restrictions that cause posts with otherwise public stati to not be publicly accessible to all users. So it isn't a viable solution to just duplicate each core check that would normally apply to posts—others might be added by plugins. For capability checks I don't have to worry about that. I just check for a particular cap and anything that affects that cap will just hook into `map_meta_cap` and I never have to know. But if the user is logged out I can't use the capability API, and suddenly the onus is on me to know about every possible restriction that could ever apply to a post, in order to check if the post is public. I should be able to check `is_thing_accessible( $post_id )` and just forget it, same as I can do with `current_user_can( 'read_post', $post_id )`. We are talking about non-logged-in users here after all. If a restriction isn't taken into account, it doesn't just mean that some less privileged users can view the object, it means that everybody can. ---- I [https://wordpress.slack.com/archives/core/p1476106801005182 brought this up] and [https://wordpress.slack.com/archives/core/p1476107282005192 discussed it with @johnbillion] in the `#core` channel on Slack before creating this (admittedly long-winded) ticket. " feature request new normal Awaiting Review Role/Capability 4.7 normal dev-feedback