1. Project Clover database Tue Dec 20 2016 21:24:09 CET
  2. Package org.xwiki.security.authorization.internal

File DefaultSecurityCacheRulesInvalidatorListener.java

 

Coverage histogram

../../../../../img/srcFileCovDistChart10.png
0% of files have more coverage

Code metrics

12
40
7
1
230
136
17
0.43
5.71
7
2.43

Classes

Class Line # Actions
DefaultSecurityCacheRulesInvalidatorListener 64 40 0% 17 4
0.932203493.2%
 

Contributing tests

This file is covered by 5 tests. .

Source view

1    /*
2    * See the NOTICE file distributed with this work for additional
3    * information regarding copyright ownership.
4    *
5    * This is free software; you can redistribute it and/or modify it
6    * under the terms of the GNU Lesser General Public License as
7    * published by the Free Software Foundation; either version 2.1 of
8    * the License, or (at your option) any later version.
9    *
10    * This software is distributed in the hope that it will be useful,
11    * but WITHOUT ANY WARRANTY; without even the implied warranty of
12    * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13    * Lesser General Public License for more details.
14    *
15    * You should have received a copy of the GNU Lesser General Public
16    * License along with this software; if not, write to the Free
17    * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
18    * 02110-1301 USA, or see the FSF site: http://www.fsf.org.
19    */
20    package org.xwiki.security.authorization.internal;
21   
22    import java.util.Arrays;
23    import java.util.Collection;
24    import java.util.List;
25    import java.util.concurrent.locks.ReadWriteLock;
26   
27    import javax.inject.Inject;
28    import javax.inject.Named;
29    import javax.inject.Provider;
30    import javax.inject.Singleton;
31   
32    import org.slf4j.Logger;
33    import org.xwiki.bridge.event.DocumentCreatedEvent;
34    import org.xwiki.bridge.event.DocumentDeletedEvent;
35    import org.xwiki.bridge.event.DocumentUpdatedEvent;
36    import org.xwiki.component.annotation.Component;
37    import org.xwiki.model.EntityType;
38    import org.xwiki.model.reference.DocumentReference;
39    import org.xwiki.model.reference.DocumentReferenceResolver;
40    import org.xwiki.model.reference.EntityReferenceSerializer;
41    import org.xwiki.model.reference.WikiReference;
42    import org.xwiki.observation.EventListener;
43    import org.xwiki.observation.event.Event;
44    import org.xwiki.security.SecurityReferenceFactory;
45    import org.xwiki.security.authorization.AuthorizationException;
46    import org.xwiki.security.authorization.cache.SecurityCache;
47    import org.xwiki.security.internal.XWikiConstants;
48   
49    import com.xpn.xwiki.XWikiContext;
50    import com.xpn.xwiki.XWikiException;
51    import com.xpn.xwiki.doc.XWikiDocument;
52    import com.xpn.xwiki.objects.BaseObject;
53    import com.xpn.xwiki.user.api.XWikiGroupService;
54   
55    /**
56    * The instance of this class monitors updates and invalidates right cache entries whenever necessary.
57    *
58    * @version $Id: 058addb6dc19b7bb55eb499ba57b6dfc6b3559d3 $
59    * @since 4.0M2
60    */
61    @Component
62    @Named(DefaultSecurityCacheRulesInvalidator.NAME)
63    @Singleton
 
