You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@jspwiki.apache.org by ju...@apache.org on 2020/02/24 16:52:56 UTC

[jspwiki] 14/38: JSPWIKI-120: rename + extract interface from AuthorizationManager

This is an automated email from the ASF dual-hosted git repository.

juanpablo pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/jspwiki.git

commit f1b62ab54488614ac209b26ffa727504df5cf81d
Author: juanpablo <ju...@apache.org>
AuthorDate: Thu Feb 20 18:43:16 2020 +0100

    JSPWIKI-120: rename + extract interface from AuthorizationManager
---
 .../org/apache/wiki/auth/AuthorizationManager.java | 654 +++------------------
 .../main/java/org/apache/wiki/auth/Authorizer.java |  10 +-
 .../wiki/auth/DefaultAuthorizationManager.java     | 425 +++++++++++++
 .../apache/wiki/auth/authorize/GroupDatabase.java  |   4 +-
 .../apache/wiki/auth/authorize/GroupManager.java   | 141 +++--
 .../wiki/auth/authorize/JDBCGroupDatabase.java     | 118 ++--
 .../apache/wiki/auth/authorize/WebAuthorizer.java  |  20 +-
 .../auth/authorize/WebContainerAuthorizer.java     |  92 ++-
 .../wiki/auth/authorize/XMLGroupDatabase.java      |  35 +-
 .../src/main/resources/ini/classmappings.xml       |   2 +-
 .../wiki/auth/AuthenticationManagerTest.java       | 103 ++--
 .../java/org/apache/wiki/auth/TestAuthorizer.java  |  42 +-
 12 files changed, 774 insertions(+), 872 deletions(-)

diff --git a/jspwiki-main/src/main/java/org/apache/wiki/auth/AuthorizationManager.java b/jspwiki-main/src/main/java/org/apache/wiki/auth/AuthorizationManager.java
index 265cef2..a0ab560 100644
--- a/jspwiki-main/src/main/java/org/apache/wiki/auth/AuthorizationManager.java
+++ b/jspwiki-main/src/main/java/org/apache/wiki/auth/AuthorizationManager.java
@@ -18,382 +18,145 @@
  */
 package org.apache.wiki.auth;
 
-
-import org.apache.log4j.Logger;
 import org.apache.wiki.WikiContext;
-import org.apache.wiki.WikiEngine;
-import org.apache.wiki.WikiPage;
 import org.apache.wiki.WikiSession;
-import org.apache.wiki.api.exceptions.NoRequiredPropertyException;
+import org.apache.wiki.api.core.Engine;
 import org.apache.wiki.api.exceptions.WikiException;
-import org.apache.wiki.auth.acl.Acl;
-import org.apache.wiki.auth.acl.AclEntry;
-import org.apache.wiki.auth.acl.UnresolvedPrincipal;
 import org.apache.wiki.auth.authorize.Role;
-import org.apache.wiki.auth.permissions.AllPermission;
-import org.apache.wiki.auth.permissions.PagePermission;
-import org.apache.wiki.auth.user.UserDatabase;
-import org.apache.wiki.auth.user.UserProfile;
 import org.apache.wiki.event.WikiEventListener;
 import org.apache.wiki.event.WikiEventManager;
 import org.apache.wiki.event.WikiSecurityEvent;
-import org.apache.wiki.i18n.InternationalizationManager;
-import org.apache.wiki.preferences.Preferences;
-import org.apache.wiki.util.ClassUtil;
-import org.freshcookies.security.policy.LocalPolicy;
 
 import javax.servlet.http.HttpServletResponse;
-import java.io.File;
 import java.io.IOException;
-import java.net.URL;
-import java.security.AccessControlException;
-import java.security.AccessController;
-import java.security.CodeSource;
 import java.security.Permission;
 import java.security.Principal;
-import java.security.PrivilegedAction;
-import java.security.ProtectionDomain;
-import java.security.cert.Certificate;
-import java.text.MessageFormat;
-import java.util.Arrays;
-import java.util.Map;
 import java.util.Properties;
-import java.util.ResourceBundle;
-import java.util.WeakHashMap;
+
 
 /**
- * <p>Manages all access control and authorization; determines what authenticated
- * users are allowed to do.</p>
- * <p>Privileges in JSPWiki are expressed as Java-standard {@link java.security.Permission}
- * classes. There are two types of permissions:</p>
+ * <p>Manages all access control and authorization; determines what authenticated users are allowed to do.</p>
+ * <p>Privileges in JSPWiki are expressed as Java-standard {@link java.security.Permission} classes. There are two types of permissions:</p>
  * <ul>
- *   <li>{@link org.apache.wiki.auth.permissions.WikiPermission} - privileges that apply
- *   to an entire wiki instance: <em>e.g.,</em> editing user profiles, creating pages, creating groups</li>
- *   <li>{@link org.apache.wiki.auth.permissions.PagePermission} - privileges that apply
- *   to a single wiki page or range of pages: <em>e.g.,</em> reading, editing, renaming
+ *   <li>{@link org.apache.wiki.auth.permissions.WikiPermission} - privileges that apply to an entire wiki instance: <em>e.g.,</em>
+ *   editing user profiles, creating pages, creating groups</li>
+ *   <li>{@link org.apache.wiki.auth.permissions.PagePermission} - privileges that apply to a single wiki page or range of pages:
+ *   <em>e.g.,</em> reading, editing, renaming
  * </ul>
- * <p>Calling classes determine whether they are entitled to perform a particular action
- * by constructing the appropriate permission first, then passing it and the current
- * {@link org.apache.wiki.WikiSession} to the
- * {@link #checkPermission(WikiSession, Permission)} method. If the session's
- * Subject possesses the permission, the action is allowed.</p>
- * <p>For WikiPermissions, the decision criteria is relatively simple: the caller either
- * possesses the permission, as granted by the wiki security policy -- or not.</p>
- * <p>For PagePermissions, the logic is exactly the same if the page being checked
- * does not have an access control list. However, if the page does have an ACL, the
- * authorization decision is made based the <em>union</em> of the permissions
- * granted in the ACL and in the security policy. In other words, the user must
- * be named in the ACL (or belong to a group or role that is named in the ACL)
- * <em>and</em> be granted (at least) the same permission in the security policy. We
- * do this to prevent a user from gaining more permissions than they already
- * have, based on the security policy.</p>
- * <p>See the {@link #checkPermission(WikiSession, Permission)} and
- * {@link #hasRoleOrPrincipal(WikiSession, Principal)} methods for more information
- * on the authorization logic.</p>
+ * <p>Calling classes determine whether they are entitled to perform a particular action by constructing the appropriate permission first,
+ * then passing it and the current {@link org.apache.wiki.WikiSession} to the {@link #checkPermission(WikiSession, Permission)} method. If
+ * the session's Subject possesses the permission, the action is allowed.</p>
+ * <p>For WikiPermissions, the decision criteria is relatively simple: the caller either possesses the permission, as granted by the wiki
+ * security policy -- or not.</p>
+ * <p>For PagePermissions, the logic is exactly the same if the page being checked does not have an access control list. However, if the
+ * page does have an ACL, the authorization decision is made based the <em>union</em> of the permissions granted in the ACL and in the
+ * security policy. In other words, the user must be named in the ACL (or belong to a group or role that is named in the ACL) <em>and</em>
+ * be granted (at least) the same permission in the security policy. We do this to prevent a user from gaining more permissions than they
+ * already have, based on the security policy.</p>
+ * <p>See the implementation on {@link #checkPermission(WikiSession, Permission)} method for more information on the authorization logic.</p>
+ *
  * @since 2.3
  * @see AuthenticationManager
  */
