1. Project Clover database Sat Feb 2 2019 06:45:20 CET
  2. Package org.xwiki.security.authorization.internal

File XWikiCachingRightService.java

 

Coverage histogram

../../../../../img/srcFileCovDistChart8.png
56% of files have more coverage

Code metrics

22
57
14
2
360
227
37
0.65
4.07
7
2.64

Classes

Class Line # Actions
XWikiCachingRightService 49 55 0% 36 22
0.7555555775.6%
XWikiCachingRightService.ActionMap 153 2 0% 1 0
1.0100%
 

Contributing tests

This file is covered by 10 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.HashMap;
23    import java.util.List;
24   
25    import org.slf4j.Logger;
26    import org.slf4j.LoggerFactory;
27    import org.xwiki.model.reference.DocumentReference;
28    import org.xwiki.model.reference.DocumentReferenceResolver;
29    import org.xwiki.model.reference.EntityReference;
30    import org.xwiki.model.reference.WikiReference;
31    import org.xwiki.rendering.transformation.RenderingContext;
32    import org.xwiki.security.authorization.AuthorizationManager;
33    import org.xwiki.security.authorization.ContextualAuthorizationManager;
34    import org.xwiki.security.authorization.Right;
35    import org.xwiki.security.internal.XWikiConstants;
36   
37    import com.xpn.xwiki.XWikiContext;
38    import com.xpn.xwiki.XWikiException;
39    import com.xpn.xwiki.doc.XWikiDocument;
40    import com.xpn.xwiki.user.api.XWikiRightService;
41    import com.xpn.xwiki.user.api.XWikiUser;
42    import com.xpn.xwiki.web.Utils;
43   
44    /**
45    * Legacy bridge aimed to replace the current RightService until the new API is used in all places.
46    * @version $Id: ba110b3cfc8e41ad8aafc4b2816552a64cc7ea32 $
47    * @since 4.0M2
48    */
 
