Details
-
Bug
-
Status: Closed
-
Blocker
-
Resolution: Fixed
-
None
-
None
-
None
Description
What happens is the following :
1) The HST liveuser is used to handle a request
2) The request is for a faceted navigation node
3) The documents in the hippo:resultset are fetched via the liveuser
When the above happens, to populate a document in the faceted navigation resultset,
the read access on the canonical version (the non virtual) node has to be checked. The reason for this
is simple: The faceted navigation query returns any document variant that matches the
query. After this, the variants are filtered by read access!
This is done as follows:
1) From the virtual nodeId of the variant the canonical nodeId is fetched and during this fetch,
the read access is checked, see in HippoLocalItemStateManager :
public ItemState getCanonicalItemState(ItemId id) throws NoSuchItemStateException, ItemStateException { try { if (!accessManager.isGranted(id, AccessManager.READ)) { return null; } } catch (RepositoryException ex) { return null; } return super.getItemState(id); }
2) The 'id' above is THUS from the document variant! This is important to realize. The call
accessManager.isGranted(id, AccessManager.READ) will hit
HippoAccessManager#canRead(org.apache.jackrabbit.core.id.NodeId)
To avoid recursion, this method adds the variant 'id' to the 'inprocessNodeReadAccess'
cache: As long as a node is in that cache, it returns 'true' on subsequent
invocation of canRead(id) with id the id of the variant.
3) Then canRead continues for the variant id, but before the actual security
domains for that node are checked, first parent id access is checked. In case of
a variant id, this parent will be a handle
4) For normal node access, the parent handle is always accessed earlier
already hence already in local item state manager cache including the
document variant reshuffling that is done in
HippoLocalItemStateManager#reorderHandleChildNodeEntries : This reshuffling makes
sure that the readable variants are the first child node entries, so
for example if the readable variant for the liveuser is mydoc[3], then
after reshuffling it become mydoc[1] (the first child)
5) For access via the faceted navigation resultset however, the node id's
and corresponding NodeState's for variants can be loaded while the handle
(parent) NodeState has not yet been loaded (and not cached in the
HippoLocalItemStateManager with reordered child node entries (variants)
6) In (3) I mentioned that thus first parent id access is checked. This is done in
canRead method in:
if (!rootNodeId.equals(id) && !(id instanceof HippoNodeId)) { if (!canRead(nodeState.getParentId())) { removeAccessFromCache(id); return false; } }
7) canRead(nodeState.getParentId()) triggers for the parent id (handle id) to fetch the
NodeState via:
nodeState = (NodeState) getItemState(id);
8) If the NodeState for the handle has not been accessed before by the
HippoLocalItemStateManager, as can be the case as explained for faceted navigation
nodes, then the HippoLocalItemStateManager will get the NodeState (after which
it gets added to the cache of Jackrabbit LocalItemStateManager).
9) When the HippoLocalItemStateManager loads a NodeState for a hippo handle, it
reshuffles the document variants below it based on read access: Namely, the
readable variants MUST be the first child node entries: This is to get the
path correct. Assume the liveuser can only read mydoc[3] (as seen by an
admin user). However, the liveuser want to access /mydoc/mydoc because
it can only read mydoc[3] and not mydoc[2] and mydoc[1].
10) So, the document variants are reshuffled by checking the read access
of the variants one by one. In the HippoLocalItemStateManager for every
variant we invoke
accessManager.isGranted(variant.getId(), AccessManager.READ)
and if there is not read access, the variant is moved to the last child entry
11) And now we can see the problem: We came here via (2) where we were checking the
read access for one of the variants. That resulted in the parent NodeState lookup,
triggering the reshuffling of all variants based on read access. BUT, the variant id
being check in (2) has been added and still is present in the 'inprocessNodeReadAccess'
cache, resulting in a possibly INCORRECT temporary read access for the variant id,
making it possible incorrectly not reshuffled to the last position
12) As a result of (11), it might be that after the variant reshuffling, we for example
end up for a liveuser with a handle NodeState as follows:
+ mydoc (handle) + mydoc (draft) + mydoc (live) + mydoc (preview)
13) Now, the liveuser wants to access /mydoc/mydoc, but the returned node id for that
path is the node id for the 'draft', for which the AccessManager now does not give
read access any more since it has been dropped from the 'inprocessNodeReadAccess'
cache for long already. As a result, the liveuser cannot access the live variant