The REST resource that we just created works, but it is pretty naive. At the moment, it doesn't make any attempt to ensure that the requesting user has permissions to view any of the content. The first part of that is making sure that we don't show any content that is unpublished and that the requesting user might not have access to. When our REST resource is called, we have access to the currentUser attribute. This AccountProxy is the user that is provided from the appropriate authentication methods enabled on the endpoint. We need this since, as we discussed earlier, we can't rely on there being an actual user session. So let's create a function to check to see if the requesting user is able to see unpublished content:
/**
* Returns whether the provided user should be able to see un-published content
*
* @param \Drupal\user\Entity\User $user
*
* @return Boolean
*/
protected function canSeeUnpublished($user) {
$filter_unpublished = TRUE; // If user can bypass node access don't filter by published
if ($this->currentUser->hasPermission('bypass node access')) {
$filter_unpublished = FALSE;
}
// If there are node access
else if (count(\Drupal::moduleHandler() ->getImplementations('node_grants'))
&& node_access_view_all_nodes($this->currentUser)) {
$filter_unpublished = FALSE;
}
// If current user and can view own unpublished content
else if ($user->id() == $this->currentUser->id()
&& $this->currentUser->hasPermission('view own unpublished content')) {
$filter_unpublished = FALSE;
} return !$filter_unpublished;
}
There are a number of conditions that could allow a user to view unpublished content. The first is that they have permissions to "bypass all access restrictions", the next is if there is a module that implements node_grants and this user is granted the ability to view all nodes, and lastly, if the requesting user is the same as the requested user and they have permissions to "view own unpublished content." If any of these is correct, we need to return that we should not filter unpublished content. We then need to rewrite the queries to make use of that information. While we're at it, we will add the query tags and metadata to ensure that any queries we run also check against node access rules and are executed as the requesting user:
$filter_unpublished = !$this->canSeeUnpublished($request_user); // Get count of all nodes authored by the user
$count_query = \Drupal::entityQuery('node')
->condition('uid', $user); if ($filter_unpublished) {
$count_query->condition('status', 1);
} $count_query->count()
->addTag('node_access')
->addMetaData('op', 'view')
->addMetaData('account', $this->currentUser->getAccount()); $count = $count_query->execute(); // Load all nodes authored by the user
$node_query = \Drupal::entityQuery('node')
->condition('uid', $user); if ($filter_unpublished) {
$node_query->condition('status', 1);
} $node_query->sort('created', 'ASC')
->pager()
->addTag('node_access')
->addMetaData('op', 'view')
->addMetaData('account', $this->currentUser->getAccount()); $nids = $node_query->execute();
Now you can test un-publishing a node and then testing localhost/user/1/posts?_format=hal_json logged in, as the administrator as well as not logged in and see how the count of nodes changes. If you haven't done so already, ensure that anonymous users are able to access "Access GET on User posts resource resource" in the permissions list. Your get() function should look like this.

Only one thing remains to properly secure our new REST endpoint. We should ensure that the requesting user has permissions to view the user they are trying to get information about. This is a simple matter of adding the following test after we load the user:
// Ensure the requesting user is able to view the requested user
$entity_access = $request_user->access('view',
$this->currentUser, TRUE);
if (!$entity_access->isAllowed()) {
throw new AccessDeniedHttpException();
}
This will ensure that only users with permission to view the requested users will see their posts. We also need to add the result of the access test to the cache context to ensure that, if that permission changes, our page is invalidated correctly. That's a simple matter of adding the following code at the bottom of the function before we return the response:
$response->addCacheableDependency($entity_access);
Now we have a robust way of retrieving all nodes authored by a given user. It has pagination and allows traversal through the API. It also has security in place to ensure that the user who requests the list is able to view the user in question, as well as all nodes that might be returned. It also respects all the authentication methods available in both core and contrib. In the end, your get() function should look like this:
