001/*
002    Licensed to the Apache Software Foundation (ASF) under one
003    or more contributor license agreements.  See the NOTICE file
004    distributed with this work for additional information
005    regarding copyright ownership.  The ASF licenses this file
006    to you under the Apache License, Version 2.0 (the
007    "License"); you may not use this file except in compliance
008    with the License.  You may obtain a copy of the License at
009
010       http://www.apache.org/licenses/LICENSE-2.0
011
012    Unless required by applicable law or agreed to in writing,
013    software distributed under the License is distributed on an
014    "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
015    KIND, either express or implied.  See the License for the
016    specific language governing permissions and limitations
017    under the License.
018 */
019package org.apache.wiki;
020
021import java.security.Permission;
022import java.security.Principal;
023import java.util.HashMap;
024import java.util.PropertyPermission;
025
026import javax.servlet.http.HttpServletRequest;
027import javax.servlet.http.HttpSession;
028import javax.servlet.jsp.PageContext;
029
030import org.apache.log4j.Logger;
031import org.apache.wiki.auth.NoSuchPrincipalException;
032import org.apache.wiki.auth.UserManager;
033import org.apache.wiki.auth.WikiPrincipal;
034import org.apache.wiki.auth.permissions.AllPermission;
035import org.apache.wiki.auth.user.UserDatabase;
036import org.apache.wiki.tags.WikiTagBase;
037import org.apache.wiki.ui.Command;
038import org.apache.wiki.ui.CommandResolver;
039import org.apache.wiki.ui.GroupCommand;
040import org.apache.wiki.ui.Installer;
041import org.apache.wiki.ui.PageCommand;
042import org.apache.wiki.ui.WikiCommand;
043import org.apache.wiki.util.TextUtil;
044
045/**
046 *  <p>Provides state information throughout the processing of a page.  A
047 *  WikiContext is born when the JSP pages that are the main entry
048 *  points, are invoked.  The JSPWiki engine creates the new
049 *  WikiContext, which basically holds information about the page, the
050 *  handling engine, and in which context (view, edit, etc) the
051 *  call was done.</p>
052 *  <p>A WikiContext also provides request-specific variables, which can
053 *  be used to communicate between plugins on the same page, or
054 *  between different instances of the same plugin.  A WikiContext
055 *  variable is valid until the processing of the page has ended.  For
056 *  an example, please see the Counter plugin.</p>
057 *  <p>When a WikiContext is created, it automatically associates a
058 *  {@link WikiSession} object with the user's HttpSession. The
059 *  WikiSession contains information about the user's authentication
060 *  status, and is consulted by {@link #getCurrentUser()}.
061 *  object</p>
062 *  <p>Do not cache the page object that you get from the WikiContext; always
063 *  use getPage()!</p>
064 *
065 *  @see org.apache.wiki.plugin.Counter
066 *
067 */
068public class WikiContext
069    implements Cloneable, Command
070{
071    private    Command m_command = null;
072
073    private    WikiPage   m_page;
074    private    WikiPage   m_realPage;
075    private    WikiEngine m_engine;
076    private    String     m_template = "default";
077    
078    private    HashMap<String,Object> m_variableMap = new HashMap<>();
079
080    /** Stores the HttpServletRequest.  May be null, if the request did not come from a servlet. */
081    protected  HttpServletRequest m_request = null;
082
083    private    WikiSession m_session = null;
084    
085    /** User is administering JSPWiki (Install, SecurityConfig). */
086    public static final String INSTALL = WikiCommand.INSTALL.getRequestContext();
087
088    /** The VIEW context - the user just wants to view the page contents. */
089    public static final String VIEW = PageCommand.VIEW.getRequestContext();
090
091    /** User wants to view or administer workflows. */
092    public static final String WORKFLOW = WikiCommand.WORKFLOW.getRequestContext();
093
094    /** The EDIT context - the user is editing the page. */
095    public static final String EDIT = PageCommand.EDIT.getRequestContext();
096
097    /** User is preparing for a login/authentication. */
098    public static final String LOGIN = WikiCommand.LOGIN.getRequestContext();
099
100    /** User is preparing to log out. */
101    public static final String LOGOUT = WikiCommand.LOGOUT.getRequestContext();
102
103    /** JSPWiki wants to display a message. */
104    public static final String MESSAGE = WikiCommand.MESSAGE.getRequestContext();
105
106    /** User is viewing a DIFF between the two versions of the page. */
107    public static final String DIFF = PageCommand.DIFF.getRequestContext();
108
109    /** User is viewing page history. */
110    public static final String INFO = PageCommand.INFO.getRequestContext();
111
112    /** User is previewing the changes he just made. */
113    public static final String PREVIEW = PageCommand.PREVIEW.getRequestContext();
114
115    /** User has an internal conflict, and does quite not know what to
116        do. Please provide some counseling. */
117    public static final String CONFLICT = PageCommand.CONFLICT.getRequestContext();
118
119    /** An error has been encountered and the user needs to be informed. */
120    public static final String ERROR = WikiCommand.ERROR.getRequestContext();
121
122    /** User is uploading something. */
123    public static final String UPLOAD = PageCommand.UPLOAD.getRequestContext();
124
125    /** User is commenting something. */
126    public static final String COMMENT = PageCommand.COMMENT.getRequestContext();
127
128    /** User is searching for content. */
129    public static final String FIND = WikiCommand.FIND.getRequestContext();
130
131    /** User wishes to create a new group */
132    public static final String CREATE_GROUP = WikiCommand.CREATE_GROUP.getRequestContext();
133
134    /** User is deleting an existing group. */
135    public static final String DELETE_GROUP = GroupCommand.DELETE_GROUP.getRequestContext();
136
137    /** User is editing an existing group. */
138    public static final String EDIT_GROUP = GroupCommand.EDIT_GROUP.getRequestContext();
139
140    /** User is viewing an existing group */
141    public static final String VIEW_GROUP = GroupCommand.VIEW_GROUP.getRequestContext();
142
143    /** User is editing preferences */
144    public static final String PREFS = WikiCommand.PREFS.getRequestContext();
145
146    /** User is renaming a page. */
147    public static final String RENAME = PageCommand.RENAME.getRequestContext();
148
149    /** User is deleting a page or an attachment. */
150    public static final String DELETE = PageCommand.DELETE.getRequestContext();
151
152    /** User is downloading an attachment. */
153    public static final String ATTACH = PageCommand.ATTACH.getRequestContext();
154
155    /** RSS feed is being generated. */
156    public static final String RSS = PageCommand.RSS.getRequestContext();
157
158    /** This is not a JSPWiki context, use it to access static files. */
159    public static final String NONE = PageCommand.NONE.getRequestContext();
160
161    /** Same as NONE; this is just a clarification. */
162    public static final String OTHER = PageCommand.OTHER.getRequestContext();
163
164    /** User is doing administrative things. */
165    public static final String ADMIN = WikiCommand.ADMIN.getRequestContext();
166
167    private static final Logger log = Logger.getLogger( WikiContext.class );
168
169    private static final Permission DUMMY_PERMISSION = new PropertyPermission( "os.name", "read" );
170
171    /**
172     *  Create a new WikiContext for the given WikiPage. Delegates to
173     * {@link #WikiContext(WikiEngine, HttpServletRequest, WikiPage)}.
174     *  @param engine The WikiEngine that is handling the request.
175     *  @param page   The WikiPage.  If you want to create a
176     *  WikiContext for an older version of a page, you must use this
177     *  constructor.
178     */
179    public WikiContext( WikiEngine engine, WikiPage page )
180    {
181        this( engine, null, findCommand( engine, null, page ) );
182    }
183
184    /**
185     * <p>
186     * Creates a new WikiContext for the given WikiEngine, Command and
187     * HttpServletRequest.
188     * </p>
189     * <p>
190     * This constructor will also look up the HttpSession associated with the
191     * request, and determine if a WikiSession object is present. If not, a new
192     * one is created.
193     * </p>
194     * @param engine The WikiEngine that is handling the request
195     * @param request The HttpServletRequest that should be associated with this
196     *            context. This parameter may be <code>null</code>.
197     * @param command the command
198     * @throws IllegalArgumentException if <code>engine</code> or
199     *             <code>command</code> are <code>null</code>
200     */
201    public WikiContext( WikiEngine engine, HttpServletRequest request, Command command )
202        throws IllegalArgumentException
203    {
204        super();
205        if ( engine == null || command == null )
206        {
207            throw new IllegalArgumentException( "Parameter engine and command must not be null." );
208        }
209
210        m_engine = engine;
211        m_request = request;
212        m_session = WikiSession.getWikiSession( engine, request );
213        m_command = command;
214
215        // If PageCommand, get the WikiPage
216        if( command instanceof PageCommand )
217        {
218            m_page = (WikiPage)((PageCommand)command).getTarget();
219        }
220
221        // If page not supplied, default to front page to avoid NPEs
222        if( m_page == null )
223        {
224            m_page = m_engine.getPage( m_engine.getFrontPage() );
225
226            // Front page does not exist?
227            if( m_page == null )
228            {
229                m_page = new WikiPage( m_engine, m_engine.getFrontPage() );
230            }
231        }
232
233        m_realPage = m_page;
234
235        // Special case: retarget any empty 'view' PageCommands to the front page
236        if ( PageCommand.VIEW.equals( command ) && command.getTarget() == null )
237        {
238            m_command = command.targetedCommand( m_page );
239        }
240
241        // Debugging...
242        if( log.isDebugEnabled() )
243        {
244            HttpSession session = ( request == null ) ? null : request.getSession( false );
245            String sid = ( session == null ) ? "(null)" : session.getId();
246            log.debug( "Creating WikiContext for session ID=" + sid + "; target=" + getName() );
247        }
248
249        // Figure out what template to use
250        setDefaultTemplate( request );
251    }
252
253    /**
254     * Creates a new WikiContext for the given WikiEngine, WikiPage and
255     * HttpServletRequest. This method simply looks up the appropriate Command
256     * using {@link #findCommand(WikiEngine, HttpServletRequest, WikiPage)} and
257     * delegates to
258     * {@link #WikiContext(WikiEngine, HttpServletRequest, Command)}.
259     * @param engine The WikiEngine that is handling the request
260     * @param request The HttpServletRequest that should be associated with this
261     *            context. This parameter may be <code>null</code>.
262     * @param page The WikiPage. If you want to create a WikiContext for an
263     *            older version of a page, you must supply this parameter
264     */
265    public WikiContext(WikiEngine engine, HttpServletRequest request, WikiPage page)
266    {
267        this( engine, request, findCommand( engine, request, page ) );
268    }
269
270    /**
271     * {@inheritDoc}
272     * @see org.apache.wiki.ui.Command#getContentTemplate()
273     */
274    @Override
275    public String getContentTemplate()
276    {
277        return m_command.getContentTemplate();
278    }
279
280    /**
281     * {@inheritDoc}
282     * @see org.apache.wiki.ui.Command#getJSP()
283     */
284    @Override
285    public String getJSP()
286    {
287        return m_command.getContentTemplate();
288    }
289
290    /**
291     *  Sets a reference to the real page whose content is currently being
292     *  rendered.
293     *  <p>
294     *  Sometimes you may want to render the page using some other page's context.
295     *  In those cases, it is highly recommended that you set the setRealPage()
296     *  to point at the real page you are rendering.  Please see InsertPageTag
297     *  for an example.
298     *  <p>
299     *  Also, if your plugin e.g. does some variable setting, be aware that if it
300     *  is embedded in the LeftMenu or some other page added with InsertPageTag,
301     *  you should consider what you want to do - do you wish to really reference
302     *  the "master" page or the included page.
303     *
304     *  @param page  The real page which is being rendered.
305     *  @return The previous real page
306     *  @since 2.3.14
307     *  @see org.apache.wiki.tags.InsertPageTag
308     */
309    public WikiPage setRealPage( WikiPage page )
310    {
311        WikiPage old = m_realPage;
312        m_realPage = page;
313        updateCommand( m_command.getRequestContext() );
314        return old;
315    }
316
317    /**
318     *  Gets a reference to the real page whose content is currently being rendered.
319     *  If your plugin e.g. does some variable setting, be aware that if it
320     *  is embedded in the LeftMenu or some other page added with InsertPageTag,
321     *  you should consider what you want to do - do you wish to really reference
322     *  the "master" page or the included page.
323     *  <p>
324     *  For example, in the default template, there is a page called "LeftMenu".
325     *  Whenever you access a page, e.g. "Main", the master page will be Main, and
326     *  that's what the getPage() will return - regardless of whether your plugin
327     *  resides on the LeftMenu or on the Main page.  However, getRealPage()
328     *  will return "LeftMenu".
329     *
330     *  @return A reference to the real page.
331     *  @see org.apache.wiki.tags.InsertPageTag
332     *  @see org.apache.wiki.parser.JSPWikiMarkupParser
333     */
334    public WikiPage getRealPage()
335    {
336        return m_realPage;
337    }
338
339    /**
340     *  Figure out to which page we are really going to.  Considers
341     *  special page names from the jspwiki.properties, and possible aliases.
342     *  This method forwards requests to
343     *  {@link org.apache.wiki.ui.CommandResolver#getSpecialPageReference(String)}.
344     *  @return A complete URL to the new page to redirect to
345     *  @since 2.2
346     */
347
348    public String getRedirectURL()
349    {
350        String pagename = m_page.getName();
351        String redirURL = m_engine.getCommandResolver().getSpecialPageReference( pagename );
352
353        if( redirURL == null )
354        {
355            String alias = (String)m_page.getAttribute( WikiPage.ALIAS );
356
357            if( alias != null )
358            {
359                redirURL = getViewURL( alias );
360            }
361            else
362            {
363                redirURL = (String)m_page.getAttribute( WikiPage.REDIRECT );
364            }
365        }
366
367        return redirURL;
368    }
369
370    /**
371     *  Returns the handling engine.
372     *
373     *  @return The wikiengine owning this context.
374     */
375    public WikiEngine getEngine()
376    {
377        return m_engine;
378    }
379
380    /**
381     *  Returns the page that is being handled.
382     *
383     *  @return the page which was fetched.
384     */
385    public WikiPage getPage()
386    {
387        return m_page;
388    }
389
390    /**
391     *  Sets the page that is being handled.
392     *
393     *  @param page The wikipage
394     *  @since 2.1.37.
395     */
396    public void setPage( WikiPage page )
397    {
398        m_page = page;
399        updateCommand( m_command.getRequestContext() );
400    }
401
402    /**
403     *  Returns the request context.
404     *  @return The name of the request context (e.g. VIEW).
405     */
406    @Override
407    public String getRequestContext()
408    {
409        return m_command.getRequestContext();
410    }
411
412    /**
413     *  Sets the request context.  See above for the different
414     *  request contexts (VIEW, EDIT, etc.)
415     *
416     *  @param arg The request context (one of the predefined contexts.)
417     */
418    public void setRequestContext( String arg )
419    {
420        updateCommand( arg );
421    }
422
423    /**
424     * {@inheritDoc}
425     * @see org.apache.wiki.ui.Command#getTarget()
426     */
427    @Override
428    public Object getTarget()
429    {
430        return m_command.getTarget();
431    }
432
433    /**
434     * {@inheritDoc}
435     * @see org.apache.wiki.ui.Command#getURLPattern()
436     */
437    @Override
438    public String getURLPattern()
439    {
440        return m_command.getURLPattern();
441    }
442
443    /**
444     *  Gets a previously set variable.
445     *
446     *  @param key The variable name.
447     *  @return The variable contents.
448     */
449    public Object getVariable( String key )
450    {
451        return m_variableMap.get( key );
452    }
453
454    /**
455     *  Sets a variable.  The variable is valid while the WikiContext is valid,
456     *  i.e. while page processing continues.  The variable data is discarded
457     *  once the page processing is finished.
458     *
459     *  @param key The variable name.
460     *  @param data The variable value.
461     */
462    public void setVariable( String key, Object data )
463    {
464        m_variableMap.put( key, data );
465        updateCommand( m_command.getRequestContext() );
466    }
467
468    /**
469     * This is just a simple helper method which will first check the context
470     * if there is already an override in place, and if there is not,
471     * it will then check the given properties.
472     *
473     * @param key      What key are we searching for?
474     * @param defValue Default value for the boolean
475     * @return  {@code true} or {@code false}.
476     */
477    public boolean getBooleanWikiProperty( final String key, final boolean defValue ) {
478        Object bool = getVariable( key );
479        if( bool != null ) {
480            return TextUtil.isPositive( (String) bool );
481        }
482
483        return TextUtil.getBooleanProperty( getEngine().getWikiProperties(), key, defValue );
484    }
485
486    /**
487     *  This method will safely return any HTTP parameters that
488     *  might have been defined.  You should use this method instead
489     *  of peeking directly into the result of getHttpRequest(), since
490     *  this method is smart enough to do all of the right things,
491     *  figure out UTF-8 encoded parameters, etc.
492     *
493     *  @since 2.0.13.
494     *  @param paramName Parameter name to look for.
495     *  @return HTTP parameter, or null, if no such parameter existed.
496     */
497    public String getHttpParameter( String paramName )
498    {
499        String result = null;
500
501        if( m_request != null )
502        {
503            result = m_request.getParameter( paramName );
504        }
505
506        return result;
507    }
508
509    /**
510     *  If the request did originate from a HTTP request,
511     *  then the HTTP request can be fetched here.  However, it the request
512     *  did NOT originate from a HTTP request, then this method will
513     *  return null, and YOU SHOULD CHECK FOR IT!
514     *
515     *  @return Null, if no HTTP request was done.
516     *  @since 2.0.13.
517     */
518    public HttpServletRequest getHttpRequest()
519    {
520        return m_request;
521    }
522
523    /**
524     *  Sets the template to be used for this request.
525     *
526     *  @param dir The template name
527     *  @since 2.1.15.
528     */
529    public void setTemplate( String dir )
530    {
531        m_template = dir;
532    }
533
534    /**
535     * Returns the target of this wiki context: a page, group name or JSP. If
536     * the associated Command is a PageCommand, this method returns the page's
537     * name. Otherwise, this method delegates to the associated Command's
538     * {@link org.apache.wiki.ui.Command#getName()} method. Calling classes
539     * can rely on the results of this method for looking up canonically-correct
540     * page or group names. Because it does not automatically assume that the
541     * wiki context is a PageCommand, calling this method is inherently safer
542     * than calling <code>getPage().getName()</code>.
543     * @return the name of the target of this wiki context
544     * @see org.apache.wiki.ui.PageCommand#getName()
545     * @see org.apache.wiki.ui.GroupCommand#getName()
546     */
547    @Override
548    public String getName()
549    {
550        if ( m_command instanceof PageCommand )
551        {
552            return m_page != null ? m_page.getName() : "<no page>";
553        }
554        return m_command.getName();
555    }
556
557    /**
558     *  Gets the template that is to be used throughout this request.
559     *  @since 2.1.15.
560     *  @return template name
561     */
562    public String getTemplate()
563    {
564        return m_template;
565    }
566
567    /**
568     *  Convenience method that gets the current user. Delegates the
569     *  lookup to the WikiSession associated with this WikiContect.
570     *  May return null, in case the current
571     *  user has not yet been determined; or this is an internal system.
572     *  If the WikiSession has not been set, <em>always</em> returns null.
573     *
574     *  @return The current user; or maybe null in case of internal calls.
575     */
576    public Principal getCurrentUser()
577    {
578        if (m_session == null)
579        {
580            // This shouldn't happen, really...
581            return WikiPrincipal.GUEST;
582        }
583        return m_session.getUserPrincipal();
584    }
585
586    /**
587     *  A shortcut to generate a VIEW url.
588     *
589     *  @param page The page to which to link.
590     *  @return An URL to the page.  This honours the current absolute/relative setting.
591     */
592    public String getViewURL( String page )
593    {
594        return getURL( VIEW, page, null );
595    }
596
597    /**
598     *  Creates an URL for the given request context.
599     *
600     *  @param context e.g. WikiContext.EDIT
601     *  @param page The page to which to link
602     *  @return An URL to the page, honours the absolute/relative setting in jspwiki.properties
603     */
604    public String getURL( String context,
605                          String page )
606    {
607        return getURL( context, page, null );
608    }
609
610    /**
611     *  Returns an URL from a page. It this WikiContext instance was constructed
612     *  with an actual HttpServletRequest, we will attempt to construct the
613     *  URL using HttpUtil, which preserves the HTTPS portion if it was used.
614     *
615     *  @param context The request context (e.g. WikiContext.UPLOAD)
616     *  @param page    The page to which to link
617     *  @param params  A list of parameters, separated with "&amp;"
618     *
619     *  @return An URL to the given context and page.
620     */
621    public String getURL( String context,
622                          String page,
623                          String params )
624    {
625        boolean absolute = "absolute".equals(m_engine.getVariable( this, WikiEngine.PROP_REFSTYLE ));
626
627        // FIXME: is rather slow
628        return m_engine.getURL( context,
629                                page,
630                                params,
631                                absolute );
632
633    }
634
635    /**
636     * Returns the Command associated with this WikiContext.
637     * @return the command
638     */
639    public Command getCommand()
640    {
641        return m_command;
642    }
643
644    /**
645     *  Returns a shallow clone of the WikiContext.
646     *
647     *  @since 2.1.37.
648     *  @return A shallow clone of the WikiContext
649     */
650    @Override
651    public Object clone()
652    {
653        try
654        {
655            // super.clone() must always be called to make sure that inherited objects
656            // get the right type
657            WikiContext copy = (WikiContext)super.clone();
658
659            copy.m_engine = m_engine;
660            copy.m_command = m_command;
661
662            copy.m_template    = m_template;
663            copy.m_variableMap = m_variableMap;
664            copy.m_request     = m_request;
665            copy.m_session     = m_session;
666            copy.m_page        = m_page;
667            copy.m_realPage    = m_realPage;
668            return copy;
669        }
670        catch( CloneNotSupportedException e ){} // Never happens
671
672        return null;
673    }
674
675    /**
676     *  Creates a deep clone of the WikiContext.  This is useful when you want
677     *  to be sure that you don't accidentally mess with page attributes, etc.
678     *
679     *  @since  2.8.0
680     *  @return A deep clone of the WikiContext.
681     */
682    @SuppressWarnings("unchecked")
683    public WikiContext deepClone()
684    {
685        try
686        {
687            // super.clone() must always be called to make sure that inherited objects
688            // get the right type
689            WikiContext copy = (WikiContext)super.clone();
690
691            //  No need to deep clone these
692            copy.m_engine  = m_engine;
693            copy.m_command = m_command; // Static structure
694
695            copy.m_template    = m_template;
696            copy.m_variableMap = (HashMap<String,Object>)m_variableMap.clone();
697            copy.m_request     = m_request;
698            copy.m_session     = m_session;
699            copy.m_page        = (WikiPage)m_page.clone();
700            copy.m_realPage    = (WikiPage)m_realPage.clone();
701            return copy;
702        }
703        catch( CloneNotSupportedException e ){} // Never happens
704
705        return null;
706    }
707
708    /**
709     *  Returns the WikiSession associated with the context.
710     *  This method is guaranteed to always return a valid WikiSession.
711     *  If this context was constructed without an associated
712     *  HttpServletRequest, it will return {@link WikiSession#guestSession(WikiEngine)}.
713     *
714     *  @return The WikiSession associate with this context.
715     */
716    public WikiSession getWikiSession()
717    {
718        return m_session;
719    }
720
721    /**
722     *  This method can be used to find the WikiContext programmatically
723     *  from a JSP PageContext. We check the request context.
724     *  The wiki context, if it exists,
725     *  is looked up using the key
726     *  {@link org.apache.wiki.tags.WikiTagBase#ATTR_CONTEXT}.
727     *
728     *  @since 2.4
729     *  @param pageContext the JSP page context
730     *  @return Current WikiContext, or null, of no context exists.
731     */
732    public static WikiContext findContext( PageContext pageContext )
733    {
734        HttpServletRequest request = (HttpServletRequest) pageContext.getRequest();
735        WikiContext context = (WikiContext)request.getAttribute( WikiTagBase.ATTR_CONTEXT );
736        return context;
737    }
738
739    /**
740     * Returns the permission required to successfully execute this context.
741     * For example, the a wiki context of VIEW for a certain page means that
742     * the PagePermission "view" is required for the page. In some cases, no
743     * particular permission is required, in which case a dummy permission will
744     * be returned ({@link java.util.PropertyPermission}<code> "os.name",
745     * "read"</code>). This method is guaranteed to always return a valid,
746     * non-null permission.
747     * @return the permission
748     * @since 2.4
749     */
750    @Override
751    public Permission requiredPermission()
752    {
753        // This is a filthy rotten hack -- absolutely putrid
754        if ( WikiCommand.INSTALL.equals( m_command ) )
755        {
756            // See if admin users exists
757            try
758            {
759                UserManager userMgr = m_engine.getUserManager();
760                UserDatabase userDb = userMgr.getUserDatabase();
761                userDb.findByLoginName( Installer.ADMIN_ID );
762            }
763            catch ( NoSuchPrincipalException e )
764            {
765                return DUMMY_PERMISSION;
766            }
767            return new AllPermission( m_engine.getApplicationName() );
768        }
769
770        // TODO: we should really break the contract so that this
771        // method returns null, but until then we will use this hack
772        if ( m_command.requiredPermission() == null )
773        {
774            return DUMMY_PERMISSION;
775        }
776
777        return m_command.requiredPermission();
778    }
779
780    /**
781     * Associates a target with the current Command and returns
782     * the new targeted Command. If the Command associated with this
783     * WikiContext is already "targeted", it is returned instead.
784     * @see org.apache.wiki.ui.Command#targetedCommand(java.lang.Object)
785     *
786     * {@inheritDoc}
787     */
788    @Override
789    public Command targetedCommand( Object target )
790    {
791        if ( m_command.getTarget() == null )
792        {
793            return m_command.targetedCommand( target );
794        }
795        return m_command;
796    }
797
798    /**
799     *  Returns true, if the current user has administrative permissions (i.e. the omnipotent
800     *  AllPermission).
801     *
802     *  @since 2.4.46
803     *  @return true, if the user has all permissions.
804     */
805    public boolean hasAdminPermissions() {
806        return m_engine.getAuthorizationManager().checkPermission( getWikiSession(), new AllPermission(m_engine.getApplicationName()) );
807    }
808
809    /**
810     * Figures out which template a new WikiContext should be using.
811     * @param request the HTTP request
812     */
813    protected void setDefaultTemplate( HttpServletRequest request )
814    {
815        String defaultTemplate = m_engine.getTemplateDir();
816
817        //  Figure out which template we should be using for this page.
818        String template = null;
819        if ( request != null )
820        {
821            template = request.getParameter( "skin" );
822        }
823
824        // If request doesn't supply the value, extract from wiki page
825        if( template == null )
826        {
827            WikiPage page = getPage();
828            if ( page != null )
829            {
830                template = (String)page.getAttribute( WikiEngine.PROP_TEMPLATEDIR );
831            }
832
833        }
834
835        // If something over-wrote the default, set the new value.
836        if ( template != null )
837        {
838            setTemplate( template );
839        }
840        else
841        {
842            setTemplate( defaultTemplate );
843        }
844    }
845
846    /**
847     * Looks up and returns a PageCommand based on a supplied WikiPage and HTTP
848     * request. First, the appropriate Command is obtained by examining the HTTP
849     * request; the default is {@link PageCommand#VIEW}. If the Command is a
850     * PageCommand (and it should be, in most cases), a targeted Command is
851     * created using the (non-<code>null</code>) WikiPage as target.
852     * @param engine the wiki engine
853     * @param request the HTTP request
854     * @param page the wiki page
855     * @return the correct command
856     */
857    protected static Command findCommand( WikiEngine engine, HttpServletRequest request, WikiPage page )
858    {
859        String defaultContext = PageCommand.VIEW.getRequestContext();
860        Command command = engine.getCommandResolver().findCommand( request, defaultContext );
861        if ( command instanceof PageCommand && page != null )
862        {
863            command = command.targetedCommand( page );
864        }
865        return command;
866    }
867
868    /**
869     * Protected method that updates the internally cached Command.
870     * Will always be called when the page name, request context, or variable
871     * changes.
872     * @param requestContext the desired request context
873     * @since 2.4
874     */
875    protected void updateCommand( String requestContext )
876    {
877        if ( requestContext == null )
878        {
879            m_command = PageCommand.NONE;
880        }
881        else
882        {
883            CommandResolver resolver = m_engine.getCommandResolver();
884            m_command = resolver.findCommand( m_request, requestContext );
885        }
886
887        if ( m_command instanceof PageCommand && m_page != null )
888        {
889            m_command = m_command.targetedCommand( m_page );
890        }
891    }
892
893}