1. Project Clover database Sat Feb 2 2019 06:45:20 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
38
7
1
225
131
17
0.45
5.43
7
2.43

Classes

Class Line # Actions
DefaultSecurityCacheRulesInvalidatorListener 63 38 0% 17 4
0.929824693%
 

Contributing tests

This file is covered by 17 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   
26    import javax.inject.Inject;
27    import javax.inject.Named;
28    import javax.inject.Provider;
29    import javax.inject.Singleton;
30   
31    import org.slf4j.Logger;
32    import org.xwiki.bridge.event.DocumentCreatedEvent;
33    import org.xwiki.bridge.event.DocumentDeletedEvent;
34    import org.xwiki.bridge.event.DocumentUpdatedEvent;
35    import org.xwiki.component.annotation.Component;
36    import org.xwiki.model.EntityType;
37    import org.xwiki.model.reference.DocumentReference;
38    import org.xwiki.model.reference.DocumentReferenceResolver;
39    import org.xwiki.model.reference.EntityReferenceSerializer;
40    import org.xwiki.model.reference.WikiReference;
41    import org.xwiki.observation.EventListener;
42    import org.xwiki.observation.event.Event;
43    import org.xwiki.security.SecurityReferenceFactory;
44    import org.xwiki.security.authorization.AuthorizationException;
45    import org.xwiki.security.authorization.cache.SecurityCache;
46    import org.xwiki.security.internal.XWikiConstants;
47   
48    import com.xpn.xwiki.XWikiContext;
49    import com.xpn.xwiki.XWikiException;
50    import com.xpn.xwiki.doc.XWikiDocument;
51    import com.xpn.xwiki.objects.BaseObject;
52    import com.xpn.xwiki.user.api.XWikiGroupService;
53   
54    /**
55    * The instance of this class monitors updates and invalidates right cache entries whenever necessary.
56    *
57    * @version $Id: f449aa423fa2797d7518afdaf4e2b78e84dcd3a4 $
58    * @since 4.0M2
59    */
60    @Component
61    @Named(DefaultSecurityCacheRulesInvalidatorListener.NAME)
62    @Singleton
 
