1. Project Clover database Tue Dec 20 2016 21:24:09 CET
  2. Package org.xwiki.wiki.script

File WikiManagerScriptService.java

 

Coverage histogram

../../../../img/srcFileCovDistChart9.png
38% of files have more coverage

Code metrics

20
115
23
1
557
290
45
0.39
5
23
1.96

Classes

Class Line # Actions
WikiManagerScriptService 63 115 0% 45 17
0.892405189.2%
 

Contributing tests

This file is covered by 33 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.wiki.script;
21   
22    import java.util.ArrayList;
23    import java.util.Collection;
24   
25    import javax.inject.Inject;
26    import javax.inject.Named;
27    import javax.inject.Provider;
28    import javax.inject.Singleton;
29   
30    import org.apache.commons.lang3.StringUtils;
31    import org.slf4j.Logger;
32    import org.xwiki.component.annotation.Component;
33    import org.xwiki.context.Execution;
34    import org.xwiki.model.reference.DocumentReference;
35    import org.xwiki.model.reference.DocumentReferenceResolver;
36    import org.xwiki.model.reference.EntityReferenceSerializer;
37    import org.xwiki.model.reference.WikiReference;
38    import org.xwiki.script.service.ScriptService;
39    import org.xwiki.script.service.ScriptServiceManager;
40    import org.xwiki.security.authorization.AccessDeniedException;
41    import org.xwiki.security.authorization.AuthorizationException;
42    import org.xwiki.security.authorization.AuthorizationManager;
43    import org.xwiki.security.authorization.Right;
44    import org.xwiki.url.internal.standard.StandardURLConfiguration;
45    import org.xwiki.wiki.configuration.WikiConfiguration;
46    import org.xwiki.wiki.descriptor.WikiDescriptor;
47    import org.xwiki.wiki.descriptor.WikiDescriptorManager;
48    import org.xwiki.wiki.internal.descriptor.document.WikiDescriptorDocumentHelper;
49    import org.xwiki.wiki.manager.WikiManager;
50    import org.xwiki.wiki.manager.WikiManagerException;
51   
52    import com.xpn.xwiki.XWikiContext;
53   
54    /**
55    * Script service to manage wikis.
56    *
57    * @version $Id: 8284b6e60c5225954b1e4db90a6684f118a46f99 $
58    * @since 5.3M2
59    */
60    @Component
61    @Named(WikiManagerScriptService.ROLEHINT)
62    @Singleton
 