49    public class XWikiCachingRightService implements XWikiRightService
50    {
51    /** Logger. */
52    private static final Logger LOGGER = LoggerFactory.getLogger(XWikiCachingRightService.class);
53   
54    /** The login action. */
55    private static final String DELETE_ACTION = "delete";
56   
57    /** The delete action. */
58    private static final String LOGIN_ACTION = "login";
59   
60    /**
61    * Map containing all known actions.
62    */
63    private static final ActionMap ACTION_MAP = new ActionMap();
64   
 
65  50 toggle static {
66  50 ACTION_MAP
67    .putAction(LOGIN_ACTION, Right.LOGIN)
68    .putAction("view", Right.VIEW)
69    .putAction(DELETE_ACTION, Right.DELETE)
70    .putAction("distribution", Right.VIEW)
71    .putAction("admin", Right.ADMIN)
72    .putAction("programming", Right.PROGRAM)
73    .putAction("edit", Right.EDIT)
74    .putAction("register", Right.REGISTER)
75    .putAction("logout", Right.LOGIN)
76    .putAction("loginerror", Right.LOGIN)
77    .putAction("loginsubmit", Right.LOGIN)
78    .putAction("viewrev", Right.VIEW)
79    .putAction("viewattachrev", Right.VIEW)
80    .putAction("get", Right.VIEW)
81    .putAction("downloadrev", Right.VIEW)
82    .putAction("plain", Right.VIEW)
83    .putAction("raw", Right.VIEW)
84    .putAction("attach", Right.VIEW)
85    .putAction("charting", Right.VIEW)
86    .putAction("skin", Right.VIEW)
87    .putAction("download", Right.VIEW)
88    .putAction("dot", Right.VIEW)
89    .putAction("svg", Right.VIEW)
90    .putAction("pdf", Right.VIEW)
91    // TODO: The "undelete" action is mapped to the right "undelete" in the legacy
92    // implementation. We should check whether the "undelete" right is actually used or not and
93    // if we need to introduce it here as well for compatiblity reasons.
94    .putAction("undelete", Right.EDIT)
95    .putAction("reset", Right.DELETE)
96    .putAction("commentadd", Right.COMMENT)
97    .putAction("commentsave", Right.COMMENT)
98    .putAction("redirect", Right.VIEW)
99    .putAction("export", Right.VIEW)
100    .putAction("import", Right.ADMIN)
101    .putAction("jsx", Right.VIEW)
102    .putAction("ssx", Right.VIEW)
103    .putAction("tex", Right.VIEW)
104    .putAction("unknown", Right.VIEW)
105    .putAction("save", Right.EDIT)
106    .putAction("preview", Right.EDIT)
107    .putAction("lock", Right.EDIT)
108    .putAction("cancel", Right.EDIT)
109    .putAction("delattachment", Right.EDIT)
110    .putAction("inline", Right.EDIT)
111    .putAction("propadd", Right.EDIT)
112    .putAction("propupdate", Right.EDIT)
113    .putAction("propdelete", Right.EDIT)
114    .putAction("propdisable", Right.EDIT)
115    .putAction("propenable", Right.EDIT)
116    .putAction("objectadd", Right.EDIT)
117    .putAction("objectremove", Right.EDIT)
118    .putAction("objectsync", Right.EDIT)
119    .putAction("rollback", Right.EDIT)
120    .putAction("upload", Right.EDIT)
121    .putAction("create", Right.VIEW)
122    .putAction("deleteversions", Right.ADMIN)
123    .putAction("deletespace", Right.ADMIN)
124    .putAction("temp", Right.VIEW)
125    .putAction("webjars", Right.VIEW);
126    }
127   
128    /** Resolver for document references. */
129    @SuppressWarnings("unchecked")
130    private DocumentReferenceResolver<String> documentReferenceResolver
131    = Utils.getComponent(DocumentReferenceResolver.TYPE_STRING, "currentmixed");
132   
133    /** Resolver for user and group document references. */
134    @SuppressWarnings("unchecked")
135    private DocumentReferenceResolver<String> userAndGroupReferenceResolver
136    = Utils.getComponent(DocumentReferenceResolver.TYPE_STRING, "user");
137   
138    /** The rendering context to check PR for signed macro. */
139    private final RenderingContext renderingContext
140    = Utils.getComponent(RenderingContext.class);
141   
142    /** The authorization manager used to really do the job. */
143    private final AuthorizationManager authorizationManager
144    = Utils.getComponent(AuthorizationManager.class);
145   
146    /** The contextual authorization manager used to really do the job. */
147    private final ContextualAuthorizationManager contextualAuthorizationManager
148    = Utils.getComponent(ContextualAuthorizationManager.class);
149   
150    /**
151    * Specialized map with a chainable put action to avoid exceeding code complexity during initialization.
152    */
 
153    private static class ActionMap extends HashMap<String, Right>
154    {
155    /** Serialization identifier for conformance to Serializable. */
156    private static final long serialVersionUID = 1;
157   
158    /** Allow filling the map in the initializer without exceeding code complexity.
159    * @param action the action name
160    * @param right the corresponding right required
161    * @return this action map to allow code chaining
162    */
 
163  2800 toggle public ActionMap putAction(String action, Right right)
164    {
165  2800 put(action, right);
166  2800 return this;
167    }
168    }
169   
170    /**
171    * Map an action represented by a string to a right.
172    * @param action String representation of action.
173    * @return right The corresponding Right instance, or
174    * {@code ILLEGAL}.
175    */
 
176  41812 toggle public static Right actionToRight(String action)
177    {
178  41819 Right right = ACTION_MAP.get(action);
179  41816 if (right == null) {
180  0 return Right.ILLEGAL;
181    }
182  41817 return right;
183    }
184   
185    /**
186    * @param username name as a string.
187    * @param wikiReference default wiki, if not explicitly specified in the username.
188    * @return A document reference that uniquely identifies the user.
189    */
 
190  574485 toggle private DocumentReference resolveUserName(String username, WikiReference wikiReference)
191    {
192  574583 return userAndGroupReferenceResolver.resolve(username, wikiReference);
193    }
194   
195    /**
196    * @param docname name of the document as string.
197    * @param wikiReference the default wiki where the document will be
198    * assumed do be located, unless explicitly specified in docname.
199    * @return the document reference.
200    */
 
201  548484 toggle private DocumentReference resolveDocumentName(String docname, WikiReference wikiReference)
202    {
203  548697 return documentReferenceResolver.resolve(docname, wikiReference);
204    }
205   
206    /**
207    * Show the login page, unless the wiki is configured otherwise.
208    * @param context the context
209    */
 
210  288 toggle private void showLogin(XWikiContext context)
211    {
212  288 try {
213  288 if (context.getRequest() != null
214    /*
215    * We must explicitly check the action from the context, as some templates that are
216    * rendered may call checkAccess with different actions (which, strictly speaking is
217    * incorrect, those templates should use hasAccessLevel). In particular, 'menuview.vm'
218    * will call checkAccess with action 'view', if the document 'XWiki.XWikiLogin' exists.
219    */
220    && !LOGIN_ACTION.equals(context.getAction())
221    && !context.getWiki().Param("xwiki.hidelogin", "false").equalsIgnoreCase("true")) {
222  288 context.getWiki().getAuthService().showLogin(context);
223    }
224    } catch (XWikiException e) {
225  0 LOGGER.error("Failed to show login page.", e);
226    }
227    }
228   
229    /**
230    * Ensure user authentication if needed.
231    *
232    * @param context Current XWikiContext
233    */
 
234  41794 toggle private void authenticateUser(XWikiContext context)
235    {
236  41807 DocumentReference contextUserReference = context.getUserReference();
237  41803 DocumentReference userReference = contextUserReference;
238   
239  41802 if (userReference == null && context.getMode() != XWikiContext.MODE_XMLRPC) {
240  35799 try {
241  35800 XWikiUser user = context.getWiki().checkAuth(context);
242  35821 if (user != null) {
243  25125 userReference = resolveUserName(user.getUser(), new WikiReference(context.getWikiId()));
244    }
245    } catch (XWikiException e) {
246  0 LOGGER.error("Caught exception while authenticating user.", e);
247    }
248    }
249   
250  41800 if (userReference != null && XWikiConstants.GUEST_USER.equals(userReference.getName())) {
251    // Public users (not logged in) should be passed as null in the new API. It may happen that badly
252    // design code, and poorly written API does not take care, so we prevent security issue here.
253  0 userReference = null;
254    }
255   
256  41809 if (userReference != contextUserReference
257    && (userReference == null || !userReference.equals(contextUserReference))) {
258  25130 context.setUserReference(userReference);
259    }
260    }
261   
 
262  41819 toggle @Override
263    public boolean checkAccess(String action, XWikiDocument doc, XWikiContext context)
264    throws XWikiException
265    {
266  41817 Right right = actionToRight(action);
267  41811 EntityReference entityReference = doc.getDocumentReference();
268   
269  41810 LOGGER.debug("checkAccess for action [{}] on entity [{}].", right, entityReference);
270   
271  41810 authenticateUser(context);
272   
273  41816 if (contextualAuthorizationManager.hasAccess(right, entityReference)) {
274  41500 return true;
275    }
276   
277    // If the right has been denied, and we have guest user, redirect the user to login page
278    // unless the denied is on the login action, which could cause infinite redirection.
279    // FIXME: The hasAccessLevel is broken (do not allow document creator) on the delete action in the old
280    // implementation, so code that simply want to verify if a user can delete (but is not actually deleting)
281    // has to call checkAccess. This happen really often, and this why we should not redirect to login on failed
282    // delete, since it would prevent most user to do anything.
283  308 if (context.getUserReference() == null && !DELETE_ACTION.equals(action) && !LOGIN_ACTION.equals(action)) {
284  288 LOGGER.debug("Redirecting unauthenticated user to login, since it have been denied [{}] on [{}].",
285    right, entityReference);
286  288 showLogin(context);
287    }
288   
289  308 return false;
290    }
291   
 
292  548586 toggle @Override
293    public boolean hasAccessLevel(String rightName, String username, String docname, XWikiContext context)
294    throws XWikiException
295    {
296  548984 WikiReference wikiReference = new WikiReference(context.getWikiId());
297  549172 DocumentReference document = resolveDocumentName(docname, wikiReference);
298  549177 LOGGER.debug("hasAccessLevel() resolved document named [{}] into reference [{}]", docname, document);
299  549403 DocumentReference user = resolveUserName(username, wikiReference);
300   
301  549099 if (user != null && XWikiConstants.GUEST_USER.equals(user.getName())) {
302    // Public users (not logged in) should be passed as null in the new API
303  156224 user = null;
304    }
305   
306  549277 Right right = Right.toRight(rightName);
307   
308  548277 return authorizationManager.hasAccess(right, user, document);
309    }
310   
 
311  2479 toggle @Override
312    public boolean hasProgrammingRights(XWikiContext context)
313    {
314  2479 return contextualAuthorizationManager.hasAccess(Right.PROGRAM);
315    }
316   
 
317  0 toggle @Override
318    public boolean hasProgrammingRights(XWikiDocument doc, XWikiContext context)
319    {
320  0 DocumentReference user;
321  0 WikiReference wiki;
322   
323  0 if (doc != null) {
324  0 user = doc.getContentAuthorReference();
325  0 wiki = doc.getDocumentReference().getWikiReference();
326    } else {
327  0 user = context.getUserReference();
328  0 wiki = new WikiReference(context.getWikiId());
329    }
330   
331  0 if (user != null && XWikiConstants.GUEST_USER.equals(user.getName())) {
332    // Public users (not logged in) should be passed as null in the new API. It may happen that badly
333    // design code, and poorly written API does not take care, so we prevent security issue here.
334  0 user = null;
335    }
336   
337    // This method as never check for external contextual aspect like rendering context restriction or dropping of
338    // permissions. So we do not use the contextual authorization manager to keep backward compatibility.
339  0 return authorizationManager.hasAccess(Right.PROGRAM, user, wiki);
340    }
341   
 
342  19088 toggle @Override
343    public boolean hasAdminRights(XWikiContext context)
344    {
345  19088 return contextualAuthorizationManager.hasAccess(Right.ADMIN);
346    }
347   
 
348  7204 toggle @Override
349    public boolean hasWikiAdminRights(XWikiContext context)
350    {
351  7204 return contextualAuthorizationManager.hasAccess(Right.ADMIN, new WikiReference(context.getWikiId()));
352    }
353   
 
354  44 toggle @Override
355    public List<String> listAllLevels(XWikiContext context)
356    throws XWikiException
357    {
358  44 return Right.getAllRightsAsString();
359    }
360    }