63    public class DefaultSecurityCacheRulesInvalidatorListener implements EventListener
64    {
65    /**
66    * The name of the listener.
67    */
68    public static final String NAME =
69    "org.xwiki.security.authorization.internal.DefaultSecurityCacheRulesInvalidatorListener";
70   
71    private static final List<Event> EVENTS =
72    Arrays.<Event>asList(new DocumentCreatedEvent(), new DocumentUpdatedEvent(), new DocumentDeletedEvent());
73   
74    /** Logger. **/
75    @Inject
76    private Logger logger;
77   
78    /** The right cache. */
79    @Inject
80    private SecurityCache securityCache;
81   
82    /** The security reference factory. */
83    @Inject
84    private SecurityReferenceFactory securityReferenceFactory;
85   
86    /** Document reference resolver. */
87    @Inject
88    private DocumentReferenceResolver<String> resolver;
89   
90    /** User document reference resolver. */
91    @Inject
92    @Named("user")
93    private DocumentReferenceResolver<String> userResolver;
94   
95    /** Entity reference serializer. */
96    @Inject
97    private EntityReferenceSerializer<String> serializer;
98   
99    /** Execution object. */
100    @Inject
101    private Provider<XWikiContext> xcontextProvider;
102   
 
103  910 toggle @Override
104    public String getName()
105    {
106  910 return NAME;
107    }
108   
 
109  114 toggle @Override
110    public List<Event> getEvents()
111    {
112  114 return EVENTS;
113    }
114   
115    /**
116    * Obtain a document reference to the {@link com.xpn.xwiki.doc.XWikiDocument} given as parameter.
117    *
118    * @param xwikiDocument The xwiki document.
119    * @return The document reference.
120    */
 
121  8301 toggle private static DocumentReference getDocumentReference(Object xwikiDocument)
122    {
123  8301 XWikiDocument doc = (XWikiDocument) xwikiDocument;
124  8301 return doc.getDocumentReference();
125    }
126   
127    /**
128    * @param source an xwiki document, that has just been updated.
129    * @return true if and only if the xwiki document corresponds to a group.
130    */
 
131  8300 toggle private boolean isGroupDocument(Object source)
132    {
133  8300 XWikiDocument doc = (XWikiDocument) source;
134  8300 DocumentReference docRef = doc.getDocumentReference();
135  8300 DocumentReference groupClass = resolver.resolve(XWikiConstants.GROUP_CLASS, docRef);
136  8300 List<BaseObject> objects = doc.getXObjects(groupClass);
137  8300 return objects != null && objects.size() > 0;
138    }
139   
140    /**
141    * Drop from the cache all members of a given group.
142    *
143    * @param group The group.
144    * @param securityCache Right cache instance to invalidate.
145    * @throws org.xwiki.security.authorization.AuthorizationException on error.
146    */
 
147  133 toggle public void invalidateGroupMembers(DocumentReference group, SecurityCache securityCache)
148    throws AuthorizationException
149    {
150  133 try {
151  133 XWikiContext xwikiContext = this.xcontextProvider.get();
152  133 XWikiGroupService groupService = xwikiContext.getWiki().getGroupService(xwikiContext);
153  133 String groupName = serializer.serialize(group);
154   
155    // The group members inherit the wiki from the group
156    // itself, unless the wiki name is explicitly given.
157   
158  133 WikiReference wikiReference = group.getWikiReference();
159  133 final int nb = 100;
160  133 int i = 0;
161  133 Collection<String> memberNames;
162  133 do {
163  133 memberNames = groupService.getAllMembersNamesForGroup(groupName, nb, i * nb, xwikiContext);
164  133 for (String member : memberNames) {
165  278 DocumentReference memberRef = userResolver.resolve(member, wikiReference);
166   
167    // Avoid infinite loops.
168   
169  278 if (!memberRef.equals(group)) {
170  278 securityCache.remove(securityReferenceFactory.newUserReference(memberRef));
171    }
172    }
173  133 i++;
174  133 } while (memberNames.size() == nb);
175    } catch (XWikiException e) {
176  0 throw new AuthorizationException("Failed to invalidate group member.", e);
177    }
178    }
179   
 
180  8301 toggle @Override
181    public void onEvent(Event event, Object source, Object data)
182    {
183  8301 DocumentReference ref = getDocumentReference(source);
184  8301 try {
185  8301 deliverUpdateEvent(ref);
186  8300 if (isGroupDocument(source)) {
187    // When a group receive a new member, the update event is triggered and the above invalidate the group
188    // and also all its existing members already in cache, but NOT the new member that could be currently
189    // in the cache, and is not yet linked to the group. Here, we invalidate individually all members of
190    // the group based on the updated group, which will only have the effect of invaliding new members.
191  133 invalidateGroupMembers(ref, securityCache);
192    }
193    } catch (AuthorizationException e) {
194  0 this.logger.error("Failed to invalidate group members on the document: {}", ref, e);
195    }
196    }
197   
198    /**
199    * Describe {@code deliverUpdateEvent} method here.
200    *
201    * @param ref Reference to the document that should be invalidated.
202    */
 
203  8301 toggle private void deliverUpdateEvent(DocumentReference ref)
204    {
205  8301 if (XWikiConstants.WIKI_DOC_REFERENCE.equals(ref, EntityType.SPACE)) {
206    // For XWiki.XWikiPreferences, remove the whole wiki.
207  197 securityCache.remove(securityReferenceFactory.newEntityReference(ref.getWikiReference()));
208  8104 } else if (ref.getName().equals(XWikiConstants.SPACE_DOC)) {
209    // For WebPreferences, remove the whole space.
210  85 securityCache.remove(securityReferenceFactory.newEntityReference(ref.getParent()));
211    } else {
212    // For any other documents, remove that document cache.
213  8019 securityCache.remove(securityReferenceFactory.newEntityReference(ref));
214   
215    // If it's a wiki descriptor remove the wiki reference from the cache
216  8019 if (ref.getName().startsWith(XWikiConstants.WIKI_DESCRIPTOR_PREFIX)
217    && XWikiConstants.XWIKI_SPACE_REFERENCE.equals(ref.getLastSpaceReference(), EntityType.SPACE)
218    && ref.getWikiReference().getName().equals(this.xcontextProvider.get().getMainXWiki())) {
219    // For xwiki:XWiki.XWikiServer... documents, also remove the whole corresponding wiki.
220  117 securityCache.remove(securityReferenceFactory.newEntityReference(new WikiReference(
221    ref.getName().substring(XWikiConstants.WIKI_DESCRIPTOR_PREFIX.length()).toLowerCase())));
222    }
223    }
224    }
225    }