63    public class WikiManagerScriptService implements ScriptService
64    {
65    /**
66    * Hint of the component.
67    */
68    public static final String ROLEHINT = "wiki";
69   
70    /**
71    * Field name of the last API exception inserted in context.
72    */
73    @Deprecated
74    public static final String CONTEXT_LASTEXCEPTION = "lastexception";
75   
76    /**
77    * The key under which the last encountered error is stored in the current execution context.
78    */
79    private static final String WIKIERROR_KEY = "scriptservice.wiki.error";
80   
81    @Inject
82    private WikiManager wikiManager;
83   
84    @Inject
85    private WikiDescriptorManager wikiDescriptorManager;
86   
87    @Inject
88    private Provider<XWikiContext> xcontextProvider;
89   
90    /**
91    * Execution context.
92    */
93    @Inject
94    private Execution execution;
95   
96    @Inject
97    private AuthorizationManager authorizationManager;
98   
99    @Inject
100    private DocumentReferenceResolver<String> documentReferenceResolver;
101   
102    @Inject
103    private EntityReferenceSerializer<String> entityReferenceSerializer;
104   
105    @Inject
106    private ScriptServiceManager scriptServiceManager;
107   
108    @Inject
109    private StandardURLConfiguration standardURLConfiguration;
110   
111    @Inject
112    private WikiConfiguration wikiConfiguration;
113   
114    @Inject
115    private WikiDescriptorDocumentHelper wikiDescriptorDocumentHelper;
116   
117    /**
118    * Logging tool.
119    */
120    @Inject
121    private Logger logger;
122   
123    /**
124    * Get a sub script service related to wiki. (Note that we're voluntarily using an API name of "get" to make it
125    * extra easy to access Script Services from Velocity (since in Velocity writing <code>$services.wiki.name</code> is
126    * equivalent to writing <code>$services.wiki.get("name")</code>). It also makes it a short and easy API name for
127    * other scripting languages.
128    *
129    * @param serviceName id of the script service
130    * @return the service asked or null if none could be found
131    */
 
132  210 toggle public ScriptService get(String serviceName)
133    {
134  210 return scriptServiceManager.get(ROLEHINT + '.' + serviceName);
135    }
136   
137    /**
138    * Get the error generated while performing the previously called action.
139    *
140    * @return an eventual exception or {@code null} if no exception was thrown
141    */
 
142  15 toggle public Exception getLastError()
143    {
144  15 return (Exception) this.execution.getContext().getProperty(WIKIERROR_KEY);
145    }
146   
147    /**
148    * Store a caught exception in the context, so that it can be later retrieved using {@link #getLastError()}.
149    *
150    * @param e the exception to store, can be {@code null} to clear the previously stored exception
151    * @see #getLastError()
152    */
 
153  14 toggle private void setLastError(Exception e)
154    {
155  14 this.execution.getContext().setProperty(WIKIERROR_KEY, e);
156    }
157   
158    // TODO: move to new API a soon as a proper helper is provided
 
159  8 toggle private void checkProgrammingRights() throws AuthorizationException
160    {
161  8 XWikiContext xcontext = this.xcontextProvider.get();
162  8 authorizationManager.checkAccess(Right.PROGRAM, xcontext.getDoc().getAuthorReference(), xcontext.getDoc()
163    .getDocumentReference());
164    }
165   
166    /**
167    * Create a new wiki.
168    *
169    * @param wikiId unique identifier of the new wiki
170    * @param wikiAlias default alias of the new wiki
171    * @param ownerId Id of the user that will own the wiki
172    * @param failOnExist Fail the operation if the wiki id already exists
173    * @return the wiki descriptor of the new wiki, or null if problems occur
174    */
 
175  5 toggle public WikiDescriptor createWiki(String wikiId, String wikiAlias, String ownerId, boolean failOnExist)
176    {
177  5 WikiDescriptor descriptor = null;
178   
179  5 XWikiContext context = xcontextProvider.get();
180   
181  5 try {
182    // Check if the current script has the programing rights
183  5 checkProgrammingRights();
184   
185    // Check right access
186  4 WikiReference mainWikiReference = new WikiReference(getMainWikiId());
187  4 authorizationManager.checkAccess(Right.CREATE_WIKI, context.getUserReference(), mainWikiReference);
188  3 if (!failOnExist) {
189  1 authorizationManager.checkAccess(Right.PROGRAM, context.getUserReference(), mainWikiReference);
190    }
191   
192    // Create the wiki
193  2 descriptor = wikiManager.create(wikiId, wikiAlias, failOnExist);
194    // Set the owner
195  1 descriptor.setOwnerId(ownerId);
196  1 wikiDescriptorManager.saveDescriptor(descriptor);
197    } catch (Exception e) {
198  4 error(e);
199    }
200   
201  5 return descriptor;
202    }
203   
204    /**
205    * Delete the specified wiki.
206    *
207    * @param wikiId unique identifier of the wiki to delete
208    * @return true if the wiki has been successfully deleted
209    */
 
210  3 toggle public boolean deleteWiki(String wikiId)
211    {
212    // Test if the script has the programming right
213  3 XWikiContext context = xcontextProvider.get();
214   
215  3 try {
216    // Check if the current script has the programming rights
217  3 checkProgrammingRights();
218   
219    // Test right
220  3 if (!canDeleteWiki(entityReferenceSerializer.serialize(context.getUserReference()), wikiId)) {
221  0 throw new AuthorizationException("You don't have the right to delete the wiki");
222    }
223   
224    // Delete the wiki
225  3 wikiManager.delete(wikiId);
226   
227    // Return success
228  3 return true;
229    } catch (Exception e) {
230  0 error(String.format("Failed to delete wiki [%s]", wikiId), e);
231    }
232   
233  0 return false;
234    }
235   
236    /**
237    * Test if a given user can delete a given wiki.
238    *
239    * @param userId the id of the user to test
240    * @param wikiId the id of the wiki
241    * @return whether or not the user can delete the specified wiki
242    */
 
243  37 toggle public boolean canDeleteWiki(String userId, String wikiId)
244    {
245  37 try {
246    // Get target wiki descriptor
247  37 WikiDescriptor descriptor = wikiDescriptorManager.getById(wikiId);
248  37 if (descriptor == null) {
249  0 error(new Exception(String.format("Could not find descriptor for wiki [%s]]", wikiId)));
250  0 return false;
251    }
252    // Get the full reference of the given user
253  37 DocumentReference userReference = documentReferenceResolver.resolve(userId);
254  37 String fullUserId = entityReferenceSerializer.serialize(userReference);
255   
256    // If the user is the owner
257  37 String owner = descriptor.getOwnerId();
258  37 if (fullUserId.equals(owner)) {
259  37 return true;
260    }
261   
262    // If the user is an admin
263  0 WikiReference wikiReference = new WikiReference(wikiId);
264  0 if (authorizationManager.hasAccess(Right.ADMIN, userReference, wikiReference)) {
265  0 return true;
266    }
267    } catch (WikiManagerException e) {
268  0 error(String.format("Error while getting the descriptor of wiki [%s]", wikiId), e);
269    }
270   
271  0 return false;
272    }
273   
274    /**
275    * Get a wiki descriptor from one of its alias.
276    *
277    * @param wikiAlias alias to search
278    * @return the wiki descriptor corresponding to the alias, or null if no descriptors match the alias
279    */
 
280  2 toggle public WikiDescriptor getByAlias(String wikiAlias)
281    {
282  2 WikiDescriptor descriptor = null;
283   
284  2 try {
285  2 descriptor = wikiDescriptorManager.getByAlias(wikiAlias);
286    } catch (WikiManagerException e) {
287  1 error(e);
288    }
289   
290  2 return descriptor;
291    }
292   
293    /**
294    * Get a wiki descriptor from its unique identifier.
295    *
296    * @param wikiId unique identifier of the wiki to search
297    * @return the wiki descriptor corresponding to the Id, or null if no descriptors match the id
298    */
 
299  3551 toggle public WikiDescriptor getById(String wikiId)
300    {
301  3551 WikiDescriptor descriptor = null;
302   
303  3551 try {
304  3551 descriptor = wikiDescriptorManager.getById(wikiId);
305    } catch (WikiManagerException e) {
306  1 error(e);
307    }
308   
309  3551 return descriptor;
310    }
311   
312    /**
313    * Get all the wiki descriptors.
314    *
315    * @return the list of all wiki descriptors
316    */
 
317  50 toggle public Collection<WikiDescriptor> getAll()
318    {
319  50 Collection<WikiDescriptor> wikis;
320  50 try {
321  50 wikis = wikiDescriptorManager.getAll();
322    } catch (WikiManagerException e) {
323  1 error(e);
324  1 wikis = new ArrayList<WikiDescriptor>();
325    }
326   
327  50 return wikis;
328    }
329   
330    /**
331    * Get all the wiki identifiers.
332    *
333    * @return the list of all wiki identifiers
334    * @since 6.2M1
335    */
 
336  2 toggle public Collection<String> getAllIds()
337    {
338  2 Collection<String> wikis;
339  2 try {
340  2 wikis = wikiDescriptorManager.getAllIds();
341    } catch (WikiManagerException e) {
342  1 error(e);
343  1 wikis = new ArrayList<String>();
344    }
345   
346  2 return wikis;
347    }
348   
349    /**
350    * Test if a wiki exists.
351    *
352    * @param wikiId unique identifier to test
353    * @return true if a wiki with this Id exists on the system or null if some error occurs.
354    */
 
355  4 toggle public Boolean exists(String wikiId)
356    {
357  4 try {
358  4 return wikiDescriptorManager.exists(wikiId);
359    } catch (WikiManagerException e) {
360  1 error(e);
361  1 return null;
362    }
363    }
364   
365    /**
366    * Check if the wikiId is valid and available (the name is not already taken for technical reasons).
367    *
368    * @param wikiId the Id to test
369    * @return true if the Id is valid and available or null if some error occurs
370    */
 
371  34 toggle public Boolean idAvailable(String wikiId)
372    {
373  33 try {
374  34 return wikiManager.idAvailable(wikiId);
375    } catch (WikiManagerException e) {
376  1 error(e);
377  1 return null;
378    }
379    }
380   
381    /**
382    * @return the descriptor of the main wiki or null if problems occur
383    */
 
384  2 toggle public WikiDescriptor getMainWikiDescriptor()
385    {
386  2 WikiDescriptor descriptor = null;
387  2 try {
388  2 descriptor = wikiDescriptorManager.getMainWikiDescriptor();
389    } catch (WikiManagerException e) {
390  1 error(e);
391    }
392   
393  2 return descriptor;
394    }
395   
396    /**
397    * @return the Id of the main wiki
398    */
 
399  237 toggle public String getMainWikiId()
400    {
401  237 return wikiDescriptorManager.getMainWikiId();
402    }
403   
404    /**
405    * @return the id of the current wiki
406    */
 
407  87 toggle public String getCurrentWikiId()
408    {
409  87 return wikiDescriptorManager.getCurrentWikiId();
410    }
411   
412    /**
413    * @return the descriptor of the current wiki
414    */
 
415  3385 toggle public WikiDescriptor getCurrentWikiDescriptor()
416    {
417  3385 WikiDescriptor descriptor = null;
418  3384 try {
419  3385 descriptor = wikiDescriptorManager.getCurrentWikiDescriptor();
420    } catch (WikiManagerException e) {
421  0 error(e);
422    }
423   
424  3385 return descriptor;
425    }
426   
427    /**
428    * Save the specified descriptor (if you have the right).
429    *
430    * @param descriptor descriptor to save
431    * @return true if it succeed
432    */
 
433  8 toggle public boolean saveDescriptor(WikiDescriptor descriptor)
434    {
435  8 XWikiContext context = xcontextProvider.get();
436   
437  8 boolean isAllowed;
438   
439  8 try {
440    // Get the wiki owner
441  8 WikiDescriptor oldDescriptor = wikiDescriptorManager.getById(descriptor.getId());
442  8 WikiReference wikiReference = descriptor.getReference();
443  8 if (oldDescriptor != null) {
444    // Users that can edit the wiki's descriptor document are allowed to use this API as well. This
445    // includes global admins.
446  6 DocumentReference descriptorDocument =
447    wikiDescriptorDocumentHelper.getDocumentReferenceFromId(oldDescriptor.getId());
448  6 isAllowed = authorizationManager.hasAccess(Right.EDIT, context.getUserReference(), descriptorDocument);
449   
450  6 String currentOwner = oldDescriptor.getOwnerId();
451  6 if (!isAllowed) {
452    // The current owner can edit anything.
453  4 isAllowed = entityReferenceSerializer.serialize(context.getUserReference()).equals(currentOwner);
454    }
455   
456  6 if (!isAllowed) {
457    // Local admins can edit the descriptor, except for the "ownerId" field, which should be
458    // editable only by the current owner or main wiki admins.
459  3 String newOwner = descriptor.getOwnerId();
460  3 isAllowed =
461    authorizationManager.hasAccess(Right.ADMIN, context.getUserReference(), wikiReference)
462    && StringUtils.equals(newOwner, currentOwner);
463    }
464    } else {
465    // Saving a descriptor that did not already exist should be reserved to global admins
466  2 isAllowed =
467    authorizationManager.hasAccess(Right.ADMIN, context.getUserReference(), new WikiReference(
468    wikiDescriptorManager.getMainWikiId()));
469    }
470   
471  8 if (!isAllowed) {
472    // Exhausted all options. Deny access for the current user to edit the descriptor.
473  3 throw new AccessDeniedException(context.getUserReference(), wikiReference);
474    } else {
475    // Execute the operation.
476  5 wikiDescriptorManager.saveDescriptor(descriptor);
477    }
478   
479  5 return true;
480    } catch (Exception e) {
481  3 error(e);
482  3 return false;
483    }
484    }
485   
486    /**
487    * Tell if the path mode is used for subwikis.
488    * <p>
489    * Example:
490    *
491    * <pre>
492    * {@code
493    * wiki alias: subwiki
494    * URL if path mode is enabled:
495    * /xwiki/wiki/subwiki/
496    * URL if path mode is disabled:
497    * http://subwiki/
498    * }
499    * </pre>
500    *
501    * @return either or not the path mode is enabled
502    */
 
503  6 toggle public boolean isPathMode()
504    {
505  6 return standardURLConfiguration.isPathBasedMultiWiki();
506    }
507   
508    /**
509    * @return the default suffix to append to new wiki aliases.
510    */
 
511  2 toggle public String getAliasSuffix()
512    {
513  2 return wikiConfiguration.getAliasSuffix();
514    }
515   
516    /**
517    * Log exception and store it in the context.
518    *
519    * @param e the caught exception
520    */
 
521  14 toggle private void error(Exception e)
522    {
523  14 error(null, e);
524    }
525   
526    /**
527    * Log exception and store it in the context.
528    *
529    * @param errorMessage error message
530    * @param e the caught exception
531    */
 
532  14 toggle private void error(String errorMessage, Exception e)
533    {
534  14 String errorMessageToLog = errorMessage;
535  14 if (errorMessageToLog == null) {
536  14 errorMessageToLog = e.getMessage();
537    }
538   
539    // Log exception.
540  14 logger.error(errorMessageToLog, e);
541   
542    // Store exception in context.
543  14 setLastError(e);
544    // Deprecated
545  14 this.execution.getContext().setProperty(CONTEXT_LASTEXCEPTION, e);
546    }
547   
548    /**
549    * @return the last exception, or null if there is not
550    * @deprecated since 5.4RC1 use {@link #getLastError()} ()} instead
551    */
 
552  4 toggle @Deprecated
553    public Exception getLastException()
554    {
555  4 return (Exception) this.execution.getContext().getProperty(CONTEXT_LASTEXCEPTION);
556    }
557    }