-public class AuthorizationManager {
+public interface AuthorizationManager {
 
-    private static final Logger log = Logger.getLogger( AuthorizationManager.class );
-    /**
-     * The default external Authorizer is the {@link org.apache.wiki.auth.authorize.WebContainerAuthorizer}
-     */
-    public static final String                DEFAULT_AUTHORIZER = "org.apache.wiki.auth.authorize.WebContainerAuthorizer";
+    /** The default external Authorizer is the {@link org.apache.wiki.auth.authorize.WebContainerAuthorizer} */
+    String DEFAULT_AUTHORIZER = "org.apache.wiki.auth.authorize.WebContainerAuthorizer";
 
     /** Property that supplies the security policy file name, in WEB-INF. */
-    protected static final String             POLICY      = "jspwiki.policy.file";
+    String POLICY = "jspwiki.policy.file";
 
     /** Name of the default security policy file, in WEB-INF. */
-    protected static final String             DEFAULT_POLICY      = "jspwiki.policy";
-
-    /**
-     * The property name in jspwiki.properties for specifying the external {@link Authorizer}.
-     */
-    public static final String                PROP_AUTHORIZER   = "jspwiki.authorizer";
+    String DEFAULT_POLICY = "jspwiki.policy";
 
-    private Authorizer                        m_authorizer      = null;
-
-    /** Cache for storing ProtectionDomains used to evaluate the local policy. */
-    private Map<Principal, ProtectionDomain>                               m_cachedPds       = new WeakHashMap<>();
-
-    private WikiEngine                        m_engine          = null;
-
-    private LocalPolicy                       m_localPolicy     = null;
+    /** The property name in jspwiki.properties for specifying the external {@link Authorizer}. */
+    String PROP_AUTHORIZER = "jspwiki.authorizer";
 
     /**
-     * Constructs a new AuthorizationManager instance.
-     */
-    public AuthorizationManager()
-    {
-    }
-
-    /**
-     * Returns <code>true</code> or <code>false</code>, depending on
-     * whether a Permission is allowed for the Subject associated with
+     * Returns <code>true</code> or <code>false</code>, depending on whether a Permission is allowed for the Subject associated with
      * a supplied WikiSession. The access control algorithm works this way:
      * <ol>
      * <li>The {@link org.apache.wiki.auth.acl.Acl} for the page is obtained</li>
-     * <li>The Subject associated with the current
-     * {@link org.apache.wiki.WikiSession} is obtained</li>
-     * <li>If the Subject's Principal set includes the Role Principal that is
-     * the administrator group, always allow the Permission</li>
-     * <li>For all permissions, check to see if the Permission is allowed according
-     * to the default security policy. If it isn't, deny the permission and halt
-     * further processing.</li>
-     * <li>If there is an Acl, get the list of Principals assigned this
-     * Permission in the Acl: these will be role, group or user Principals, or
-     * {@link org.apache.wiki.auth.acl.UnresolvedPrincipal}s (see below).
-     * Then iterate through the Subject's Principal set and determine whether
-     * the user (Subject) possesses any one of these specified Roles or
-     * Principals. The matching process delegates to
-     * {@link #hasRoleOrPrincipal(WikiSession, Principal)}.
+     * <li>The Subject associated with the current {@link org.apache.wiki.WikiSession} is obtained</li>
+     * <li>If the Subject's Principal set includes the Role Principal that is the administrator group, always allow the Permission</li>
+     * <li>For all permissions, check to see if the Permission is allowed according to the default security policy. If it isn't, deny
+     * the permission and halt further processing.</li>
+     * <li>If there is an Acl, get the list of Principals assigned this Permission in the Acl: these will be role, group or user Principals,
+     * or {@link org.apache.wiki.auth.acl.UnresolvedPrincipal}s (see below). Then iterate through the Subject's Principal set and determine
+     * whether the user (Subject) possesses any one of these specified Roles or Principals.</li>
      * </ol>
      * <p>
-     * Note that when iterating through the Acl's list of authorized Principals,
-     * it is possible that one or more of the Acl's Principal entries are of
-     * type <code>UnresolvedPrincipal</code>. This means that the last time
-     * the ACL was read, the Principal (user, built-in Role, authorizer Role, or
-     * wiki Group) could not be resolved: the Role was not valid, the user
-     * wasn't found in the UserDatabase, or the Group wasn't known to (e.g.,
-     * cached) in the GroupManager. If an <code>UnresolvedPrincipal</code> is
-     * encountered, this method will attempt to resolve it first <em>before</em>
-     * checking to see if the Subject possesses this principal, by calling
-     * {@link #resolvePrincipal(String)}. If the (re-)resolution does not
-     * succeed, the access check for the principal will fail by definition (the
-     * Subject should never contain UnresolvedPrincipals).
+     * Note that when iterating through the Acl's list of authorized Principals, it is possible that one or more of the Acl's Principal
+     * entries are of type <code>UnresolvedPrincipal</code>. This means that the last time the ACL was read, the Principal (user, built-in
+     * Role, authorizer Role, or wiki Group) could not be resolved: the Role was not valid, the user wasn't found in the UserDatabase, or
+     * the Group wasn't known to (e.g., cached) in the GroupManager. If an <code>UnresolvedPrincipal</code> is encountered, this method
+     * will attempt to resolve it first <em>before</em> checking to see if the Subject possesses this principal, by calling
+     * {@link #resolvePrincipal(String)}. If the (re-)resolution does not succeed, the access check for the principal will fail by
+     * definition (the Subject should never contain UnresolvedPrincipals).
      * </p>
      * <p>
      * If security not set to JAAS, will return true.
      * </p>
+     *
      * @param session the current wiki session
      * @param permission the Permission being checked
-     * @see #hasRoleOrPrincipal(WikiSession, Principal)
      * @return the result of the Permission check
      */
-    public boolean checkPermission( final WikiSession session, final Permission permission )
-    {
-        //
-        //  A slight sanity check.
-        //
-        if ( session == null || permission == null )
-        {
-            fireEvent( WikiSecurityEvent.ACCESS_DENIED, null, permission );
-            return false;
-        }
-
-        final Principal user = session.getLoginPrincipal();
-
-        // Always allow the action if user has AllPermission
-        final Permission allPermission = new AllPermission( m_engine.getApplicationName() );
-        final boolean hasAllPermission = checkStaticPermission( session, allPermission );
-        if ( hasAllPermission )
-        {
-            fireEvent( WikiSecurityEvent.ACCESS_ALLOWED, user, permission );
-            return true;
-        }
-
-        // If the user doesn't have *at least* the permission
-        // granted by policy, return false.
-        final boolean hasPolicyPermission = checkStaticPermission( session, permission );
-        if ( !hasPolicyPermission )
-        {
-            fireEvent( WikiSecurityEvent.ACCESS_DENIED, user, permission );
-            return false;
-        }
-
-        // If this isn't a PagePermission, it's allowed
-        if ( ! ( permission instanceof PagePermission ) )
-        {
-            fireEvent( WikiSecurityEvent.ACCESS_ALLOWED, user, permission );
-            return true;
-        }
-
-        //
-        // If the page or ACL is null, it's allowed.
-        //
-        final String pageName = ((PagePermission)permission).getPage();
-        final WikiPage page = m_engine.getPageManager().getPage( pageName );
-        final Acl acl = ( page == null) ? null : m_engine.getAclManager().getPermissions( page );
-        if ( page == null ||  acl == null || acl.isEmpty() )
-        {
-            fireEvent( WikiSecurityEvent.ACCESS_ALLOWED, user, permission );
-            return true;
-        }
-
-        //
-        //  Next, iterate through the Principal objects assigned
-        //  this permission. If the context's subject possesses
-        //  any of these, the action is allowed.
-
-        final Principal[] aclPrincipals = acl.findPrincipals( permission );
-
-        log.debug( "Checking ACL entries..." );
-        log.debug( "Acl for this page is: " + acl );
-        log.debug( "Checking for principal: " + Arrays.toString( aclPrincipals ) );
-        log.debug( "Permission: " + permission );
-
-        for( Principal aclPrincipal : aclPrincipals )
-        {
-            // If the ACL principal we're looking at is unresolved,
-            // try to resolve it here & correct the Acl
-            if ( aclPrincipal instanceof UnresolvedPrincipal )
-            {
-                final AclEntry aclEntry = acl.getEntry( aclPrincipal );
-                aclPrincipal = resolvePrincipal( aclPrincipal.getName() );
-                if ( aclEntry != null && !( aclPrincipal instanceof UnresolvedPrincipal ) )
-                {
-                    aclEntry.setPrincipal( aclPrincipal );
-                }
-            }
-
-            if ( hasRoleOrPrincipal( session, aclPrincipal ) )
-            {
-                fireEvent( WikiSecurityEvent.ACCESS_ALLOWED, user, permission );
-                return true;
-            }
-        }
-        fireEvent( WikiSecurityEvent.ACCESS_DENIED, user, permission );
-        return false;
-    }
+    boolean checkPermission( WikiSession session, Permission permission );
 
     /**
-     * <p>Determines if the Subject associated with a
-     * supplied WikiSession contains a desired Role or GroupPrincipal.
-     * The algorithm simply checks to see if the Subject possesses
-     * the Role or GroupPrincipal it in its Principal set. Note that
-     * any user (anonymous, asserted, authenticated) can possess
-     * a built-in role. But a user <em>must</em> be authenticated to
-     * possess a role other than one of the built-in ones.
-     * We do this to prevent privilege escalation.</p>
+     * <p>Determines if the Subject associated with a supplied WikiSession contains a desired Role or GroupPrincipal. The algorithm
+     * simply checks to see if the Subject possesses the Role or GroupPrincipal it in its Principal set. Note that any user (anonymous,
+     * asserted, authenticated) can possess a built-in role. But a user <em>must</em> be authenticated to possess a role other than one
+     * of the built-in ones. We do this to prevent privilege escalation.</p>
      * <p>For all other cases, this method returns <code>false</code>.</p>
-     * <p>Note that this method does <em>not</em> consult the external
-     * Authorizer or GroupManager; it relies on the Principals that
-     * have been injected into the user's Subject at login time, or
-     * after group creation/modification/deletion.</p>
-     * @param session the current wiki session, which must be non-null. If null,
-     *            the result of this method always returns <code>false</code>
-     * @param principal the Principal (role or group principal) to look
-     *            for, which must be non-<code>null</code>. If <code>null</code>,
-     *            the result of this method always returns <code>false</code>
-     * @return <code>true</code> if the Subject supplied with the WikiContext
-     *         posesses the Role or GroupPrincipal, <code>false</code> otherwise
+     * <p>Note that this method does <em>not</em> consult the external Authorizer or GroupManager; it relies on the Principals that
+     * have been injected into the user's Subject at login time, or after group creation/modification/deletion.</p>
+     *
+     * @param session the current wiki session, which must be non-null. If null, the result of this method always returns <code>false</code>
+     * @param principal the Principal (role or group principal) to look for, which must be non-<code>null</code>. If <code>null</code>,
+     *                  the result of this method always returns <code>false</code>
+     * @return <code>true</code> if the Subject supplied with the WikiContext posesses the Role or GroupPrincipal, <code>false</code> otherwise
      */
-    public boolean isUserInRole( final WikiSession session, final Principal principal )
-    {
-        if ( session == null || principal == null ||
-             AuthenticationManager.isUserPrincipal( principal ) )
-        {
+    default boolean isUserInRole( final WikiSession session, final Principal principal ) {
+        if ( session == null || principal == null || AuthenticationManager.isUserPrincipal( principal ) ) {
             return false;
         }
 
         // Any type of user can possess a built-in role
-        if ( principal instanceof Role && Role.isBuiltInRole( (Role)principal ) )
-        {
+        if ( principal instanceof Role && Role.isBuiltInRole( (Role)principal ) ) {
             return session.hasPrincipal( principal );
         }
 
         // Only authenticated users can possess groups or custom roles
-        if ( session.isAuthenticated() && AuthenticationManager.isRolePrincipal( principal ) )
-        {
+        if ( session.isAuthenticated() && AuthenticationManager.isRolePrincipal( principal ) ) {
             return session.hasPrincipal( principal );
         }
         return false;
     }
 
     /**
-     * Returns the current external {@link Authorizer} in use. This method
-     * is guaranteed to return a properly-initialized Authorizer, unless
-     * it could not be initialized. In that case, this method throws
-     * a {@link org.apache.wiki.auth.WikiSecurityException}.
-     * @throws org.apache.wiki.auth.WikiSecurityException if the Authorizer could
-     * not be initialized
+     * Returns the current external {@link Authorizer} in use. This method is guaranteed to return a properly-initialized Authorizer, unless
+     * it could not be initialized. In that case, this method throws a {@link org.apache.wiki.auth.WikiSecurityException}.
+     *
+     * @throws org.apache.wiki.auth.WikiSecurityException if the Authorizer could not be initialized
      * @return the current Authorizer
      */
-    public Authorizer getAuthorizer() throws WikiSecurityException
-    {
-        if ( m_authorizer != null )
-        {
-            return m_authorizer;
-        }
-        throw new WikiSecurityException( "Authorizer did not initialize properly. Check the logs." );
-    }
-
-    /**
-     * <p>Determines if the Subject associated with a supplied WikiSession contains
-     * a desired user Principal or built-in Role principal, OR is a member a
-     * Group or external Role. The rules are as follows:</p>
-     * <ol>
-     * <li>First, if desired Principal is a Role or GroupPrincipal, delegate to
-     * {@link #isUserInRole(WikiSession, Principal)} and
-     * return the result.</li>
-     * <li>Otherwise, we're looking for a user Principal,
-     * so iterate through the Principal set and see if
-     * any share the same name as the one we are looking for.</li>
-     * </ol>
-     * <p><em>Note: if the Principal parameter is a user principal, the session
-     * must be authenticated in order for the user to "possess it". Anonymous
-     * or asserted sessions will never posseess a named user principal.</em></p>
-     * @param session the current wiki session, which must be non-null. If null,
-     *            the result of this method always returns <code>false</code>
-     * @param principal the Principal (role, group, or user principal) to look
-     *            for, which must be non-null. If null, the result of this
-     *            method always returns <code>false</code>
-     * @return <code>true</code> if the Subject supplied with the WikiContext
-     *         posesses the Role, GroupPrincipal or desired
-     *         user Principal, <code>false</code> otherwise
-     */
-    protected boolean hasRoleOrPrincipal( final WikiSession session, final Principal principal )
-    {
-        // If either parameter is null, always deny
-        if( session == null || principal == null )
-        {
-            return false;
-        }
-
-        // If principal is role, delegate to isUserInRole
-        if( AuthenticationManager.isRolePrincipal( principal ) )
-        {
-            return isUserInRole( session, principal );
-        }
-
-        // We must be looking for a user principal, assuming that the user
-        // has been properly logged in.
-        // So just look for a name match.
-        if( session.isAuthenticated() && AuthenticationManager.isUserPrincipal( principal ) )
-        {
-            final String principalName = principal.getName();
-            final Principal[] userPrincipals = session.getPrincipals();
-            for( final Principal userPrincipal : userPrincipals )
-            {
-                if( userPrincipal.getName().equals( principalName ) )
-                {
-                    return true;
-                }
-            }
-        }
-        return false;
-    }
+    Authorizer getAuthorizer() throws WikiSecurityException;
 
     /**
-     * Checks whether the current user has access to the wiki context,
-     * by obtaining the required Permission ({@link WikiContext#requiredPermission()})
-     * and delegating the access check to {@link #checkPermission(WikiSession, Permission)}.
-     * If the user is allowed, this method returns <code>true</code>;
-     * <code>false</code> otherwise. If access is allowed,
-     * the wiki context will be added to the request as an attribute
-     * with the key name {@link org.apache.wiki.WikiContext#ATTR_CONTEXT}.
-     * Note that this method will automatically redirect the user to
-     * a login or error page, as appropriate, if access fails. This is
-     * NOT guaranteed to be default behavior in the future.
+     * Checks whether the current user has access to the wiki context, by obtaining the required Permission ({@link WikiContext#requiredPermission()})
+     * and delegating the access check to {@link #checkPermission(WikiSession, Permission)}. If the user is allowed, this method returns
+     * <code>true</code>; <code>false</code> otherwise. If access is allowed, the wiki context will be added to the request as an attribute
+     * with the key name {@link org.apache.wiki.WikiContext#ATTR_CONTEXT}. Note that this method will automatically redirect the user to
+     * a login or error page, as appropriate, if access fails. This is NOT guaranteed to be default behavior in the future.
      *
      * @param context wiki context to check if it is accesible
      * @param response the http response
      * @return the result of the access check
      * @throws IOException In case something goes wrong
      */
-    public boolean hasAccess( final WikiContext context, final HttpServletResponse response ) throws IOException
-    {
+    default boolean hasAccess( final WikiContext context, final HttpServletResponse response ) throws IOException {
         return hasAccess( context, response, true );
     }
 
@@ -411,294 +174,63 @@ public class AuthorizationManager {
      * @return the result of the access check
      * @throws IOException If something goes wrong
      */
-    public boolean hasAccess( final WikiContext context, final HttpServletResponse response, final boolean redirect ) throws IOException {
-        final boolean allowed = checkPermission( context.getWikiSession(), context.requiredPermission() );
-        final ResourceBundle rb = Preferences.getBundle( context, InternationalizationManager.CORE_BUNDLE );
-
-        // Stash the wiki context
-        if ( context.getHttpRequest() != null && context.getHttpRequest().getAttribute( WikiContext.ATTR_CONTEXT ) == null ) {
-            context.getHttpRequest().setAttribute( WikiContext.ATTR_CONTEXT, context );
-        }
-
-        // If access not allowed, redirect
-        if( !allowed && redirect ) {
-            final Principal currentUser  = context.getWikiSession().getUserPrincipal();
-            final String pageurl = context.getPage().getName();
-            if( context.getWikiSession().isAuthenticated() ) {
-                log.info("User "+currentUser.getName()+" has no access - forbidden (permission=" + context.requiredPermission() + ")" );
-                context.getWikiSession().addMessage( MessageFormat.format( rb.getString( "security.error.noaccess.logged" ),
-                                                     context.getName()) );
-            } else {
-                log.info("User "+currentUser.getName()+" has no access - redirecting (permission=" + context.requiredPermission() + ")");
-                context.getWikiSession().addMessage( MessageFormat.format( rb.getString("security.error.noaccess"), context.getName() ) );
-            }
-            response.sendRedirect( m_engine.getURL(WikiContext.LOGIN, pageurl, null ) );
-        }
-        return allowed;
-    }
+    boolean hasAccess( final WikiContext context, final HttpServletResponse response, final boolean redirect ) throws IOException;
 
     /**
-     * Initializes AuthorizationManager with an engine and set of properties.
-     * Expects to find property 'jspwiki.authorizer' with a valid Authorizer
-     * implementation name to take care of role lookup operations.
+     * Initializes AuthorizationManager with an engine and set of properties. Expects to find property 'jspwiki.authorizer' with a valid
+     * Authorizer implementation name to take care of role lookup operations.
+     *
      * @param engine the wiki engine
      * @param properties the set of properties used to initialize the wiki engine
      * @throws WikiException if the AuthorizationManager cannot be initialized
      */
-    public void initialize( final WikiEngine engine, final Properties properties ) throws WikiException {
-        m_engine = engine;
-
-        //
-        //  JAAS authorization continues
-        //
-        m_authorizer = getAuthorizerImplementation( properties );
-        m_authorizer.initialize( engine, properties );
-
-        // Initialize local security policy
-        try {
-            final String policyFileName = properties.getProperty( POLICY, DEFAULT_POLICY );
-            final URL policyURL = engine.findConfigFile( policyFileName );
-
-            if (policyURL != null) {
-                final File policyFile = new File( policyURL.toURI().getPath() );
-                log.info("We found security policy URL: " + policyURL + " and transformed it to file " + policyFile.getAbsolutePath());
-                m_localPolicy = new LocalPolicy( policyFile, engine.getContentEncoding().displayName() );
-                m_localPolicy.refresh();
-                log.info( "Initialized default security policy: " + policyFile.getAbsolutePath() );
-            } else {
-                final String sb = "JSPWiki was unable to initialize the default security policy (WEB-INF/jspwiki.policy) file. " +
-                                  "Please ensure that the jspwiki.policy file exists in the default location. " +
-                		          "This file should exist regardless of the existance of a global policy file. " +
-                                  "The global policy file is identified by the java.security.policy variable. ";
-                final WikiSecurityException wse = new WikiSecurityException( sb );
-                log.fatal( sb, wse );
-                throw wse;
-            }
-        } catch ( final Exception e) {
-            log.error("Could not initialize local security policy: " + e.getMessage() );
-            throw new WikiException( "Could not initialize local security policy: " + e.getMessage(), e );
-        }
-    }
-
-    /**
-     * Attempts to locate and initialize a Authorizer to use with this manager.
-     * Throws a WikiException if no entry is found, or if one fails to
-     * initialize.
-     * @param props jspwiki.properties, containing a
-     *            'jspwiki.authorization.provider' class name
-     * @return a Authorizer used to get page authorization information
-     * @throws WikiException
-     */
-    private Authorizer getAuthorizerImplementation( final Properties props ) throws WikiException {
-        final String authClassName = props.getProperty( PROP_AUTHORIZER, DEFAULT_AUTHORIZER );
-        return ( Authorizer )locateImplementation( authClassName );
-    }
-
-    private Object locateImplementation( final String clazz ) throws WikiException {
-        if ( clazz != null ) {
-            try {
-                final Class< ? > authClass = ClassUtil.findClass( "org.apache.wiki.auth.authorize", clazz );
-                final Object impl = authClass.newInstance();
-                return impl;
-            } catch( final ClassNotFoundException e ) {
-                log.fatal( "Authorizer " + clazz + " cannot be found", e );
-                throw new WikiException( "Authorizer " + clazz + " cannot be found", e );
-            } catch( final InstantiationException e ) {
-                log.fatal( "Authorizer " + clazz + " cannot be created", e );
-                throw new WikiException( "Authorizer " + clazz + " cannot be created", e );
-            } catch( final IllegalAccessException e ) {
-                log.fatal( "You are not allowed to access this authorizer class", e );
-                throw new WikiException( "You are not allowed to access this authorizer class", e );
-            }
-        }
-
-        throw new NoRequiredPropertyException( "Unable to find a " + PROP_AUTHORIZER + " entry in the properties.",
-                                               PROP_AUTHORIZER );
-    }
-
-    /**
-     * Checks to see if the local security policy allows a particular static Permission.
-     * Do not use this method for normal permission checks; use
-     * {@link #checkPermission(WikiSession, Permission)} instead.
-     * @param principals the Principals to check
-     * @param permission the Permission
-     * @return the result
-     */
-    protected boolean allowedByLocalPolicy( final Principal[] principals, final Permission permission )
-    {
-        for ( final Principal principal : principals )
-        {
-            // Get ProtectionDomain for this Principal from cache, or create new one
-            ProtectionDomain pd = m_cachedPds.get( principal );
-            if ( pd == null )
-            {
-                final ClassLoader cl = this.getClass().getClassLoader();
-                final CodeSource cs = new CodeSource( null, (Certificate[])null );
-                pd = new ProtectionDomain( cs, null, cl, new Principal[]{ principal } );
-                m_cachedPds.put( principal, pd );
-            }
-
-            // Consult the local policy and get the answer
-            if ( m_localPolicy.implies( pd, permission ) )
-            {
-                return true;
-            }
-        }
-        return false;
-    }
+    void initialize( final Engine engine, final Properties properties ) throws WikiException;
 
     /**
-     * Determines whether a Subject possesses a given "static" Permission as
-     * defined in the security policy file. This method uses standard Java 2
-     * security calls to do its work. Note that the current access control
-     * context's <code>codeBase</code> is effectively <em>this class</em>,
-     * not that of the caller. Therefore, this method will work best when what
-     * matters in the policy is <em>who</em> makes the permission check, not
-     * what the caller's code source is. Internally, this method works by
-     * executing <code>Subject.doAsPrivileged</code> with a privileged action
-     * that simply calls {@link java.security.AccessController#checkPermission(Permission)}.
-     * @see AccessController#checkPermission(java.security.Permission) . A
-     *       caught exception (or lack thereof) determines whether the privilege
-     *       is absent (or present).
-     * @param session the WikiSession whose permission status is being queried
-     * @param permission the Permission the Subject must possess
-     * @return <code>true</code> if the Subject possesses the permission,
-     *         <code>false</code> otherwise
-     */
-    protected boolean checkStaticPermission( final WikiSession session, final Permission permission )
-    {
-        final Boolean allowed = (Boolean) WikiSession.doPrivileged( session, new PrivilegedAction<Boolean>()
-        {
-            @Override public Boolean run()
-            {
-                try
-                {
-                    // Check the JVM-wide security policy first
-                    AccessController.checkPermission( permission );
-                    return Boolean.TRUE;
-                }
-                catch( final AccessControlException e )
-                {
-                    // Global policy denied the permission
-                }
-
-                // Try the local policy - check each Role/Group and User Principal
-                if ( allowedByLocalPolicy( session.getRoles(), permission ) ||
-                     allowedByLocalPolicy( session.getPrincipals(), permission ) )
-                {
-                    return Boolean.TRUE;
-                }
-                return Boolean.FALSE;
-            }
-        } );
-        return allowed.booleanValue();
-    }
-
-    /**
-     * <p>Given a supplied string representing a Principal's name from an Acl, this
-     * method resolves the correct type of Principal (role, group, or user).
-     * This method is guaranteed to always return a Principal.
-     * The algorithm is straightforward:</p>
+     * <p>Given a supplied string representing a Principal's name from an Acl, this method resolves the correct type of Principal (role,
+     * group, or user). This method is guaranteed to always return a Principal. The algorithm is straightforward:</p>
      * <ol>
-     * <li>If the name matches one of the built-in {@link org.apache.wiki.auth.authorize.Role} names,
-     * return that built-in Role</li>
-     * <li>If the name matches one supplied by the current
-     * {@link org.apache.wiki.auth.Authorizer}, return that Role</li>
-     * <li>If the name matches a group managed by the
-     * current {@link org.apache.wiki.auth.authorize.GroupManager}, return that Group</li>
-     * <li>Otherwise, assume that the name represents a user
-     * principal. Using the current {@link org.apache.wiki.auth.user.UserDatabase}, find the
-     * first user who matches the supplied name by calling
-     * {@link org.apache.wiki.auth.user.UserDatabase#find(String)}.</li>
-     * <li>Finally, if a user cannot be found, manufacture
-     * and return a generic {@link org.apache.wiki.auth.acl.UnresolvedPrincipal}</li>
+     * <li>If the name matches one of the built-in {@link org.apache.wiki.auth.authorize.Role} names, return that built-in Role</li>
+     * <li>If the name matches one supplied by the current {@link org.apache.wiki.auth.Authorizer}, return that Role</li>
+     * <li>If the name matches a group managed by the current {@link org.apache.wiki.auth.authorize.GroupManager}, return that Group</li>
+     * <li>Otherwise, assume that the name represents a user principal. Using the current {@link org.apache.wiki.auth.user.UserDatabase},
+     * find the first user who matches the supplied name by calling {@link org.apache.wiki.auth.user.UserDatabase#find(String)}.</li>
+     * <li>Finally, if a user cannot be found, manufacture and return a generic {@link org.apache.wiki.auth.acl.UnresolvedPrincipal}</li>
      * </ol>
+     *
      * @param name the name of the Principal to resolve
      * @return the fully-resolved Principal
      */
-    public Principal resolvePrincipal( final String name )
-    {
-        // Check built-in Roles first
-        final Role role = new Role(name);
-        if ( Role.isBuiltInRole( role ) )
-        {
-            return role;
-        }
-
-        // Check Authorizer Roles
-        Principal principal = m_authorizer.findRole( name );
-        if ( principal != null )
-        {
-            return principal;
-        }
-
-        // Check Groups
-        principal = m_engine.getGroupManager().findRole( name );
-        if ( principal != null )
-        {
-            return principal;
-        }
-
-        // Ok, no luck---this must be a user principal
-        Principal[] principals = null;
-        UserProfile profile = null;
-        final UserDatabase db = m_engine.getUserManager().getUserDatabase();
-        try
-        {
-            profile = db.find( name );
-            principals = db.getPrincipals( profile.getLoginName() );
-            for (int i = 0; i < principals.length; i++)
-            {
-                principal = principals[i];
-                if ( principal.getName().equals( name ) )
-                {
-                    return principal;
-                }
-            }
-        }
-        catch( final NoSuchPrincipalException e )
-        {
-            // We couldn't find the user...
-        }
-        // Ok, no luck---mark this as unresolved and move on
-        return new UnresolvedPrincipal( name );
-    }
+    Principal resolvePrincipal( final String name );
 
 
     // events processing .......................................................
 
     /**
      * Registers a WikiEventListener with this instance.
+     *
      * @param listener the event listener
      */
-    public synchronized void addWikiEventListener( final WikiEventListener listener )
-    {
-        WikiEventManager.addWikiEventListener( this, listener );
-    }
+    void addWikiEventListener( WikiEventListener listener );
 
     /**
      * Un-registers a WikiEventListener with this instance.
+     *
      * @param listener the event listener
      */
-    public synchronized void removeWikiEventListener( final WikiEventListener listener )
-    {
-        WikiEventManager.removeWikiEventListener( this, listener );
-    }
+    void removeWikiEventListener( final WikiEventListener listener );
 
     /**
-     *  Fires a WikiSecurityEvent of the provided type, user,
-     *  and permission to all registered listeners.
+     * Fires a WikiSecurityEvent of the provided type, user, and permission to all registered listeners.
      *
      * @see org.apache.wiki.event.WikiSecurityEvent
      * @param type        the event type to be fired
      * @param user        the user associated with the event
      * @param permission  the permission the subject must possess
      */
-    protected void fireEvent( final int type, final Principal user, final Object permission )
-    {
-        if ( WikiEventManager.isListening(this) )
-        {
-            WikiEventManager.fireEvent(this,new WikiSecurityEvent(this,type,user,permission));
+    default void fireEvent( final int type, final Principal user, final Object permission ) {
+        if( WikiEventManager.isListening( this ) ) {
+            WikiEventManager.fireEvent( this, new WikiSecurityEvent( this, type, user, permission ) );
         }
     }
 
diff --git a/jspwiki-main/src/main/java/org/apache/wiki/auth/Authorizer.java b/jspwiki-main/src/main/java/org/apache/wiki/auth/Authorizer.java
index 36d44c2..13f82c2 100644
--- a/jspwiki-main/src/main/java/org/apache/wiki/auth/Authorizer.java
+++ b/jspwiki-main/src/main/java/org/apache/wiki/auth/Authorizer.java
@@ -18,11 +18,12 @@
  */
 package org.apache.wiki.auth;
 
+import org.apache.wiki.WikiSession;
+import org.apache.wiki.api.core.Engine;
+
 import java.security.Principal;
 import java.util.Properties;
 
-import org.apache.wiki.WikiEngine;
-import org.apache.wiki.WikiSession;
 
 /**
  * Interface for service providers of authorization information. After a user
@@ -43,8 +44,7 @@ import org.apache.wiki.WikiSession;
  * 
  * @since 2.3
  */
-public interface Authorizer
-{
+public interface Authorizer {
 
     /**
      * Returns an array of role Principals this Authorizer knows about. This
@@ -74,7 +74,7 @@ public interface Authorizer
      * @param props the wiki engine initialization properties
      * @throws WikiSecurityException if the Authorizer could not be initialized
      */
-    void initialize( WikiEngine engine, Properties props ) throws WikiSecurityException;
+    void initialize( Engine engine, Properties props ) throws WikiSecurityException;
 
     /**
      * Determines whether the Subject associated with a WikiSession is in a
diff --git a/jspwiki-main/src/main/java/org/apache/wiki/auth/DefaultAuthorizationManager.java b/jspwiki-main/src/main/java/org/apache/wiki/auth/DefaultAuthorizationManager.java
new file mode 100644
index 0000000..a388a08
--- /dev/null
+++ b/jspwiki-main/src/main/java/org/apache/wiki/auth/DefaultAuthorizationManager.java
@@ -0,0 +1,425 @@
+/*
+    Licensed to the Apache Software Foundation (ASF) under one
+    or more contributor license agreements.  See the NOTICE file
+    distributed with this work for additional information
+    regarding copyright ownership.  The ASF licenses this file
+    to you under the Apache License, Version 2.0 (the
+    "License"); you may not use this file except in compliance
+    with the License.  You may obtain a copy of the License at
+
+       http://www.apache.org/licenses/LICENSE-2.0
+
+    Unless required by applicable law or agreed to in writing,
+    software distributed under the License is distributed on an
+    "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+    KIND, either express or implied.  See the License for the
+    specific language governing permissions and limitations
+    under the License.
+ */
+package org.apache.wiki.auth;
+
+import org.apache.log4j.Logger;
+import org.apache.wiki.WikiContext;
+import org.apache.wiki.WikiPage;
+import org.apache.wiki.WikiSession;
+import org.apache.wiki.api.core.Engine;
+import org.apache.wiki.api.exceptions.NoRequiredPropertyException;
+import org.apache.wiki.api.exceptions.WikiException;
+import org.apache.wiki.auth.acl.Acl;
+import org.apache.wiki.auth.acl.AclEntry;
+import org.apache.wiki.auth.acl.AclManager;
+import org.apache.wiki.auth.acl.UnresolvedPrincipal;
+import org.apache.wiki.auth.authorize.GroupManager;
+import org.apache.wiki.auth.authorize.Role;
+import org.apache.wiki.auth.permissions.AllPermission;
+import org.apache.wiki.auth.permissions.PagePermission;
+import org.apache.wiki.auth.user.UserDatabase;
+import org.apache.wiki.auth.user.UserProfile;
+import org.apache.wiki.event.WikiEventListener;
+import org.apache.wiki.event.WikiEventManager;
+import org.apache.wiki.event.WikiSecurityEvent;
+import org.apache.wiki.i18n.InternationalizationManager;
+import org.apache.wiki.pages.PageManager;
+import org.apache.wiki.preferences.Preferences;
+import org.apache.wiki.util.ClassUtil;
+import org.freshcookies.security.policy.LocalPolicy;
+
+import javax.servlet.http.HttpServletResponse;
+import java.io.File;
+import java.io.IOException;
+import java.net.URL;
+import java.security.AccessControlException;
+import java.security.AccessController;
+import java.security.CodeSource;
+import java.security.Permission;
+import java.security.Principal;
+import java.security.PrivilegedAction;
+import java.security.ProtectionDomain;
+import java.security.cert.Certificate;
+import java.text.MessageFormat;
+import java.util.Arrays;
+import java.util.Map;
+import java.util.Properties;
+import java.util.ResourceBundle;
+import java.util.WeakHashMap;
+
+
+/**
+ * <p>Default implementation for {@link AuthorizationManager}</p>
+ * {@inheritDoc}
+ *
+ * <p>See the {@link #checkPermission(WikiSession, Permission)} and {@link #hasRoleOrPrincipal(WikiSession, Principal)} methods for more
+ * information on the authorization logic.</p>
+ * @since 2.3
+ * @see AuthenticationManager
+ */
+public class DefaultAuthorizationManager implements AuthorizationManager {
+
+    private static final Logger log = Logger.getLogger( DefaultAuthorizationManager.class );
+
+    private Authorizer m_authorizer = null;
+
+    /** Cache for storing ProtectionDomains used to evaluate the local policy. */
+    private Map< Principal, ProtectionDomain > m_cachedPds = new WeakHashMap<>();
+
+    private Engine m_engine = null;
+
+    private LocalPolicy m_localPolicy = null;
+
+    /**
+     * Constructs a new DefaultAuthorizationManager instance.
+     */
+    public DefaultAuthorizationManager() {
+    }
+
+    /** {@inheritDoc} */
+    @Override
+    public boolean checkPermission( final WikiSession session, final Permission permission ) {
+        // A slight sanity check.
+        if( session == null || permission == null ) {
+            fireEvent( WikiSecurityEvent.ACCESS_DENIED, null, permission );
+            return false;
+        }
+
+        final Principal user = session.getLoginPrincipal();
+
+        // Always allow the action if user has AllPermission
+        final Permission allPermission = new AllPermission( m_engine.getApplicationName() );
+        final boolean hasAllPermission = checkStaticPermission( session, allPermission );
+        if( hasAllPermission ) {
+            fireEvent( WikiSecurityEvent.ACCESS_ALLOWED, user, permission );
+            return true;
+        }
+
+        // If the user doesn't have *at least* the permission granted by policy, return false.
+        final boolean hasPolicyPermission = checkStaticPermission( session, permission );
+        if( !hasPolicyPermission ) {
+            fireEvent( WikiSecurityEvent.ACCESS_DENIED, user, permission );
+            return false;
+        }
+
+        // If this isn't a PagePermission, it's allowed
+        if( !( permission instanceof PagePermission ) ) {
+            fireEvent( WikiSecurityEvent.ACCESS_ALLOWED, user, permission );
+            return true;
+        }
+
+        // If the page or ACL is null, it's allowed.
+        final String pageName = ((PagePermission)permission).getPage();
+        final WikiPage page = m_engine.getManager( PageManager.class ).getPage( pageName );
+        final Acl acl = ( page == null) ? null : m_engine.getManager( AclManager.class ).getPermissions( page );
+        if( page == null ||  acl == null || acl.isEmpty() ) {
+            fireEvent( WikiSecurityEvent.ACCESS_ALLOWED, user, permission );
+            return true;
+        }
+
+        // Next, iterate through the Principal objects assigned this permission. If the context's subject possesses
+        // any of these, the action is allowed.
+        final Principal[] aclPrincipals = acl.findPrincipals( permission );
+
+        log.debug( "Checking ACL entries..." );
+        log.debug( "Acl for this page is: " + acl );
+        log.debug( "Checking for principal: " + Arrays.toString( aclPrincipals ) );
+        log.debug( "Permission: " + permission );
+
+        for( Principal aclPrincipal : aclPrincipals ) {
+            // If the ACL principal we're looking at is unresolved, try to resolve it here & correct the Acl
+            if ( aclPrincipal instanceof UnresolvedPrincipal ) {
+                final AclEntry aclEntry = acl.getEntry( aclPrincipal );
+                aclPrincipal = resolvePrincipal( aclPrincipal.getName() );
+                if ( aclEntry != null && !( aclPrincipal instanceof UnresolvedPrincipal ) ) {
+                    aclEntry.setPrincipal( aclPrincipal );
+                }
+            }
+
+            if ( hasRoleOrPrincipal( session, aclPrincipal ) ) {
+                fireEvent( WikiSecurityEvent.ACCESS_ALLOWED, user, permission );
+                return true;
+            }
+        }
+        fireEvent( WikiSecurityEvent.ACCESS_DENIED, user, permission );
+        return false;
+    }
+
+    /** {@inheritDoc} */
+    @Override
+    public Authorizer getAuthorizer() throws WikiSecurityException {
+        if ( m_authorizer != null ) {
+            return m_authorizer;
+        }
+        throw new WikiSecurityException( "Authorizer did not initialize properly. Check the logs." );
+    }
+
+    /**
+     * <p>Determines if the Subject associated with a supplied WikiSession contains a desired user Principal or built-in Role principal,
+     * OR is a member a Group or external Role. The rules are as follows:</p>
+     * <ol>
+     * <li>First, if desired Principal is a Role or GroupPrincipal, delegate to {@link #isUserInRole(WikiSession, Principal)} and
+     * return the result.</li>
+     * <li>Otherwise, we're looking for a user Principal, so iterate through the Principal set and see if any share the same name as the
+     * one we are looking for.</li>
+     * </ol>
+     * <p><em>Note: if the Principal parameter is a user principal, the session must be authenticated in order for the user to "possess it".
+     * Anonymous or asserted sessions will never posseess a named user principal.</em></p>
+     *
+     * @param session the current wiki session, which must be non-null. If null, the result of this method always returns <code>false</code>
+     * @param principal the Principal (role, group, or user principal) to look for, which must be non-null. If null, the result of this
+     *                  method always returns <code>false</code>
+     * @return <code>true</code> if the Subject supplied with the WikiContext posesses the Role, GroupPrincipal or desired
+     *         user Principal, <code>false</code> otherwise
+     */
+    protected boolean hasRoleOrPrincipal( final WikiSession session, final Principal principal ) {
+        // If either parameter is null, always deny
+        if( session == null || principal == null ) {
+            return false;
+        }
+
+        // If principal is role, delegate to isUserInRole
+        if( AuthenticationManager.isRolePrincipal( principal ) ) {
+            return isUserInRole( session, principal );
+        }
+
+        // We must be looking for a user principal, assuming that the user has been properly logged in. So just look for a name match.
+        if( session.isAuthenticated() && AuthenticationManager.isUserPrincipal( principal ) ) {
+            final String principalName = principal.getName();
+            final Principal[] userPrincipals = session.getPrincipals();
+            for( final Principal userPrincipal : userPrincipals ) {
+                if( userPrincipal.getName().equals( principalName ) ) {
+                    return true;
+                }
+            }
+        }
+        return false;
+    }
+
+    /** {@inheritDoc} */
+    @Override
+    public boolean hasAccess( final WikiContext context, final HttpServletResponse response, final boolean redirect ) throws IOException {
+        final boolean allowed = checkPermission( context.getWikiSession(), context.requiredPermission() );
+        final ResourceBundle rb = Preferences.getBundle( context, InternationalizationManager.CORE_BUNDLE );
+
+        // Stash the wiki context
+        if ( context.getHttpRequest() != null && context.getHttpRequest().getAttribute( WikiContext.ATTR_CONTEXT ) == null ) {
+            context.getHttpRequest().setAttribute( WikiContext.ATTR_CONTEXT, context );
+        }
+
+        // If access not allowed, redirect
+        if( !allowed && redirect ) {
+            final Principal currentUser  = context.getWikiSession().getUserPrincipal();
+            final String pageurl = context.getPage().getName();
+            if( context.getWikiSession().isAuthenticated() ) {
+                log.info( "User " + currentUser.getName() + " has no access - forbidden (permission=" + context.requiredPermission() + ")" );
+                context.getWikiSession().addMessage( MessageFormat.format( rb.getString( "security.error.noaccess.logged" ),
+                                                     context.getName()) );
+            } else {
+                log.info( "User " + currentUser.getName() + " has no access - redirecting (permission=" + context.requiredPermission() + ")" );
+                context.getWikiSession().addMessage( MessageFormat.format( rb.getString("security.error.noaccess"), context.getName() ) );
+            }
+            response.sendRedirect( m_engine.getURL(WikiContext.LOGIN, pageurl, null ) );
+        }
+        return allowed;
+    }
+
+    /** {@inheritDoc} */
+    @Override
+    public void initialize( final Engine engine, final Properties properties ) throws WikiException {
+        m_engine = engine;
+
+        //  JAAS authorization continues
+        m_authorizer = getAuthorizerImplementation( properties );
+        m_authorizer.initialize( engine, properties );
+
+        // Initialize local security policy
+        try {
+            final String policyFileName = properties.getProperty( POLICY, DEFAULT_POLICY );
+            final URL policyURL = engine.findConfigFile( policyFileName );
+
+            if (policyURL != null) {
+                final File policyFile = new File( policyURL.toURI().getPath() );
+                log.info("We found security policy URL: " + policyURL + " and transformed it to file " + policyFile.getAbsolutePath());
+                m_localPolicy = new LocalPolicy( policyFile, engine.getContentEncoding().displayName() );
+                m_localPolicy.refresh();
+                log.info( "Initialized default security policy: " + policyFile.getAbsolutePath() );
+            } else {
+                final String sb = "JSPWiki was unable to initialize the default security policy (WEB-INF/jspwiki.policy) file. " +
+                                  "Please ensure that the jspwiki.policy file exists in the default location. " +
+                		          "This file should exist regardless of the existance of a global policy file. " +
+                                  "The global policy file is identified by the java.security.policy variable. ";
+                final WikiSecurityException wse = new WikiSecurityException( sb );
+                log.fatal( sb, wse );
+                throw wse;
+            }
+        } catch ( final Exception e) {
+            log.error("Could not initialize local security policy: " + e.getMessage() );
+            throw new WikiException( "Could not initialize local security policy: " + e.getMessage(), e );
+        }
+    }
+
+    /**
+     * Attempts to locate and initialize a Authorizer to use with this manager. Throws a WikiException if no entry is found, or if one
+     * fails to initialize.
+     *
+     * @param props jspwiki.properties, containing a 'jspwiki.authorization.provider' class name.
+     * @return a Authorizer used to get page authorization information.
+     * @throws WikiException if there are problems finding the authorizer implementation.
+     */
+    private Authorizer getAuthorizerImplementation( final Properties props ) throws WikiException {
+        final String authClassName = props.getProperty( PROP_AUTHORIZER, DEFAULT_AUTHORIZER );
+        return ( Authorizer )locateImplementation( authClassName );
+    }
+
+    private Object locateImplementation( final String clazz ) throws WikiException {
+        if ( clazz != null ) {
+            try {
+                final Class< ? > authClass = ClassUtil.findClass( "org.apache.wiki.auth.authorize", clazz );
+                return authClass.newInstance();
+            } catch( final ClassNotFoundException e ) {
+                log.fatal( "Authorizer " + clazz + " cannot be found", e );
+                throw new WikiException( "Authorizer " + clazz + " cannot be found", e );
+            } catch( final InstantiationException e ) {
+                log.fatal( "Authorizer " + clazz + " cannot be created", e );
+                throw new WikiException( "Authorizer " + clazz + " cannot be created", e );
+            } catch( final IllegalAccessException e ) {
+                log.fatal( "You are not allowed to access this authorizer class", e );
+                throw new WikiException( "You are not allowed to access this authorizer class", e );
+            }
+        }
+
+        throw new NoRequiredPropertyException( "Unable to find a " + PROP_AUTHORIZER + " entry in the properties.", PROP_AUTHORIZER );
+    }
+
+    /**
+     * Checks to see if the local security policy allows a particular static Permission.
+     * Do not use this method for normal permission checks; use {@link #checkPermission(WikiSession, Permission)} instead.
+     *
+     * @param principals the Principals to check
+     * @param permission the Permission
+     * @return the result
+     */
+    protected boolean allowedByLocalPolicy( final Principal[] principals, final Permission permission ) {
+        for ( final Principal principal : principals ) {
+            // Get ProtectionDomain for this Principal from cache, or create new one
+            ProtectionDomain pd = m_cachedPds.get( principal );
+            if ( pd == null ) {
+                final ClassLoader cl = this.getClass().getClassLoader();
+                final CodeSource cs = new CodeSource( null, (Certificate[])null );
+                pd = new ProtectionDomain( cs, null, cl, new Principal[]{ principal } );
+                m_cachedPds.put( principal, pd );
+            }
+
+            // Consult the local policy and get the answer
+            if ( m_localPolicy.implies( pd, permission ) ) {
+                return true;
+            }
+        }
+        return false;
+    }
+
+    /**
+     * Determines whether a Subject possesses a given "static" Permission as defined in the security policy file. This method uses standard
+     * Java 2 security calls to do its work. Note that the current access control context's <code>codeBase</code> is effectively <em>this
+     * class</em>, not that of the caller. Therefore, this method will work best when what matters in the policy is <em>who</em> makes the
+     * permission check, not what the caller's code source is. Internally, this method works by executing <code>Subject.doAsPrivileged</code>
+     * with a privileged action that simply calls {@link AccessController#checkPermission(Permission)}.
+     *
+     * @see AccessController#checkPermission(Permission) . A caught exception (or lack thereof) determines whether the
+     *       privilege is absent (or present).
+     * @param session the WikiSession whose permission status is being queried
+     * @param permission the Permission the Subject must possess
+     * @return <code>true</code> if the Subject possesses the permission, <code>false</code> otherwise
+     */
+    protected boolean checkStaticPermission( final WikiSession session, final Permission permission ) {
+        return ( Boolean )WikiSession.doPrivileged( session, ( PrivilegedAction< Boolean > )() -> {
+            try {
+                // Check the JVM-wide security policy first
+                AccessController.checkPermission( permission );
+                return Boolean.TRUE;
+            } catch( final AccessControlException e ) {
+                // Global policy denied the permission
+            }
+
+            // Try the local policy - check each Role/Group and User Principal
+            if ( allowedByLocalPolicy( session.getRoles(), permission ) || allowedByLocalPolicy( session.getPrincipals(), permission ) ) {
+                return Boolean.TRUE;
+            }
+            return Boolean.FALSE;
+        } );
+    }
+
+    /** {@inheritDoc} */
+    @Override
+    public Principal resolvePrincipal( final String name ) {
+        // Check built-in Roles first
+        final Role role = new Role(name);
+        if ( Role.isBuiltInRole( role ) ) {
+            return role;
+        }
+
+        // Check Authorizer Roles
+        Principal principal = m_authorizer.findRole( name );
+        if ( principal != null ) {
+            return principal;
+        }
+
+        // Check Groups
+        principal = m_engine.getManager( GroupManager.class ).findRole( name );
+        if ( principal != null ) {
+            return principal;
+        }
+
+        // Ok, no luck---this must be a user principal
+        final Principal[] principals;
+        final UserProfile profile;
+        final UserDatabase db = m_engine.getManager( UserManager.class ).getUserDatabase();
+        try {
+            profile = db.find( name );
+            principals = db.getPrincipals( profile.getLoginName() );
+            for( final Principal value : principals ) {
+                principal = value;
+                if( principal.getName().equals( name ) ) {
+                    return principal;
+                }
+            }
+        } catch( final NoSuchPrincipalException e ) {
+            // We couldn't find the user...
+        }
+        // Ok, no luck---mark this as unresolved and move on
+        return new UnresolvedPrincipal( name );
+    }
+
+
+    // events processing .......................................................
+
+    /** {@inheritDoc} */
+    @Override
+    public synchronized void addWikiEventListener( final WikiEventListener listener ) {
+        WikiEventManager.addWikiEventListener( this, listener );
+    }
+
+    /** {@inheritDoc} */
+    @Override
+    public synchronized void removeWikiEventListener( final WikiEventListener listener ) {
+        WikiEventManager.removeWikiEventListener( this, listener );
+    }
+
+}
diff --git a/jspwiki-main/src/main/java/org/apache/wiki/auth/authorize/GroupDatabase.java b/jspwiki-main/src/main/java/org/apache/wiki/auth/authorize/GroupDatabase.java
index ca03266..579036a 100644
--- a/jspwiki-main/src/main/java/org/apache/wiki/auth/authorize/GroupDatabase.java
+++ b/jspwiki-main/src/main/java/org/apache/wiki/auth/authorize/GroupDatabase.java
@@ -18,7 +18,7 @@
  */
 package org.apache.wiki.auth.authorize;
 
-import org.apache.wiki.WikiEngine;
+import org.apache.wiki.api.core.Engine;
 import org.apache.wiki.api.exceptions.NoRequiredPropertyException;
 import org.apache.wiki.auth.WikiSecurityException;
 
@@ -50,7 +50,7 @@ public interface GroupDatabase {
      * @throws WikiSecurityException if the database could not be initialized successfully
      * @throws NoRequiredPropertyException if a required property is not present
      */
-    void initialize( WikiEngine engine, Properties props ) throws NoRequiredPropertyException, WikiSecurityException;
+    void initialize( Engine engine, Properties props ) throws NoRequiredPropertyException, WikiSecurityException;
 
     /**
      * Saves a Group to the group database. Note that this method <em>must</em>
diff --git a/jspwiki-main/src/main/java/org/apache/wiki/auth/authorize/GroupManager.java b/jspwiki-main/src/main/java/org/apache/wiki/auth/authorize/GroupManager.java
index 1f3bc60..37e3c33 100644
--- a/jspwiki-main/src/main/java/org/apache/wiki/auth/authorize/GroupManager.java
+++ b/jspwiki-main/src/main/java/org/apache/wiki/auth/authorize/GroupManager.java
@@ -21,14 +21,15 @@ package org.apache.wiki.auth.authorize;
 import org.apache.commons.lang3.ArrayUtils;
 import org.apache.log4j.Logger;
 import org.apache.wiki.WikiContext;
-import org.apache.wiki.WikiEngine;
 import org.apache.wiki.WikiSession;
+import org.apache.wiki.api.core.Engine;
 import org.apache.wiki.api.exceptions.NoRequiredPropertyException;
 import org.apache.wiki.api.exceptions.WikiException;
 import org.apache.wiki.auth.AuthenticationManager;
 import org.apache.wiki.auth.Authorizer;
 import org.apache.wiki.auth.GroupPrincipal;
 import org.apache.wiki.auth.NoSuchPrincipalException;
+import org.apache.wiki.auth.UserManager;
 import org.apache.wiki.auth.WikiPrincipal;
 import org.apache.wiki.auth.WikiSecurityException;
 import org.apache.wiki.auth.user.UserProfile;
@@ -67,38 +68,33 @@ import java.util.StringTokenizer;
 public class GroupManager implements Authorizer, WikiEventListener {
 
     /** Key used for adding UI messages to a user's WikiSession. */
-    public static final String  MESSAGES_KEY       = "group";
+    public static final String  MESSAGES_KEY = "group";
 
     private static final String PROP_GROUPDATABASE = "jspwiki.groupdatabase";
 
-    static final Logger         log                = Logger.getLogger( GroupManager.class );
+    private static final Logger log = Logger.getLogger( GroupManager.class );
 
-    protected WikiEngine        m_engine;
+    protected Engine m_engine;
 
     protected WikiEventListener m_groupListener;
 
     private GroupDatabase       m_groupDatabase    = null;
 
     /** Map with GroupPrincipals as keys, and Groups as values */
-    private final Map<Principal, Group>           m_groups           = new HashMap<Principal, Group>();
+    private final Map< Principal, Group > m_groups = new HashMap<>();
 
     /**
      * <p>
-     * Returns a GroupPrincipal matching a given name. If a group cannot be
-     * found, return <code>null</code>.
+     * Returns a GroupPrincipal matching a given name. If a group cannot be found, return <code>null</code>.
      * </p>
      * @param name Name of the group. This is case-sensitive.
      * @return A DefaultGroup instance.
      */
-    public Principal findRole( String name )
-    {
-        try
-        {
-            Group group = getGroup( name );
+    @Override public Principal findRole( final String name ) {
+        try {
+            final Group group = getGroup( name );
             return group.getPrincipal();
-        }
-        catch( NoSuchPrincipalException e )
-        {
+        } catch( final NoSuchPrincipalException e ) {
             return null;
         }
     }
@@ -110,9 +106,9 @@ public class GroupManager implements Authorizer, WikiEventListener {
      * @return the group
      * @throws NoSuchPrincipalException if the group cannot be found
      */
-    public Group getGroup( String name ) throws NoSuchPrincipalException
+    public Group getGroup( final String name ) throws NoSuchPrincipalException
     {
-        Group group = m_groups.get( new GroupPrincipal( name ) );
+        final Group group = m_groups.get( new GroupPrincipal( name ) );
         if ( group != null )
         {
             return group;
@@ -143,37 +139,37 @@ public class GroupManager implements Authorizer, WikiEventListener {
         Throwable cause = null;
         try
         {
-            Properties props = m_engine.getWikiProperties();
+            final Properties props = m_engine.getWikiProperties();
             dbClassName = props.getProperty( PROP_GROUPDATABASE );
             if ( dbClassName == null )
             {
                 dbClassName = XMLGroupDatabase.class.getName();
             }
             log.info( "Attempting to load group database class " + dbClassName );
-            Class<?> dbClass = ClassUtil.findClass( "org.apache.wiki.auth.authorize", dbClassName );
+            final Class<?> dbClass = ClassUtil.findClass( "org.apache.wiki.auth.authorize", dbClassName );
             m_groupDatabase = (GroupDatabase) dbClass.newInstance();
             m_groupDatabase.initialize( m_engine, m_engine.getWikiProperties() );
             log.info( "Group database initialized." );
         }
-        catch( ClassNotFoundException e )
+        catch( final ClassNotFoundException e )
         {
             log.error( "GroupDatabase class " + dbClassName + " cannot be found.", e );
             dbInstantiationError = "Failed to locate GroupDatabase class " + dbClassName;
             cause = e;
         }
-        catch( InstantiationException e )
+        catch( final InstantiationException e )
         {
             log.error( "GroupDatabase class " + dbClassName + " cannot be created.", e );
             dbInstantiationError = "Failed to create GroupDatabase class " + dbClassName;
             cause = e;
         }
-        catch( IllegalAccessException e )
+        catch( final IllegalAccessException e )
         {
             log.error( "You are not allowed to access group database class " + dbClassName + ".", e );
             dbInstantiationError = "Access GroupDatabase class " + dbClassName + " denied";
             cause = e;
         }
-        catch( NoRequiredPropertyException e )
+        catch( final NoRequiredPropertyException e )
         {
             log.error( "Missing property: " + e.getMessage() + "." );
             dbInstantiationError = "Missing property: " + e.getMessage();
@@ -195,7 +191,7 @@ public class GroupManager implements Authorizer, WikiEventListener {
      * defensive copy of an internally stored hashmap.
      * @return an array of Principals representing the roles
      */
-    public Principal[] getRoles()
+    @Override public Principal[] getRoles()
     {
         return m_groups.keySet().toArray( new Principal[m_groups.size()] );
     }
@@ -205,12 +201,11 @@ public class GroupManager implements Authorizer, WikiEventListener {
      * obtaining a list of all of the groups it stores.
      * @param engine the wiki engine
      * @param props the properties used to initialize the wiki engine
-     * @see GroupDatabase#initialize(org.apache.wiki.WikiEngine,
-     *      java.util.Properties)
+     * @see GroupDatabase#initialize(org.apache.wiki.api.core.Engine, java.util.Properties)
      * @see GroupDatabase#groups()
      * @throws WikiSecurityException if GroupManager cannot be initialized
      */
-    public void initialize( WikiEngine engine, Properties props ) throws WikiSecurityException
+    @Override public void initialize( final Engine engine, final Properties props ) throws WikiSecurityException
     {
         m_engine = engine;
 
@@ -218,16 +213,16 @@ public class GroupManager implements Authorizer, WikiEventListener {
         {
             m_groupDatabase = getGroupDatabase();
         }
-        catch ( WikiException e )
+        catch ( final WikiException e )
         {
             throw new WikiSecurityException( e.getMessage(), e );
         }
 
         // Load all groups from the database into the cache
-        Group[] groups = m_groupDatabase.groups();
+        final Group[] groups = m_groupDatabase.groups();
         synchronized( m_groups )
         {
-            for( Group group : groups )
+            for( final Group group : groups )
             {
                 // Add new group to cache; fire GROUP_ADD event
                 m_groups.put( group.getPrincipal(), group );
@@ -236,7 +231,7 @@ public class GroupManager implements Authorizer, WikiEventListener {
         }
 
         // Make the GroupManager listen for WikiEvents (WikiSecurityEvents for changed user profiles)
-        engine.getUserManager().addWikiEventListener( this );
+        engine.getManager( UserManager.class ).addWikiEventListener( this );
 
         // Success!
         log.info( "Authorizer GroupManager initialized successfully; loaded " + groups.length + " group(s)." );
@@ -264,7 +259,7 @@ public class GroupManager implements Authorizer, WikiEventListener {
      * @return <code>true</code> if the user is considered to be in the role,
      *         <code>false</code> otherwise
      */
-    public boolean isUserInRole( WikiSession session, Principal role )
+    @Override public boolean isUserInRole( final WikiSession session, final Principal role )
     {
         // Always return false if session/role is null, or if
         // role isn't a GroupPrincipal
@@ -274,14 +269,14 @@ public class GroupManager implements Authorizer, WikiEventListener {
         }
 
         // Get the group we're examining
-        Group group = m_groups.get( role );
+        final Group group = m_groups.get( role );
         if ( group == null )
         {
             return false;
         }
 
         // Check each user principal to see if it belongs to the group
-        for ( Principal principal : session.getPrincipals() )
+        for ( final Principal principal : session.getPrincipals() )
         {
             if ( AuthenticationManager.isUserPrincipal( principal ) && group.isMember( principal ) )
             {
@@ -321,7 +316,7 @@ public class GroupManager implements Authorizer, WikiEventListener {
      * <code>create</code> is <code>false</code>
      * and the Group named <code>name</code> does not exist
      */
-    public Group parseGroup( String name, String memberLine, boolean create ) throws WikiSecurityException
+    public Group parseGroup( String name, String memberLine, final boolean create ) throws WikiSecurityException
     {
         // If null name parameter, it's because someone's creating a new group
         if ( name == null )
@@ -350,22 +345,22 @@ public class GroupManager implements Authorizer, WikiEventListener {
         memberLine = memberLine.trim();
 
         // Create or retrieve the group (may have been previously cached)
-        Group group = new Group( name, m_engine.getApplicationName() );
+        final Group group = new Group( name, m_engine.getApplicationName() );
         try
         {
-            Group existingGroup = getGroup( name );
+            final Group existingGroup = getGroup( name );
 
             // If existing, clone it
             group.setCreator( existingGroup.getCreator() );
             group.setCreated( existingGroup.getCreated() );
             group.setModifier( existingGroup.getModifier() );
             group.setLastModified( existingGroup.getLastModified() );
-            for( Principal existingMember : existingGroup.members() )
+            for( final Principal existingMember : existingGroup.members() )
             {
                 group.add( existingMember );
             }
         }
-        catch( NoSuchPrincipalException e )
+        catch( final NoSuchPrincipalException e )
         {
             // It's a new group.... throw error if we don't create new ones
             if ( !create )
@@ -375,11 +370,11 @@ public class GroupManager implements Authorizer, WikiEventListener {
         }
 
         // If passed members not empty, overwrite
-        String[] members = extractMembers( memberLine );
+        final String[] members = extractMembers( memberLine );
         if ( members.length > 0 )
         {
             group.clear();
-            for( String member : members )
+            for( final String member : members )
             {
                 group.add( new WikiPrincipal( member ) );
             }
@@ -416,16 +411,16 @@ public class GroupManager implements Authorizer, WikiEventListener {
      * <code>create</code> is <code>false</code>
      * and the Group does not exist
      */
-    public Group parseGroup( WikiContext context, boolean create ) throws WikiSecurityException
+    public Group parseGroup( final WikiContext context, final boolean create ) throws WikiSecurityException
     {
         // Extract parameters
-        HttpServletRequest request = context.getHttpRequest();
-        String name = request.getParameter( "group" );
-        String memberLine = request.getParameter( "members" );
+        final HttpServletRequest request = context.getHttpRequest();
+        final String name = request.getParameter( "group" );
+        final String memberLine = request.getParameter( "members" );
 
         // Create the named group; we pass on any NoSuchPrincipalExceptions
         // that may be thrown if create == false, or WikiSecurityExceptions
-        Group group = parseGroup( name, memberLine, create );
+        final Group group = parseGroup( name, memberLine, create );
 
         // If no members, add the current user by default
         if ( group.members().length == 0 )
@@ -449,14 +444,14 @@ public class GroupManager implements Authorizer, WikiEventListener {
      * the back-end
      * @see org.apache.wiki.auth.authorize.GroupDatabase#delete(Group)
      */
-    public void removeGroup( String index ) throws WikiSecurityException
+    public void removeGroup( final String index ) throws WikiSecurityException
     {
         if ( index == null )
         {
             throw new IllegalArgumentException( "Group cannot be null." );
         }
 
-        Group group = m_groups.get( new GroupPrincipal( index ) );
+        final Group group = m_groups.get( new GroupPrincipal( index ) );
         if ( group == null )
         {
             throw new NoSuchPrincipalException( "Group " + index + " not found" );
@@ -512,12 +507,12 @@ public class GroupManager implements Authorizer, WikiEventListener {
      * @param group the Group, which may not be <code>null</code>
      * @throws WikiSecurityException if the Group cannot be saved by the back-end
      */
-    public void setGroup( WikiSession session, Group group ) throws WikiSecurityException
+    public void setGroup( final WikiSession session, final Group group ) throws WikiSecurityException
     {
         // TODO: check for appropriate permissions
 
         // If group already exists, delete it; fire GROUP_REMOVE event
-        Group oldGroup = m_groups.get( group.getPrincipal() );
+        final Group oldGroup = m_groups.get( group.getPrincipal() );
         if ( oldGroup != null )
         {
             fireEvent( WikiSecurityEvent.GROUP_REMOVE, oldGroup );
@@ -552,7 +547,7 @@ public class GroupManager implements Authorizer, WikiEventListener {
         }
 
         // We got an exception! Roll back...
-        catch( WikiSecurityException e )
+        catch( final WikiSecurityException e )
         {
             if ( oldGroup != null )
             {
@@ -577,22 +572,22 @@ public class GroupManager implements Authorizer, WikiEventListener {
      * @param context the current wiki context
      * @param group the supplied Group
      */
-    public void validateGroup( WikiContext context, Group group )
+    public void validateGroup( final WikiContext context, final Group group )
     {
-        InputValidator validator = new InputValidator( MESSAGES_KEY, context );
+        final InputValidator validator = new InputValidator( MESSAGES_KEY, context );
 
         // Name cannot be null or one of the restricted names
         try
         {
             checkGroupName( context, group.getName() );
         }
-        catch( WikiSecurityException e )
+        catch( final WikiSecurityException e )
         {
 
         }
 
         // Member names must be "safe" strings
-        Principal[] members = group.members();
+        final Principal[] members = group.members();
         for( int i = 0; i < members.length; i++ )
         {
             validator.validateNotNull( members[i].getName(), "Full name", InputValidator.ID );
@@ -604,15 +599,15 @@ public class GroupManager implements Authorizer, WikiEventListener {
      * @param memberLine the list of members
      * @return the list of members
      */
-    protected String[] extractMembers( String memberLine )
+    protected String[] extractMembers( final String memberLine )
     {
-        Set<String> members = new HashSet<String>();
+        final Set<String> members = new HashSet<>();
         if ( memberLine != null )
         {
-            StringTokenizer tok = new StringTokenizer( memberLine, "\n" );
+            final StringTokenizer tok = new StringTokenizer( memberLine, "\n" );
             while( tok.hasMoreTokens() )
             {
-                String uid = tok.nextToken().trim();
+                final String uid = tok.nextToken().trim();
                 if ( uid != null && uid.length() > 0 )
                 {
                     members.add( uid );
@@ -631,12 +626,12 @@ public class GroupManager implements Authorizer, WikiEventListener {
      * <code>null</code> or the Group name is illegal
      * @see Group#RESTRICTED_GROUPNAMES
      */
-    protected void checkGroupName( WikiContext context, String name ) throws WikiSecurityException
+    protected void checkGroupName( final WikiContext context, final String name ) throws WikiSecurityException
     {
         //TODO: groups cannot have the same name as a user
 
         // Name cannot be null
-        InputValidator validator = new InputValidator( MESSAGES_KEY, context );
+        final InputValidator validator = new InputValidator( MESSAGES_KEY, context );
         validator.validateNotNull( name, "Group name" );
 
         // Name cannot be one of the restricted names either
@@ -654,7 +649,7 @@ public class GroupManager implements Authorizer, WikiEventListener {
      * This is a convenience method.
      * @param listener the event listener
      */
-    public synchronized void addWikiEventListener( WikiEventListener listener )
+    public synchronized void addWikiEventListener( final WikiEventListener listener )
     {
         WikiEventManager.addWikiEventListener( this, listener );
     }
@@ -664,7 +659,7 @@ public class GroupManager implements Authorizer, WikiEventListener {
      * This is a convenience method.
      * @param listener the event listener
      */
-    public synchronized void removeWikiEventListener( WikiEventListener listener )
+    public synchronized void removeWikiEventListener( final WikiEventListener listener )
     {
         WikiEventManager.removeWikiEventListener( this, listener );
     }
@@ -677,7 +672,7 @@ public class GroupManager implements Authorizer, WikiEventListener {
      * @param type       the event type to be fired
      * @param target     the changed Object, which may be <code>null</code>
      */
-    protected void fireEvent( int type, Object target )
+    protected void fireEvent( final int type, final Object target )
     {
         if ( WikiEventManager.isListening(this) )
         {
@@ -693,32 +688,32 @@ public class GroupManager implements Authorizer, WikiEventListener {
      * only the representations of the names within that are changing.
      * @param event the incoming event
      */
-    public void actionPerformed(WikiEvent event)
+    @Override public void actionPerformed( final WikiEvent event)
     {
         if (! ( event instanceof WikiSecurityEvent ) )
         {
             return;
         }
 
-        WikiSecurityEvent se = (WikiSecurityEvent)event;
+        final WikiSecurityEvent se = (WikiSecurityEvent)event;
         if ( se.getType() == WikiSecurityEvent.PROFILE_NAME_CHANGED )
         {
-            WikiSession session = se.getSrc();
-            UserProfile[] profiles = (UserProfile[])se.getTarget();
-            Principal[] oldPrincipals = new Principal[] {
+            final WikiSession session = se.getSrc();
+            final UserProfile[] profiles = (UserProfile[])se.getTarget();
+            final Principal[] oldPrincipals = new Principal[] {
                 new WikiPrincipal( profiles[0].getLoginName() ),
                 new WikiPrincipal( profiles[0].getFullname() ),
                 new WikiPrincipal( profiles[0].getWikiName() ) };
-            Principal newPrincipal = new WikiPrincipal( profiles[1].getFullname() );
+            final Principal newPrincipal = new WikiPrincipal( profiles[1].getFullname() );
 
             // Examine each group
             int groupsChanged = 0;
             try
             {
-                for ( Group group : m_groupDatabase.groups() )
+                for ( final Group group : m_groupDatabase.groups() )
                 {
                     boolean groupChanged = false;
-                    for ( Principal oldPrincipal : oldPrincipals )
+                    for ( final Principal oldPrincipal : oldPrincipals )
                     {
                         if ( group.isMember( oldPrincipal ) )
                         {
@@ -734,7 +729,7 @@ public class GroupManager implements Authorizer, WikiEventListener {
                     }
                 }
             }
-            catch ( WikiException e )
+            catch ( final WikiException e )
             {
                 // Oooo! This is really bad...
                 log.error( "Could not change user name in Group lists because of GroupDatabase error:" + e.getMessage() );
diff --git a/jspwiki-main/src/main/java/org/apache/wiki/auth/authorize/JDBCGroupDatabase.java b/jspwiki-main/src/main/java/org/apache/wiki/auth/authorize/JDBCGroupDatabase.java
index d5ac0d2..1f22cf6 100644
--- a/jspwiki-main/src/main/java/org/apache/wiki/auth/authorize/JDBCGroupDatabase.java
+++ b/jspwiki-main/src/main/java/org/apache/wiki/auth/authorize/JDBCGroupDatabase.java
@@ -18,23 +18,29 @@
  */
 package org.apache.wiki.auth.authorize;
 
-import java.security.Principal;
-import java.sql.*;
-import java.util.*;
-import java.util.Date;
-
-import javax.naming.Context;
-import javax.naming.InitialContext;
-import javax.naming.NamingException;
-import javax.sql.DataSource;
-
 import org.apache.log4j.Logger;
-import org.apache.wiki.WikiEngine;
+import org.apache.wiki.api.core.Engine;
 import org.apache.wiki.api.exceptions.NoRequiredPropertyException;
 import org.apache.wiki.auth.NoSuchPrincipalException;
 import org.apache.wiki.auth.WikiPrincipal;
 import org.apache.wiki.auth.WikiSecurityException;
 
+import javax.naming.Context;
+import javax.naming.InitialContext;
+import javax.naming.NamingException;
+import javax.sql.DataSource;
+import java.security.Principal;
+import java.sql.Connection;
+import java.sql.DatabaseMetaData;
+import java.sql.PreparedStatement;
+import java.sql.ResultSet;
+import java.sql.SQLException;
+import java.sql.Timestamp;
+import java.util.Date;
+import java.util.HashSet;
+import java.util.Properties;
+import java.util.Set;
+
 /**
  * <p>
  * Implementation of GroupDatabase that persists {@link Group} objects to a JDBC
@@ -121,13 +127,8 @@ import org.apache.wiki.auth.WikiSecurityException;
  * </p>
  * <p>
  * JDBCGroupDatabase commits changes as transactions if the back-end database
- * supports them. If the database supports transactions, group changes are saved
- * to permanent storage only when the {@link #commit()} method is called. If the
- * database does <em>not</em> support transactions, then changes are made
- * immediately (during the {@link #save(Group, Principal)} method), and the
- * {@linkplain #commit()} method no-ops. Thus, callers should always call the
- * {@linkplain #commit()} method after saving a profile to guarantee that
- * changes are applied.
+ * supports them. Changes are made
+ * immediately (during the {@link #save(Group, Principal)} method).
  * </p>
  * 
  * @since 2.3
@@ -222,7 +223,7 @@ public class JDBCGroupDatabase implements GroupDatabase {
 
     private boolean m_supportsCommits = false;
 
-    private WikiEngine m_engine = null;
+    private Engine m_engine = null;
 
     /**
      * Looks up and deletes a {@link Group} from the group database. If the
@@ -235,14 +236,14 @@ public class JDBCGroupDatabase implements GroupDatabase {
      *             supplied group (thrown as {@link NoSuchPrincipalException})
      *             or if the commit did not succeed
      */
-    public void delete( Group group ) throws WikiSecurityException
+    @Override public void delete( final Group group ) throws WikiSecurityException
     {
         if( !exists( group ) )
         {
             throw new NoSuchPrincipalException( "Not in database: " + group.getName() );
         }
 
-        String groupName = group.getName();
+        final String groupName = group.getName();
         Connection conn = null;
         PreparedStatement ps = null;
         try
@@ -269,7 +270,7 @@ public class JDBCGroupDatabase implements GroupDatabase {
                 conn.commit();
             }
         }
-        catch( SQLException e )
+        catch( final SQLException e )
         {
         	closeQuietly( conn, ps, null );
             throw new WikiSecurityException( "Could not delete group " + groupName + ": " + e.getMessage(), e );
@@ -291,9 +292,9 @@ public class JDBCGroupDatabase implements GroupDatabase {
      * @throws WikiSecurityException if the groups cannot be returned by the
      *             back-end
      */
-    public Group[] groups() throws WikiSecurityException
+    @Override public Group[] groups() throws WikiSecurityException
     {
-        Set<Group> groups = new HashSet<Group>();
+        final Set<Group> groups = new HashSet<>();
         Connection conn = null;
         PreparedStatement ps = null;
         ResultSet rs = null;
@@ -306,14 +307,14 @@ public class JDBCGroupDatabase implements GroupDatabase {
             rs = ps.executeQuery();
             while ( rs.next() )
             {
-                String groupName = rs.getString( m_name );
+                final String groupName = rs.getString( m_name );
                 if( groupName == null )
                 {
                     log.warn( "Detected null group name in JDBCGroupDataBase. Check your group database." );
                 }
                 else
                 {
-                    Group group = new Group( groupName, m_engine.getApplicationName() );
+                    final Group group = new Group( groupName, m_engine.getApplicationName() );
                     group.setCreated( rs.getTimestamp( m_created ) );
                     group.setCreator( rs.getString( m_creator ) );
                     group.setLastModified( rs.getTimestamp( m_modified ) );
@@ -323,7 +324,7 @@ public class JDBCGroupDatabase implements GroupDatabase {
                 }
             }
         }
-        catch( SQLException e )
+        catch( final SQLException e )
         {
         	closeQuietly( conn, ps, rs );
             throw new WikiSecurityException( e.getMessage(), e );
@@ -346,17 +347,16 @@ public class JDBCGroupDatabase implements GroupDatabase {
      * 
      * @param group the Group to save
      * @param modifier the user who saved the Group
-     * @throws WikiSecurityException if the Group could not be saved
-     *             successfully
+     * @throws WikiSecurityException if the Group could not be saved successfully
      */
-    public void save( Group group, Principal modifier ) throws WikiSecurityException
+    @Override public void save( final Group group, final Principal modifier ) throws WikiSecurityException
     {
         if( group == null || modifier == null )
         {
             throw new IllegalArgumentException( "Group or modifier cannot be null." );
         }
 
-        boolean exists = exists( group );
+        final boolean exists = exists( group );
         Connection conn = null;
         PreparedStatement ps = null;
         try
@@ -368,8 +368,8 @@ public class JDBCGroupDatabase implements GroupDatabase {
                 conn.setAutoCommit( false );
             }
             
-            Timestamp ts = new Timestamp( System.currentTimeMillis() );
-            Date modDate = new Date( ts.getTime() );
+            final Timestamp ts = new Timestamp( System.currentTimeMillis() );
+            final Date modDate = new Date( ts.getTime() );
             if( !exists )
             {
                 // Group is new: insert new group record
@@ -410,10 +410,10 @@ public class JDBCGroupDatabase implements GroupDatabase {
 
             // Insert group member records
             ps = conn.prepareStatement( m_insertGroupMembers );
-            Principal[] members = group.members();
+            final Principal[] members = group.members();
             for( int i = 0; i < members.length; i++ )
             {
-                Principal member = members[i];
+                final Principal member = members[i];
                 ps.setString( 1, group.getName() );
                 ps.setString( 2, member.getName() );
                 ps.execute();
@@ -425,7 +425,7 @@ public class JDBCGroupDatabase implements GroupDatabase {
                 conn.commit();
             }
         }
-        catch( SQLException e )
+        catch( final SQLException e )
         {
         	closeQuietly(conn, ps, null );
             throw new WikiSecurityException( e.getMessage(), e );
@@ -445,18 +445,18 @@ public class JDBCGroupDatabase implements GroupDatabase {
      *             successfully
      * @throws NoRequiredPropertyException if a required property is not present
      */
-    public void initialize( WikiEngine engine, Properties props ) throws NoRequiredPropertyException, WikiSecurityException
+    @Override public void initialize( final Engine engine, final Properties props ) throws NoRequiredPropertyException, WikiSecurityException
     {
-        String table;
-        String memberTable;
+        final String table;
+        final String memberTable;
 
         m_engine = engine;
 
-        String jndiName = props.getProperty( PROP_GROUPDB_DATASOURCE, DEFAULT_GROUPDB_DATASOURCE );
+        final String jndiName = props.getProperty( PROP_GROUPDB_DATASOURCE, DEFAULT_GROUPDB_DATASOURCE );
         try
         {
-            Context initCtx = new InitialContext();
-            Context ctx = (Context) initCtx.lookup( "java:comp/env" );
+            final Context initCtx = new InitialContext();
+            final Context ctx = (Context) initCtx.lookup( "java:comp/env" );
             m_ds = (DataSource) ctx.lookup( jndiName );
 
             // Prepare the SQL selectors
@@ -485,7 +485,7 @@ public class JDBCGroupDatabase implements GroupDatabase {
             m_deleteGroup = "DELETE FROM " + table + " WHERE " + m_name + "=?";
             m_deleteGroupMembers = "DELETE FROM " + memberTable + " WHERE " + m_name + "=?";
         }
-        catch( NamingException e )
+        catch( final NamingException e )
         {
             log.error( "JDBCGroupDatabase initialization error: " + e );
             throw new NoRequiredPropertyException( PROP_GROUPDB_DATASOURCE, "JDBCGroupDatabase initialization error: " + e);
@@ -501,7 +501,7 @@ public class JDBCGroupDatabase implements GroupDatabase {
             ps.executeQuery();
             ps.close();
         }
-        catch( SQLException e )
+        catch( final SQLException e )
         {
         	closeQuietly( conn, ps, null );
             log.error( "DB connectivity error: " + e.getMessage() );
@@ -517,7 +517,7 @@ public class JDBCGroupDatabase implements GroupDatabase {
         try
         {
             conn = m_ds.getConnection();
-            DatabaseMetaData dmd = conn.getMetaData();
+            final DatabaseMetaData dmd = conn.getMetaData();
             if( dmd.supportsTransactions() )
             {
                 m_supportsCommits = true;
@@ -525,7 +525,7 @@ public class JDBCGroupDatabase implements GroupDatabase {
                 log.info( "JDBCGroupDatabase supports transactions. Good; we will use them." );
             }
         }
-        catch( SQLException e )
+        catch( final SQLException e )
         {
         	closeQuietly( conn, null, null );
             log.warn( "JDBCGroupDatabase warning: user database doesn't seem to support transactions. Reason: " + e);
@@ -542,15 +542,15 @@ public class JDBCGroupDatabase implements GroupDatabase {
      * @param group the Group to look for
      * @return the result of the search
      */
-    private boolean exists( Group group )
+    private boolean exists( final Group group )
     {
-        String index = group.getName();
+        final String index = group.getName();
         try
         {
             findGroup( index );
             return true;
         }
-        catch( NoSuchPrincipalException e )
+        catch( final NoSuchPrincipalException e )
         {
             return false;
         }
@@ -565,7 +565,7 @@ public class JDBCGroupDatabase implements GroupDatabase {
      * @throws NoSuchPrincipalException if the Group cannot be found
      * @throws SQLException if the database query returns an error
      */
-    private Group findGroup( String index ) throws NoSuchPrincipalException
+    private Group findGroup( final String index ) throws NoSuchPrincipalException
     {
         Group group = null;
         boolean found = false;
@@ -597,7 +597,7 @@ public class JDBCGroupDatabase implements GroupDatabase {
                 found = true;
             }
         }
-        catch( SQLException e )
+        catch( final SQLException e )
         {
         	closeQuietly( conn, ps, rs );
             throw new NoSuchPrincipalException( e.getMessage() );
@@ -624,7 +624,7 @@ public class JDBCGroupDatabase implements GroupDatabase {
      * @param group the group to populate
      * @return the populated Group
      */
-    private Group populateGroup( Group group )
+    private Group populateGroup( final Group group )
     {
     	ResultSet rs = null;
     	PreparedStatement ps = null;
@@ -639,15 +639,15 @@ public class JDBCGroupDatabase implements GroupDatabase {
             rs = ps.executeQuery();
             while ( rs.next() )
             {
-                String memberName = rs.getString( m_member );
+                final String memberName = rs.getString( m_member );
                 if( memberName != null )
                 {
-                    WikiPrincipal principal = new WikiPrincipal( memberName, WikiPrincipal.UNSPECIFIED );
+                    final WikiPrincipal principal = new WikiPrincipal( memberName, WikiPrincipal.UNSPECIFIED );
                     group.add( principal );
                 }
             }
         }
-        catch( SQLException e )
+        catch( final SQLException e )
         {
             // I guess that means there aren't any principals...
         }
@@ -658,23 +658,23 @@ public class JDBCGroupDatabase implements GroupDatabase {
         return group;
     }
     
-    void closeQuietly( Connection conn, PreparedStatement ps, ResultSet rs ) {
+    void closeQuietly( final Connection conn, final PreparedStatement ps, final ResultSet rs ) {
     	if( conn != null ) {
     		try {
     		    conn.close();
-    		} catch( Exception e ) {
+    		} catch( final Exception e ) {
     		}
     	}
 		if( ps != null )  {
 			try {
 			    ps.close();
-			} catch( Exception e ) {
+			} catch( final Exception e ) {
 			}
 		}
 		if( rs != null )  {
 			try {
 			    rs.close();
-			} catch( Exception e ) {
+			} catch( final Exception e ) {
 			}
 		}
 	}
diff --git a/jspwiki-main/src/main/java/org/apache/wiki/auth/authorize/WebAuthorizer.java b/jspwiki-main/src/main/java/org/apache/wiki/auth/authorize/WebAuthorizer.java
index d55569e..dd6cf08 100644
--- a/jspwiki-main/src/main/java/org/apache/wiki/auth/authorize/WebAuthorizer.java
+++ b/jspwiki-main/src/main/java/org/apache/wiki/auth/authorize/WebAuthorizer.java
@@ -18,29 +18,25 @@
  */
 package org.apache.wiki.auth.authorize;
 
-import java.security.Principal;
+import org.apache.wiki.auth.Authorizer;
 
 import javax.servlet.http.HttpServletRequest;
+import java.security.Principal;
 
-import org.apache.wiki.auth.Authorizer;
 
 /**
- * Extends the {@link org.apache.wiki.auth.Authorizer} interface by
- * including a delgate method for 
+ * Extends the {@link org.apache.wiki.auth.Authorizer} interface by including a delgate method for
  * {@link javax.servlet.http.HttpServletRequest#isUserInRole(String)}.
  */
-public interface WebAuthorizer extends Authorizer
-{
+public interface WebAuthorizer extends Authorizer {
     
     /**
-     * Determines whether a user associated with an HTTP request possesses
-     * a particular role. This method simply delegates to 
-     * {@link javax.servlet.http.HttpServletRequest#isUserInRole(String)}
-     * by converting the Principal's name to a String.
+     * Determines whether a user associated with an HTTP request possesses a particular role. This method simply delegates to
+     * {@link javax.servlet.http.HttpServletRequest#isUserInRole(String)} by converting the Principal's name to a String.
+     *
      * @param request the HTTP request
      * @param role the role to check
-     * @return <code>true</code> if the user is considered to be in the role,
-     *         <code>false</code> otherwise
+     * @return <code>true</code> if the user is considered to be in the role, <code>false</code> otherwise
      */
     boolean isUserInRole( HttpServletRequest request, Principal role );
     
diff --git a/jspwiki-main/src/main/java/org/apache/wiki/auth/authorize/WebContainerAuthorizer.java b/jspwiki-main/src/main/java/org/apache/wiki/auth/authorize/WebContainerAuthorizer.java
index cfb78b3..a4c4968 100644
--- a/jspwiki-main/src/main/java/org/apache/wiki/auth/authorize/WebContainerAuthorizer.java
+++ b/jspwiki-main/src/main/java/org/apache/wiki/auth/authorize/WebContainerAuthorizer.java
@@ -20,8 +20,8 @@ package org.apache.wiki.auth.authorize;
 
 import org.apache.log4j.Logger;
 import org.apache.wiki.InternalWikiException;
-import org.apache.wiki.WikiEngine;
 import org.apache.wiki.WikiSession;
+import org.apache.wiki.api.core.Engine;
 import org.jdom2.Document;
 import org.jdom2.Element;
 import org.jdom2.JDOMException;
@@ -56,9 +56,9 @@ public class WebContainerAuthorizer implements WebAuthorizer  {
 
     private static final String J2EE_SCHEMA_25_NAMESPACE = "http://xmlns.jcp.org/xml/ns/javaee";
 
-    protected static final Logger log                   = Logger.getLogger( WebContainerAuthorizer.class );
+    private static final Logger log = Logger.getLogger( WebContainerAuthorizer.class );
 
-    protected WikiEngine          m_engine;
+    protected Engine m_engine;
 
     /**
      * A lazily-initialized array of Roles that the container knows about. These
@@ -68,15 +68,15 @@ public class WebContainerAuthorizer implements WebAuthorizer  {
      * that we have no direct way of querying the web container about which
      * roles it manages.
      */
-    protected Role[]            m_containerRoles      = new Role[0];
+    protected Role[] m_containerRoles      = new Role[0];
 
     /**
      * Lazily-initialized boolean flag indicating whether the web container
      * protects JSPWiki resources.
      */
-    protected boolean           m_containerAuthorized = false;
+    protected boolean m_containerAuthorized = false;
 
-    private Document            m_webxml = null;
+    private Document m_webxml = null;
 
     /**
      * Constructs a new instance of the WebContainerAuthorizer class.
@@ -92,49 +92,36 @@ public class WebContainerAuthorizer implements WebAuthorizer  {
      * @param props the wiki engine initialization properties
      */
     @Override
-    public void initialize( WikiEngine engine, Properties props )
-    {
+    public void initialize( final Engine engine, final Properties props ) {
         m_engine = engine;
         m_containerAuthorized = false;
 
         // FIXME: Error handling here is not very verbose
-        try
-        {
+        try {
             m_webxml = getWebXml();
-            if ( m_webxml != null )
-            {
+            if( m_webxml != null ) {
                 // Add the J2EE 2.4 schema namespace
                 m_webxml.getRootElement().setNamespace( Namespace.getNamespace( J2EE_SCHEMA_25_NAMESPACE ) );
 
-                m_containerAuthorized = isConstrained( "/Delete.jsp", Role.ALL )
-                        && isConstrained( "/Login.jsp", Role.ALL );
+                m_containerAuthorized = isConstrained( "/Delete.jsp", Role.ALL ) && isConstrained( "/Login.jsp", Role.ALL );
             }
-            if ( m_containerAuthorized )
-            {
+            if( m_containerAuthorized ) {
                 m_containerRoles = getRoles( m_webxml );
                 log.info( "JSPWiki is using container-managed authentication." );
-            }
-            else
-            {
+            } else {
                 log.info( "JSPWiki is using custom authentication." );
             }
-        }
-        catch ( IOException e )
-        {
-            log.error("Initialization failed: ",e);
-            throw new InternalWikiException( e.getClass().getName()+": "+e.getMessage() , e);
-        }
-        catch ( JDOMException e )
-        {
-            log.error("Malformed XML in web.xml",e);
-            throw new InternalWikiException( e.getClass().getName()+": "+e.getMessage() , e);
+        } catch( final IOException e ) {
+            log.error( "Initialization failed: ", e );
+            throw new InternalWikiException( e.getClass().getName() + ": " + e.getMessage(), e );
+        } catch( final JDOMException e ) {
+            log.error( "Malformed XML in web.xml", e );
+            throw new InternalWikiException( e.getClass().getName() + ": " + e.getMessage(), e );
         }
 
-        if ( m_containerRoles.length > 0 )
-        {
+        if( m_containerRoles.length > 0 ) {
             String roles = "";
-            for( Role containerRole : m_containerRoles )
-            {
+            for( final Role containerRole : m_containerRoles ) {
                 roles = roles + containerRole + " ";
             }
             log.info( " JSPWiki determined the web container manages these roles: " + roles );
@@ -153,7 +140,7 @@ public class WebContainerAuthorizer implements WebAuthorizer  {
      *         <code>false</code> otherwise
      */
     @Override
-    public boolean isUserInRole( HttpServletRequest request, Principal role )
+    public boolean isUserInRole( final HttpServletRequest request, final Principal role )
     {
         return request.isUserInRole( role.getName() );
     }
@@ -182,10 +169,8 @@ public class WebContainerAuthorizer implements WebAuthorizer  {
      * @see org.apache.wiki.auth.Authorizer#isUserInRole(org.apache.wiki.WikiSession, java.security.Principal)
      */
     @Override
-    public boolean isUserInRole( WikiSession session, Principal role )
-    {
-        if ( session == null || role == null )
-        {
+    public boolean isUserInRole( final WikiSession session, final Principal role ) {
+        if ( session == null || role == null ) {
             return false;
         }
         return session.hasPrincipal( role );
@@ -197,15 +182,12 @@ public class WebContainerAuthorizer implements WebAuthorizer  {
      * initialization, this method returns <code>null</code>.
      * @param role the name of the Role to retrieve
      * @return a Role Principal, or <code>null</code>
-     * @see org.apache.wiki.auth.Authorizer#initialize(WikiEngine, Properties)
+     * @see org.apache.wiki.auth.Authorizer#initialize(Engine, Properties)
      */
     @Override
-    public Principal findRole( String role )
-    {
-        for( Role containerRole : m_containerRoles )
-        {
-            if ( containerRole.getName().equals( role ) )
-            {
+    public Principal findRole( final String role ) {
+        for( final Role containerRole : m_containerRoles ) {
+            if ( containerRole.getName().equals( role ) ) {
                 return containerRole;
             }
         }
@@ -266,9 +248,9 @@ public class WebContainerAuthorizer implements WebAuthorizer  {
         }
 
         // If a constraint is contained in both lists, we must be constrained
-        for ( Iterator< Element > c = constraints.iterator(); c.hasNext(); ) {
+        for ( final Iterator< Element > c = constraints.iterator(); c.hasNext(); ) {
             final Element constraint = c.next();
-            for ( Iterator< Element > r = roles.iterator(); r.hasNext(); ) {
+            for ( final Iterator< Element > r = roles.iterator(); r.hasNext(); ) {
                 final Element roleConstraint = r.next();
                 if ( constraint.equals( roleConstraint ) ) {
                     return true;
@@ -362,14 +344,14 @@ public class WebContainerAuthorizer implements WebAuthorizer  {
      */
     protected Document getWebXml() throws JDOMException, IOException
     {
-        URL url;
-        SAXBuilder builder = new SAXBuilder();
+        final URL url;
+        final SAXBuilder builder = new SAXBuilder();
         builder.setXMLReaderFactory( XMLReaders.NONVALIDATING );
         builder.setEntityResolver( new LocalEntityResolver() );
         Document doc = null;
         if ( m_engine.getServletContext() == null )
         {
-            ClassLoader cl = WebContainerAuthorizer.class.getClassLoader();
+            final ClassLoader cl = WebContainerAuthorizer.class.getClassLoader();
             url = cl.getResource( "WEB-INF/web.xml" );
             if( url != null )
                 log.info( "Examining " + url.toExternalForm() );
@@ -414,13 +396,13 @@ public class WebContainerAuthorizer implements WebAuthorizer  {
          * @throws IOException if the resource cannot be opened
          */
         @Override
-        public InputSource resolveEntity( String publicId, String systemId ) throws SAXException, IOException
+        public InputSource resolveEntity( final String publicId, final String systemId ) throws SAXException, IOException
         {
-            String file = systemId.substring( systemId.lastIndexOf( '/' ) + 1 );
-            URL url;
+            final String file = systemId.substring( systemId.lastIndexOf( '/' ) + 1 );
+            final URL url;
             if ( m_engine.getServletContext() == null )
             {
-                ClassLoader cl = WebContainerAuthorizer.class.getClassLoader();
+                final ClassLoader cl = WebContainerAuthorizer.class.getClassLoader();
                 url = cl.getResource( "WEB-INF/dtd/" + file );
             }
             else
@@ -430,7 +412,7 @@ public class WebContainerAuthorizer implements WebAuthorizer  {
 
             if( url != null )
             {
-                InputSource is = new InputSource( url.openStream() );
+                final InputSource is = new InputSource( url.openStream() );
                 log.debug( "Resolved systemID=" + systemId + " using local file " + url );
                 return is;
             }
diff --git a/jspwiki-main/src/main/java/org/apache/wiki/auth/authorize/XMLGroupDatabase.java b/jspwiki-main/src/main/java/org/apache/wiki/auth/authorize/XMLGroupDatabase.java
index 153a2d6..6b0ec1d 100644
--- a/jspwiki-main/src/main/java/org/apache/wiki/auth/authorize/XMLGroupDatabase.java
+++ b/jspwiki-main/src/main/java/org/apache/wiki/auth/authorize/XMLGroupDatabase.java
@@ -20,7 +20,7 @@ package org.apache.wiki.auth.authorize;
 
 import org.apache.commons.text.StringEscapeUtils;
 import org.apache.log4j.Logger;
-import org.apache.wiki.WikiEngine;
+import org.apache.wiki.api.core.Engine;
 import org.apache.wiki.api.exceptions.NoRequiredPropertyException;
 import org.apache.wiki.auth.NoSuchPrincipalException;
 import org.apache.wiki.auth.WikiPrincipal;
@@ -73,14 +73,11 @@ import java.util.concurrent.ConcurrentHashMap;
  * </code></blockquote>
  * @since 2.4.17
  */
-public class XMLGroupDatabase implements GroupDatabase
-{
-    protected static final Logger log              = Logger.getLogger( XMLGroupDatabase.class );
+public class XMLGroupDatabase implements GroupDatabase {
 
-    /**
-     * The jspwiki.properties property specifying the file system location of
-     * the group database.
-     */
+    private static final Logger log              = Logger.getLogger( XMLGroupDatabase.class );
+
+    /** The jspwiki.properties property specifying the file system location of the group database. */
     public static final String    PROP_DATABASE    = "jspwiki.xmlGroupDatabaseFile";
 
     private static final String   DEFAULT_DATABASE = "groupdatabase.xml";
@@ -109,7 +106,7 @@ public class XMLGroupDatabase implements GroupDatabase
 
     private File                  m_file           = null;
 
-    private WikiEngine            m_engine         = null;
+    private Engine                m_engine         = null;
 
     private Map<String, Group>    m_groups         = new ConcurrentHashMap<>();
 
@@ -124,10 +121,10 @@ public class XMLGroupDatabase implements GroupDatabase
      * the commit did not succeed
      */
     @Override
-    public void delete( Group group ) throws WikiSecurityException
+    public void delete( final Group group ) throws WikiSecurityException
     {
-        String index = group.getName();
-        boolean exists = m_groups.containsKey( index );
+        final String index = group.getName();
+        final boolean exists = m_groups.containsKey( index );
 
         if ( !exists )
         {
@@ -153,7 +150,7 @@ public class XMLGroupDatabase implements GroupDatabase
     public Group[] groups() throws WikiSecurityException
     {
         buildDOM();
-        Collection<Group> groups = m_groups.values();
+        final Collection<Group> groups = m_groups.values();
         return groups.toArray( new Group[groups.size()] );
     }
 
@@ -168,7 +165,7 @@ public class XMLGroupDatabase implements GroupDatabase
      * @throws WikiSecurityException if the database could not be initialized successfully
      */
     @Override
-    public void initialize( WikiEngine engine, Properties props ) throws NoRequiredPropertyException, WikiSecurityException
+    public void initialize( final Engine engine, final Properties props ) throws NoRequiredPropertyException, WikiSecurityException
     {
         m_engine = engine;
 
@@ -184,7 +181,7 @@ public class XMLGroupDatabase implements GroupDatabase
         }
 
         // Get database file location
-        String file = TextUtil.getStringProperty(props, PROP_DATABASE , defaultFile.getAbsolutePath());
+        final String file = TextUtil.getStringProperty(props, PROP_DATABASE , defaultFile.getAbsolutePath());
         if ( file == null )
         {
             log.warn( "XML group database property " + PROP_DATABASE + " not found; trying " + defaultFile );
@@ -213,16 +210,16 @@ public class XMLGroupDatabase implements GroupDatabase
      * @throws WikiSecurityException if the Group could not be saved successfully
      */
     @Override
-    public void save( Group group, Principal modifier ) throws WikiSecurityException {
+    public void save( final Group group, final Principal modifier ) throws WikiSecurityException {
         if ( group == null || modifier == null ) {
             throw new IllegalArgumentException( "Group or modifier cannot be null." );
         }
 
         checkForRefresh();
 
-        String index = group.getName();
-        boolean isNew = !( m_groups.containsKey( index ) );
-        Date modDate = new Date( System.currentTimeMillis() );
+        final String index = group.getName();
+        final boolean isNew = !( m_groups.containsKey( index ) );
+        final Date modDate = new Date( System.currentTimeMillis() );
         if ( isNew )
         {
             // If new, set created info
diff --git a/jspwiki-main/src/main/resources/ini/classmappings.xml b/jspwiki-main/src/main/resources/ini/classmappings.xml
index ff4ad5a..b419955 100644
--- a/jspwiki-main/src/main/resources/ini/classmappings.xml
+++ b/jspwiki-main/src/main/resources/ini/classmappings.xml
@@ -73,7 +73,7 @@
   </mapping>
   <mapping>
     <requestedClass>org.apache.wiki.auth.AuthorizationManager</requestedClass>
-    <mappedClass>org.apache.wiki.auth.AuthorizationManager</mappedClass>
+    <mappedClass>org.apache.wiki.auth.DefaultAuthorizationManager</mappedClass>
   </mapping>
   <mapping>
     <requestedClass>org.apache.wiki.auth.UserManager</requestedClass>
diff --git a/jspwiki-main/src/test/java/org/apache/wiki/auth/AuthenticationManagerTest.java b/jspwiki-main/src/test/java/org/apache/wiki/auth/AuthenticationManagerTest.java
index 803f1fd..0c1a106 100644
--- a/jspwiki-main/src/test/java/org/apache/wiki/auth/AuthenticationManagerTest.java
+++ b/jspwiki-main/src/test/java/org/apache/wiki/auth/AuthenticationManagerTest.java
@@ -17,16 +17,12 @@
     under the License.
  */
 package org.apache.wiki.auth;
-import java.security.Principal;
-import java.util.Map;
-import java.util.Properties;
-
-import javax.servlet.http.HttpServletRequest;
 
 import org.apache.wiki.TestEngine;
 import org.apache.wiki.WikiEngine;
 import org.apache.wiki.WikiSession;
 import org.apache.wiki.WikiSessionTest;
+import org.apache.wiki.api.core.Engine;
 import org.apache.wiki.auth.authorize.Group;
 import org.apache.wiki.auth.authorize.GroupManager;
 import org.apache.wiki.auth.authorize.Role;
@@ -36,45 +32,42 @@ import org.junit.jupiter.api.Assertions;
 import org.junit.jupiter.api.BeforeEach;
 import org.junit.jupiter.api.Test;
 
+import javax.servlet.http.HttpServletRequest;
+import java.security.Principal;
+import java.util.Map;
+import java.util.Properties;
+
 /**
  * Tests the AuthorizationManager class.
  *
  */
-public class AuthenticationManagerTest
-{
-    public static class DummyAuthorizer implements WebAuthorizer
-    {
+public class AuthenticationManagerTest {
+
+    public static class DummyAuthorizer implements WebAuthorizer {
         private static Principal[] m_roles = new Principal[] { new Role( "ContainerRole" ), new Role( "AuthorizerRole" ),
                                                               new Role( "DummyRole" ) };
 
-        public Principal findRole( String role )
-        {
-            for( Principal principal : m_roles )
-            {
-                if( principal.getName().equals( role ) )
-                {
+        @Override public Principal findRole( final String role ) {
+            for( final Principal principal : m_roles ) {
+                if( principal.getName().equals( role ) ) {
                     return principal;
                 }
             }
             return null;
         }
 
-        public Principal[] getRoles()
-        {
+        @Override public Principal[] getRoles() {
             return m_roles;
         }
 
-        public void initialize( WikiEngine engine, Properties props ) throws WikiSecurityException
-        {
+        @Override public void initialize( final Engine engine, final Properties props ) throws WikiSecurityException {
         }
 
-        public boolean isUserInRole( HttpServletRequest request, Principal role )
-        {
+        @Override public boolean isUserInRole( final HttpServletRequest request, final Principal role ) {
             return request != null && "ContainerRole".equals( role.getName() );
         }
 
-        public boolean isUserInRole( WikiSession session, Principal role )
-        {
+        @Override public boolean isUserInRole( final WikiSession session, final Principal role ) {
             return session != null && "AuthorizerRole".equals( role.getName() );
         }
     }
@@ -88,9 +81,8 @@ public class AuthenticationManagerTest
     private WikiSession m_session;
 
     @BeforeEach
-    public void setUp() throws Exception
-    {
-        Properties props = TestEngine.getTestProperties();
+    public void setUp() throws Exception {
+        final Properties props = TestEngine.getTestProperties();
         m_engine = new TestEngine( props );
         m_auth = m_engine.getAuthenticationManager();
         m_groupMgr = m_engine.getGroupManager();
@@ -98,15 +90,13 @@ public class AuthenticationManagerTest
     }
 
     /**
-     * Tests a dummy WebAuthorizer that is guaranteed to return true for one
-     * role for each of the two <code>isInRole</code> methods.
+     * Tests a dummy WebAuthorizer that is guaranteed to return true for one role for each of the two <code>isInRole</code> methods.
      *
      * @throws Exception
      */
     @Test
-    public void testCustomAuthorizer() throws Exception
-    {
-        Properties props = TestEngine.getTestProperties();
+    public void testCustomAuthorizer() throws Exception {
+        final Properties props = TestEngine.getTestProperties();
         props.put( AuthorizationManager.PROP_AUTHORIZER, "org.apache.wiki.auth.AuthenticationManagerTest$DummyAuthorizer" );
         m_engine = new TestEngine( props );
 
@@ -134,24 +124,22 @@ public class AuthenticationManagerTest
     }
 
     @Test
-    public void testCustomJAASLoginModule() throws Exception
-    {
-        Properties props = TestEngine.getTestProperties();
+    public void testCustomJAASLoginModule() throws Exception {
+        final Properties props = TestEngine.getTestProperties();
 
         // Supply a custom LoginModule class
         props.put( "jspwiki.loginModule.class", "org.apache.wiki.auth.login.CookieAssertionLoginModule" );
 
         // Init the engine and verify that we initialized with a custom auth
         // login module
-        WikiEngine engine = new TestEngine( props );
-        AuthenticationManager authMgr = engine.getAuthenticationManager();
+        final WikiEngine engine = new TestEngine( props );
+        final DefaultAuthenticationManager authMgr = ( DefaultAuthenticationManager )engine.getAuthenticationManager();
         Assertions.assertEquals( CookieAssertionLoginModule.class, authMgr.m_loginModuleClass );
     }
 
     @Test
-    public void testCustomJAASLoginModuleOptions() throws Exception
-    {
-        Properties props = TestEngine.getTestProperties();
+    public void testCustomJAASLoginModuleOptions() throws Exception {
+        final Properties props = TestEngine.getTestProperties();
 
         // Supply a custom LoginModule options
         props.put( "jspwiki.loginModule.options.key1", "value1" );
@@ -160,9 +148,9 @@ public class AuthenticationManagerTest
 
         // Init the engine and verify that we initialized with the correct
         // options
-        WikiEngine engine = new TestEngine( props );
-        AuthenticationManager authMgr = engine.getAuthenticationManager();
-        Map<String, String> options = authMgr.m_loginModuleOptions;
+        final WikiEngine engine = new TestEngine( props );
+        final DefaultAuthenticationManager authMgr = ( DefaultAuthenticationManager )engine.getAuthenticationManager();
+        final Map<String, String> options = authMgr.m_loginModuleOptions;
         Assertions.assertEquals( 3, options.size() );
         Assertions.assertTrue( options.containsKey( "key1" ) );
         Assertions.assertTrue( options.containsKey( "key2" ) );
@@ -173,8 +161,7 @@ public class AuthenticationManagerTest
     }
 
     @Test
-    public void testIsUserPrincipal()
-    {
+    public void testIsUserPrincipal() {
         Assertions.assertTrue( AuthenticationManager.isUserPrincipal( new WikiPrincipal( "Foo" ) ) );
         Assertions.assertFalse( AuthenticationManager.isUserPrincipal( new GroupPrincipal( "Group1" ) ) );
         Assertions.assertFalse( AuthenticationManager.isUserPrincipal( new Role( "Role1" ) ) );
@@ -182,9 +169,8 @@ public class AuthenticationManagerTest
     }
 
     @Test
-    public void testLoginCustom() throws Exception
-    {
-        WikiSession session = WikiSessionTest.authenticatedSession( m_engine, Users.JANNE, Users.JANNE_PASS );
+    public void testLoginCustom() throws Exception {
+        final WikiSession session = WikiSessionTest.authenticatedSession( m_engine, Users.JANNE, Users.JANNE_PASS );
         Assertions.assertTrue( session.hasPrincipal( Role.ALL ) );
         Assertions.assertTrue( session.hasPrincipal( Role.AUTHENTICATED ) );
         Assertions.assertTrue( session.hasPrincipal( new WikiPrincipal( Users.JANNE, WikiPrincipal.LOGIN_NAME ) ) );
@@ -193,43 +179,38 @@ public class AuthenticationManagerTest
     }
 
     @Test
-    public void testLoginCustomWithGroup() throws Exception
-    {
-        // Flush any pre-existing groups (left over from previous Assertions.failures,
-        // perhaps)
-        try
-        {
+    public void testLoginCustomWithGroup() throws Exception {
+        // Flush any pre-existing groups (left over from previous Assertions.failures, perhaps)
+        try {
             m_groupMgr.removeGroup( "Test1" );
             m_groupMgr.removeGroup( "Test2" );
-        }
-        catch( NoSuchPrincipalException e )
-        {
+        } catch( final NoSuchPrincipalException e ) {
 
         }
 
         // Log in 'janne' and verify there are 5 principals in the subject
         // (ALL, AUTHENTICATED, login, fullname, wikiname Principals)
-        WikiSession session = WikiSession.guestSession( m_engine );
+        final WikiSession session = WikiSession.guestSession( m_engine );
         m_auth.login( session, null, Users.JANNE, Users.JANNE_PASS );
         Assertions.assertEquals( 3, session.getPrincipals().length );
         Assertions.assertEquals( 2, session.getRoles().length );
         Assertions.assertTrue( session.hasPrincipal( new WikiPrincipal( "JanneJalkanen", WikiPrincipal.WIKI_NAME ) ) );
 
         // Listen for any manager group-add events
-        GroupManager manager = m_engine.getGroupManager();
-        SecurityEventTrap trap = new SecurityEventTrap();
+        final GroupManager manager = m_engine.getGroupManager();
+        final SecurityEventTrap trap = new SecurityEventTrap();
         manager.addWikiEventListener( trap );
 
         // Create two groups; one with Janne in it, and one without
         Group groupTest1 = m_groupMgr.parseGroup( "Test1", "JanneJalkanen \n Bob \n Charlie", true );
         m_groupMgr.setGroup( m_session, groupTest1 );
         groupTest1 = m_groupMgr.getGroup( "Test1" );
-        Principal principalTest1 = groupTest1.getPrincipal();
+        final Principal principalTest1 = groupTest1.getPrincipal();
 
         Group groupTest2 = m_groupMgr.parseGroup( "Test2", "Alice \n Bob \n Charlie", true );
         m_groupMgr.setGroup( m_session, groupTest2 );
         groupTest2 = m_groupMgr.getGroup( "Test2" );
-        Principal principalTest2 = groupTest2.getPrincipal();
+        final Principal principalTest2 = groupTest2.getPrincipal();
 
         // We should see two security events (one for each group create)
         // We should also see a GroupPrincipal for group Test1, but not Test2
diff --git a/jspwiki-main/src/test/java/org/apache/wiki/auth/TestAuthorizer.java b/jspwiki-main/src/test/java/org/apache/wiki/auth/TestAuthorizer.java
index c0761e9..5b272e7 100644
--- a/jspwiki-main/src/test/java/org/apache/wiki/auth/TestAuthorizer.java
+++ b/jspwiki-main/src/test/java/org/apache/wiki/auth/TestAuthorizer.java
@@ -18,24 +18,23 @@
  */
 package org.apache.wiki.auth;
 
-import java.security.Principal;
-import java.util.Properties;
-
-import javax.servlet.http.HttpServletRequest;
-
-import org.apache.wiki.WikiEngine;
 import org.apache.wiki.WikiSession;
+import org.apache.wiki.api.core.Engine;
 import org.apache.wiki.auth.authorize.Role;
 import org.apache.wiki.auth.authorize.WebAuthorizer;
 
+import javax.servlet.http.HttpServletRequest;
+import java.security.Principal;
+import java.util.Properties;
+
 /**
- * A very fast authorizer that does almost nothing. The WebContainerAuthorizer module
- * is very slow, as it parses the web.xml each time, so we use this for most of
- * the different tests.
+ * A very fast authorizer that does almost nothing. The WebContainerAuthorizer module is very slow, as it parses the web.xml each time,
+ * so we use this for most of the different tests.
+ *
  * @since 2.3
  */
-public class TestAuthorizer implements WebAuthorizer
-{
+public class TestAuthorizer implements WebAuthorizer {
+
     private Role[] m_roles = new Role[]{ 
             new Role( "Admin" ), 
             Role.AUTHENTICATED,
@@ -48,33 +47,28 @@ public class TestAuthorizer implements WebAuthorizer
         super();
     }
 
-    public Principal findRole( String role )
+    @Override public Principal findRole( final String role )
     {
         return null;
     }
 
-    public void initialize( WikiEngine engine, Properties props )
-    {
+    @Override public void initialize( final Engine engine, final Properties props ) {
     }
 
     /**
-     * Returns an array of Principal objects containing five elements:
-     * Role "Admin", Role.AUTHENTICATED, Role "IT", Role "Finance" and 
+     * Returns an array of Principal objects containing five elements: Role "Admin", Role.AUTHENTICATED, Role "IT", Role "Finance" and
      * Role "Engineering."
      */
-    public Principal[] getRoles()
+    @Override public Principal[] getRoles()
     {
         return m_roles;
     }
     
     /**
-     * Returns <code>true</code> if the WikiSession's Subject contains 
-     * a particular role principal.
+     * Returns <code>true</code> if the WikiSession's Subject contains a particular role principal.
      */
-    public boolean isUserInRole( WikiSession session, Principal role )
-    {
-        if ( session == null || role == null )
-        {
+    @Override public boolean isUserInRole( final WikiSession session, final Principal role ) {
+        if ( session == null || role == null ) {
             return false;
         }
         
@@ -87,7 +81,7 @@ public class TestAuthorizer implements WebAuthorizer
      * {@link javax.servlet.http.HttpServletRequest#isUserInRole(String)}.
      * @see org.apache.wiki.auth.authorize.WebAuthorizer#isUserInRole(javax.servlet.http.HttpServletRequest, java.security.Principal)
      */
-    public boolean isUserInRole( HttpServletRequest request, Principal role )
+    @Override public boolean isUserInRole( final HttpServletRequest request, final Principal role )
     {
         return request.isUserInRole( role.getName() );
     }