64    public class DefaultSecurityCacheRulesInvalidatorListener implements EventListener
65    {
66    private static final List<Event> EVENTS = Arrays.<Event> asList(new DocumentCreatedEvent(),
67    new DocumentUpdatedEvent(), new DocumentDeletedEvent());
68   
69    /**
70    * Fair read-write lock to suspend the delivery of cache updates while there are loads in progress.
71    */
72    @Inject
73    @Named(DefaultSecurityCacheRulesInvalidator.NAME)
74    private ReadWriteLock readWriteLock;
75   
76    /** Logger. **/
77    @Inject
78    private Logger logger;
79   
80    /** The right cache. */
81    @Inject
82    private SecurityCache securityCache;
83   
84    /** The security reference factory. */
85    @Inject
86    private SecurityReferenceFactory securityReferenceFactory;
87   
88    /** Document reference resolver. */
89    @Inject
90    private DocumentReferenceResolver<String> resolver;
91   
92    /** User document reference resolver. */
93    @Inject
94    @Named("user")
95    private DocumentReferenceResolver<String> userResolver;
96   
97    /** Entity reference serializer. */
98    @Inject
99    private EntityReferenceSerializer<String> serializer;
100   
101    /** Execution object. */
102    @Inject
103    private Provider<XWikiContext> xcontextProvider;
104   
 
105  518 toggle @Override
106    public String getName()
107    {
108  518 return DefaultSecurityCacheRulesInvalidator.NAME;
109    }
110   
 
111  65 toggle @Override
112    public List<Event> getEvents()
113    {
114  65 return EVENTS;
115    }
116   
117    /**
118    * Obtain a document reference to the {@link com.xpn.xwiki.doc.XWikiDocument} given as parameter.
119    *
120    * @param xwikiDocument The xwiki document.
121    * @return The document reference.
122    */
 
123  3970 toggle private static DocumentReference getDocumentReference(Object xwikiDocument)
124    {
125  3970 XWikiDocument doc = (XWikiDocument) xwikiDocument;
126  3970 return doc.getDocumentReference();
127    }
128   
129    /**
130    * @param source an xwiki document, that has just been updated.
131    * @return true if and only if the xwiki document corresponds to a group.
132    */
 
133  3970 toggle private boolean isGroupDocument(Object source)
134    {
135  3970 XWikiDocument doc = (XWikiDocument) source;
136  3970 DocumentReference docRef = doc.getDocumentReference();
137  3970 DocumentReference groupClass = resolver.resolve(XWikiConstants.GROUP_CLASS, docRef);
138  3970 List<BaseObject> objects = doc.getXObjects(groupClass);
139  3970 return objects != null && objects.size() > 0;
140    }
141   
142    /**
143    * Drop from the cache all members of a given group.
144    *
145    * @param group The group.
146    * @param securityCache Right cache instance to invalidate.
147    * @throws org.xwiki.security.authorization.AuthorizationException on error.
148    */
 
149  84 toggle public void invalidateGroupMembers(DocumentReference group, SecurityCache securityCache)
150    throws AuthorizationException
151    {
152  84 try {
153  84 XWikiContext xwikiContext = this.xcontextProvider.get();
154  84 XWikiGroupService groupService = xwikiContext.getWiki().getGroupService(xwikiContext);
155  84 String groupName = serializer.serialize(group);
156   
157    // The group members inherit the wiki from the group
158    // itself, unless the wiki name is explicitly given.
159   
160  84 WikiReference wikiReference = group.getWikiReference();
161  84 final int nb = 100;
162  84 int i = 0;
163  84 Collection<String> memberNames;
164  84 do {
165  84 memberNames = groupService.getAllMembersNamesForGroup(groupName, nb, i * nb, xwikiContext);
166  84 for (String member : memberNames) {
167  91 DocumentReference memberRef = userResolver.resolve(member, wikiReference);
168   
169    // Avoid infinite loops.
170   
171  91 if (!memberRef.equals(group)) {
172  91 securityCache.remove(securityReferenceFactory.newUserReference(memberRef));
173    }
174    }
175  84 i++;
176  84 } while (memberNames.size() == nb);
177    } catch (XWikiException e) {
178  0 throw new AuthorizationException("Failed to invalidate group member.", e);
179    }
180    }
181   
 
182  3970 toggle @Override
183    public void onEvent(Event event, Object source, Object data)
184    {
185  3970 DocumentReference ref = getDocumentReference(source);
186  3970 readWriteLock.writeLock().lock();
187  3970 try {
188  3970 deliverUpdateEvent(ref);
189  3970 if (isGroupDocument(source)) {
190    // When a group receive a new member, the update event is triggered and the above invalidate the group
191    // and also all its existing members already in cache, but NOT the new member that could be currently
192    // in the cache, and is not yet linked to the group. Here, we invalidate individually all members of
193    // the group based on the updated group, which will only have the effect of invaliding new members.
194  84 invalidateGroupMembers(ref, securityCache);
195    }
196    } catch (AuthorizationException e) {
197  0 this.logger.error("Failed to invalidate group members on the document: {}", ref, e);
198    } finally {
199  3970 readWriteLock.writeLock().unlock();
200    }
201    }
202   
203    /**
204    * Describe {@code deliverUpdateEvent} method here.
205    *
206    * @param ref Reference to the document that should be invalidated.
207    */
 
208  3970 toggle private void deliverUpdateEvent(DocumentReference ref)
209    {
210  3970 if (XWikiConstants.WIKI_DOC_REFERENCE.equals(ref, EntityType.SPACE)) {
211    // For XWiki.XWikiPreferences, remove the whole wiki.
212  141 securityCache.remove(securityReferenceFactory.newEntityReference(ref.getWikiReference()));
213  3829 } else if (ref.getName().equals(XWikiConstants.SPACE_DOC)) {
214    // For WebPreferences, remove the whole space.
215  21 securityCache.remove(securityReferenceFactory.newEntityReference(ref.getParent()));
216    } else {
217    // For any other documents, remove that document cache.
218  3808 securityCache.remove(securityReferenceFactory.newEntityReference(ref));
219   
220    // If it's a wiki descriptor remove the wiki reference from the cache
221  3808 if (ref.getName().startsWith(XWikiConstants.WIKI_DESCRIPTOR_PREFIX)
222    && XWikiConstants.XWIKI_SPACE_REFERENCE.equals(ref.getLastSpaceReference(), EntityType.SPACE)
223    && ref.getWikiReference().getName().equals(this.xcontextProvider.get().getMainXWiki())) {
224    // For xwiki:XWiki.XWikiServer... documents, also remove the whole corresponding wiki.
225  143 securityCache.remove(securityReferenceFactory.newEntityReference(new WikiReference(ref.getName()
226    .substring(XWikiConstants.WIKI_DESCRIPTOR_PREFIX.length()).toLowerCase())));
227    }
228    }
229    }
230    }