[wp-trac] [WordPress Trac] #38276: "Is thing public" API
WordPress Trac
noreply at wordpress.org
Mon Oct 10 16:13:18 UTC 2016
#38276: "Is thing public" API
-----------------------------+-----------------------------
Reporter: jdgrimes | Owner:
Type: feature request | Status: new
Priority: normal | Milestone: Awaiting Review
Component: Role/Capability | Version: trunk
Severity: normal | Keywords:
Focuses: |
-----------------------------+-----------------------------
'''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.
--
Ticket URL: <https://core.trac.wordpress.org/ticket/38276>
WordPress Trac <https://core.trac.wordpress.org/>
WordPress publishing platform
More information about the wp-trac
mailing list