Preventing role reading others posts

The problem

The problem is, that you have a custom capability_type. This means, that this post type will be handled differently from default post types, that have a capability type of post: Users need to be granted access to this capabilities. Important thing here: This is a good decision.

Foundation

What you’re missing is mapping the capabilities to their corresponding meta capabilities. A short explanation straight from Codex:

edit_post, read_post, and delete_post – These three are meta capabilities, which are then generally mapped to corresponding primitive capabilities depending on the context, for example the post being edited/read/deleted and the user or role being checked. Thus these capabilities would generally not be granted directly to users or roles.

Then there’re the other 4 primitive capbilities: edit_posts, edit_others_posts, publish_posts and read_private_posts. All those (7 in total) caps are assigned in the capabilities array and checked on various locations in core.

But then there’re another 7 capabilities that are not checked against, but only mapped inside map_meta_cap(). When you look at the source of map_meta_cap(), then you’ll see 17 cases for the switch in total. Don’t be confused by that, as not everything is related to posts, but to plugins activation, users and other stuff.

Now the core function map_meta_caps() checks which type of cap is checked, then starts filling an array of capabilities. Then at the end the following is returned:

return apply_filters( 'map_meta_cap', $caps, $cap, $user_id, $args );

Now let’s take a look at the core registration process of post types: map_meta_cap is NULL per default and if not explicitly set, it’s FALSE.

And when we look at a user capability check with the has_cap() function, we can see that map_meta_cap() will be called to retrieve all needed capabilities and then it will be looped through and checked against the resulting capabilities from the mapped caps.

The rule

If you assign a capability_type, you better set map_meta_cap to TRUE.