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

File XWikiCachingRightService.java

 

Coverage histogram

../../../../../img/srcFileCovDistChart7.png
64% of files have more coverage

Code metrics

22
57
14
2
361
228
37
0.65
4.07
7
2.64

Classes

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