You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@jspwiki.apache.org by aj...@apache.org on 2008/02/13 06:54:24 UTC

svn commit: r627255 [11/41] - in /incubator/jspwiki/branches/JSPWIKI_STRIPES_BRANCH/src: ./ com/ com/ecyrd/ com/ecyrd/jspwiki/ com/ecyrd/jspwiki/action/ com/ecyrd/jspwiki/attachment/ com/ecyrd/jspwiki/auth/ com/ecyrd/jspwiki/auth/acl/ com/ecyrd/jspwiki...

Added: incubator/jspwiki/branches/JSPWIKI_STRIPES_BRANCH/src/com/ecyrd/jspwiki/auth/AuthorizationManager.java
URL: http://svn.apache.org/viewvc/incubator/jspwiki/branches/JSPWIKI_STRIPES_BRANCH/src/com/ecyrd/jspwiki/auth/AuthorizationManager.java?rev=627255&view=auto
==============================================================================
--- incubator/jspwiki/branches/JSPWIKI_STRIPES_BRANCH/src/com/ecyrd/jspwiki/auth/AuthorizationManager.java (added)
+++ incubator/jspwiki/branches/JSPWIKI_STRIPES_BRANCH/src/com/ecyrd/jspwiki/auth/AuthorizationManager.java Tue Feb 12 21:53:55 2008
@@ -0,0 +1,684 @@
+/*
+ JSPWiki - a JSP-based WikiWiki clone.
+
+ Copyright (C) 2001-2003 Janne Jalkanen (Janne.Jalkanen@iki.fi)
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU Lesser General Public License as published by
+ the Free Software Foundation; either version 2.1 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ GNU Lesser General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ */
+package com.ecyrd.jspwiki.auth;
+
+
+import java.io.File;
+import java.net.URL;
+import java.security.*;
+import java.security.cert.Certificate;
+import java.util.Map;
+import java.util.Properties;
+import java.util.WeakHashMap;
+
+import org.apache.log4j.Logger;
+import org.freshcookies.security.policy.LocalPolicy;
+import org.freshcookies.security.policy.PolicyException;
+
+import com.ecyrd.jspwiki.NoRequiredPropertyException;
+import com.ecyrd.jspwiki.WikiEngine;
+import com.ecyrd.jspwiki.WikiException;
+import com.ecyrd.jspwiki.WikiPage;
+import com.ecyrd.jspwiki.WikiSession;
+import com.ecyrd.jspwiki.auth.acl.Acl;
+import com.ecyrd.jspwiki.auth.acl.AclEntry;
+import com.ecyrd.jspwiki.auth.acl.UnresolvedPrincipal;
+import com.ecyrd.jspwiki.auth.authorize.Role;
+import com.ecyrd.jspwiki.auth.permissions.AllPermission;
+import com.ecyrd.jspwiki.auth.permissions.PagePermission;
+import com.ecyrd.jspwiki.auth.user.UserDatabase;
+import com.ecyrd.jspwiki.auth.user.UserProfile;
+import com.ecyrd.jspwiki.event.WikiEventListener;
+import com.ecyrd.jspwiki.event.WikiEventManager;
+import com.ecyrd.jspwiki.event.WikiSecurityEvent;
+import com.ecyrd.jspwiki.util.ClassUtil;
+
+/**
+ * <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 com.ecyrd.jspwiki.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 com.ecyrd.jspwiki.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 com.ecyrd.jspwiki.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>
+ * @author Andrew Jaquith
+ * @since 2.3
+ * @see AuthenticationManager
+ */
+public final class AuthorizationManager
+{
+    private static final Logger log = Logger.getLogger( AuthorizationManager.class );
+    /**
+     * The default external Authorizer is the {@link com.ecyrd.jspwiki.auth.authorize.WebContainerAuthorizer}
+     */
+    public static final String                DEFAULT_AUTHORIZER = "com.ecyrd.jspwiki.auth.authorize.WebContainerAuthorizer";
+
+    /** 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";
+
+    private Authorizer                        m_authorizer      = null;
+
+    /** Cache for storing ProtectionDomains used to evaluate the local policy. */
+    private Map                               m_cachedPds       = new WeakHashMap();
+
+    private WikiEngine                        m_engine          = null;
+
+    private LocalPolicy                       m_localPolicy     = null;
+
+    private boolean                           m_useJAAS         = true;
+
+    /**
+     * 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
+     * a supplied WikiSession. The access control algorithm works this way:
+     * <ol>
+     * <li>The {@link com.ecyrd.jspwiki.auth.acl.Acl} for the page is obtained</li>
+     * <li>The Subject associated with the current
+     * {@link com.ecyrd.jspwiki.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 com.ecyrd.jspwiki.auth.acl.UnresolvedPrincipal}s (see below).
+     * Then iterate through the Subject's Principal set and determine whether
+     * the user (Subject) posesses any one of these specified Roles or
+     * Principals. The matching process delegates to
+     * {@link #hasRoleOrPrincipal(WikiSession, Principal)}.
+     * </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).
+     * </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 final boolean checkPermission( WikiSession session, Permission permission )
+    {
+        if( !m_useJAAS )
+        {
+            //
+            //  Nobody can login, if JAAS is turned off.
+            //
+
+            if( permission == null || "login".equals( permission.getActions() ) )
+                return false;
+
+            return true;
+        }
+
+        //
+        //  A slight sanity check.
+        //
+        if ( session == null || permission == null )
+        {
+            fireEvent( WikiSecurityEvent.ACCESS_DENIED, null, permission );
+            return false;
+        }
+
+        Principal user = session.getLoginPrincipal();
+
+        // Always allow the action if user has AllPermission
+        Permission allPermission = new AllPermission( m_engine.getApplicationName() );
+        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.
+        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.
+        //
+        String pageName = ((PagePermission)permission).getPage();
+        WikiPage page = m_engine.getPage( pageName );
+        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.
+
+        Principal[] aclPrincipals = acl.findPrincipals( permission );
+
+        log.debug( "Checking ACL entries..." );
+        log.debug( "Acl for this page is: " + acl );
+        log.debug( "Checking for principal: " + String.valueOf(aclPrincipals) );
+        log.debug( "Permission: " + permission );
+
+        for( int i = 0; i < aclPrincipals.length; i++ )
+        {
+            Principal aclPrincipal = aclPrincipals[i];
+
+            // If the ACL principal we're looking at is unresolved,
+            // try to resolve it here & correct the Acl
+            if ( aclPrincipal instanceof UnresolvedPrincipal )
+            {
+                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;
+    }
+
+    /**
+     * <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 (anyonymous, 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-<cor>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 final boolean isUserInRole( WikiSession session, 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 ) )
+        {
+            return session.hasPrincipal( principal );
+        }
+
+        // Only authenticated users can posssess groups or custom roles
+        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 com.ecyrd.jspwiki.auth.WikiSecurityException}.
+     * @throws com.ecyrd.jspwiki.auth.WikiSecurityException if the Authorizer could
+     * not be initialized
+     * @return the current Authorizer
+     */
+    public final 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( WikiSession session, 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 ) )
+        {
+            String principalName = principal.getName();
+            Principal[] userPrincipals = session.getPrincipals();
+            for( int i = 0; i < userPrincipals.length; i++ )
+            {
+                Principal userPrincipal = userPrincipals[i];
+                if( userPrincipal.getName().equals( principalName ) )
+                {
+                    return true;
+                }
+            }
+        }
+        return false;
+    }
+
+    /**
+     * 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 group 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 final void initialize( WikiEngine engine, Properties properties ) throws WikiException
+    {
+        m_engine = engine;
+
+        m_useJAAS = AuthenticationManager.SECURITY_JAAS.equals( properties.getProperty(AuthenticationManager.PROP_SECURITY, AuthenticationManager.SECURITY_JAAS ) );
+
+        if( !m_useJAAS ) return;
+
+        //
+        //  JAAS authorization continues
+        //
+        m_authorizer = getAuthorizerImplementation( properties );
+        m_authorizer.initialize( engine, properties );
+
+        // Initialize local security policy
+        try
+        {
+            URL policyURL = AuthenticationManager.findConfigFile( engine, DEFAULT_POLICY );
+            
+            if (policyURL != null) 
+            {
+                File policyFile = new File( policyURL.getPath() );
+                m_localPolicy = new LocalPolicy( policyFile, engine.getContentEncoding() );
+                m_localPolicy.refresh();
+                log.info("Initialized default security policy: " + policyFile.getAbsolutePath());
+            }
+            else
+            {
+                StringBuffer sb = new StringBuffer("JSPWiki was unable to initialize the ");
+                sb.append("default security policy (WEB-INF/jspwiki.policy) file. ");
+                sb.append("Please ensure that the jspwiki.policy file exists in the default location. ");
+                sb.append("This file should exist regardless of the existance of a global policy file. ");
+                sb.append("The global policy file is identified by the java.security.policy variable. ");
+                WikiSecurityException wse = new WikiSecurityException(sb.toString());
+                log.fatal(sb.toString(), wse);
+                throw wse;
+            }
+        }
+        catch ( PolicyException e )
+        {
+            log.error("Could not initialize local security policy: " + e.getMessage() );
+            throw new WikiException( e.getMessage() );
+        }
+    }
+
+    /**
+     * Returns <code>true</code> if JSPWiki's JAAS authorization system
+     * is used for authorization in addition to container controls.
+     * @return the result
+     */
+    protected boolean isJAASAuthorized()
+    {
+        return m_useJAAS;
+    }
+
+    /**
+     * 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 final Authorizer getAuthorizerImplementation( Properties props ) throws WikiException
+    {
+        String authClassName = props.getProperty( PROP_AUTHORIZER, DEFAULT_AUTHORIZER );
+        return (Authorizer) locateImplementation( authClassName );
+    }
+
+    private final Object locateImplementation( String clazz ) throws WikiException
+    {
+        if ( clazz != null )
+        {
+            try
+            {
+                Class authClass = ClassUtil.findClass( "com.ecyrd.jspwiki.auth.authorize", clazz );
+                Object impl = authClass.newInstance();
+                return impl;
+            }
+            catch( ClassNotFoundException e )
+            {
+                log.fatal( "Authorizer " + clazz + " cannot be found", e );
+                throw new WikiException( "Authorizer " + clazz + " cannot be found" );
+            }
+            catch( InstantiationException e )
+            {
+                log.fatal( "Authorizer " + clazz + " cannot be created", e );
+                throw new WikiException( "Authorizer " + clazz + " cannot be created" );
+            }
+            catch( 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" );
+            }
+        }
+
+        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( Principal[] principals, Permission permission )
+    {
+        for ( int i = 0; i < principals.length; i++ )
+        {
+            // Get ProtectionDomain for this Principal from cache, or create new one
+            ProtectionDomain pd = (ProtectionDomain)m_cachedPds.get( principals[i] );
+            if ( pd == null )
+            {
+                ClassLoader cl = this.getClass().getClassLoader();
+                CodeSource cs = new CodeSource( null, (Certificate[])null );
+                pd = new ProtectionDomain( cs, null, cl, new Principal[]{ principals[i] } );
+                m_cachedPds.put( principals[i], pd );
+            }
+
+            // Consult the local policy and get the answer
+            if ( m_localPolicy.implies( pd, permission ) )
+            {
+                return true;
+            }
+        }
+        return false;
+    }
+
+    /**
+     * Determines whether a Subject posesses 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
+     * excuting <code>Subject.doAsPrivileged</code> with a privileged action
+     * that simply calls {@link java.security.AccessController#checkPermission(Permission)}.
+     * @link 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 posesses the permission,
+     *         <code>false</code> otherwise
+     */
+    protected final boolean checkStaticPermission( final WikiSession session, final Permission permission )
+    {
+        if( !m_useJAAS ) return true;
+
+        Boolean allowed = (Boolean)WikiSession.doPrivileged( session, new PrivilegedAction()
+        {
+            public Object run()
+            {
+                try
+                {
+                    // Check the JVM-wide security policy first
+                    AccessController.checkPermission( permission );
+                    return Boolean.TRUE;
+                }
+                catch( 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>
+     * <ol>
+     * <li>If the name matches one of the built-in {@link com.ecyrd.jspwiki.auth.authorize.Role} names,
+     * return that built-in Role</li>
+     * <li>If the name matches one supplied by the current
+     * {@link com.ecyrd.jspwiki.auth.Authorizer}, return that Role</li>
+     * <li>If the name matches a group managed by the
+     * current {@link com.ecyrd.jspwiki.auth.authorize.GroupManager}, return that Group</li>
+     * <li>Otherwise, assume that the name represents a user
+     * principal. Using the current {@link com.ecyrd.jspwiki.auth.user.UserDatabase}, find the
+     * first user who matches the supplied name by calling
+     * {@link com.ecyrd.jspwiki.auth.user.UserDatabase#find(String)}.</li>
+     * <li>Finally, if a user cannot be found, manufacture
+     * and return a generic {@link com.ecyrd.jspwiki.auth.acl.UnresolvedPrincipal}</li>
+     * </ol>
+     * @param name the name of the Principal to resolve
+     * @return the fully-resolved Principal
+     */
+    public final Principal resolvePrincipal( String name )
+    {
+        if( !m_useJAAS )
+        {
+            return new UnresolvedPrincipal(name);
+        }
+
+        // Check built-in Roles first
+        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;
+        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( NoSuchPrincipalException e )
+        {
+            // We couldn't find the user...
+        }
+        // Ok, no luck---mark this as unresolved and move on
+        return new UnresolvedPrincipal( name );
+    }
+
+
+    // events processing .......................................................
+
+    /**
+     * Registers a WikiEventListener with this instance.
+     * @param listener the event listener
+     */
+    public final synchronized void addWikiEventListener( WikiEventListener listener )
+    {
+        WikiEventManager.addWikiEventListener( this, listener );
+    }
+
+    /**
+     * Un-registers a WikiEventListener with this instance.
+     * @param listener the event listener
+     */
+    public final synchronized void removeWikiEventListener( WikiEventListener listener )
+    {
+        WikiEventManager.removeWikiEventListener( this, listener );
+    }
+
+    /**
+     *  Fires a WikiSecurityEvent of the provided type, user,
+     *  and permission to all registered listeners.
+     *
+     * @see com.ecyrd.jspwiki.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 final void fireEvent( int type, Principal user, Object permission )
+    {
+        if ( WikiEventManager.isListening(this) )
+        {
+            WikiEventManager.fireEvent(this,new WikiSecurityEvent(this,type,user,permission));
+        }
+    }
+
+}

Added: incubator/jspwiki/branches/JSPWIKI_STRIPES_BRANCH/src/com/ecyrd/jspwiki/auth/Authorizer.java
URL: http://svn.apache.org/viewvc/incubator/jspwiki/branches/JSPWIKI_STRIPES_BRANCH/src/com/ecyrd/jspwiki/auth/Authorizer.java?rev=627255&view=auto
==============================================================================
--- incubator/jspwiki/branches/JSPWIKI_STRIPES_BRANCH/src/com/ecyrd/jspwiki/auth/Authorizer.java (added)
+++ incubator/jspwiki/branches/JSPWIKI_STRIPES_BRANCH/src/com/ecyrd/jspwiki/auth/Authorizer.java Tue Feb 12 21:53:55 2008
@@ -0,0 +1,76 @@
+/*
+    JSPWiki - a JSP-based WikiWiki clone.
+
+    Copyright (C) 2001-2007 Janne Jalkanen (Janne.Jalkanen@iki.fi)
+
+    This program is free software; you can redistribute it and/or modify
+    it under the terms of the GNU Lesser General Public License as published by
+    the Free Software Foundation; either version 2.1 of the License, or
+    (at your option) any later version.
+
+    This program is distributed in the hope that it will be useful,
+    but WITHOUT ANY WARRANTY; without even the implied warranty of
+    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+    GNU Lesser General Public License for more details.
+
+    You should have received a copy of the GNU Lesser General Public License
+    along with this program; if not, write to the Free Software
+    Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ */
+package com.ecyrd.jspwiki.auth;
+
+import java.security.Principal;
+import java.util.Properties;
+
+import com.ecyrd.jspwiki.WikiEngine;
+import com.ecyrd.jspwiki.WikiSession;
+
+/**
+ * Interface for service providers of authorization information.
+ * @author Andrew Jaquith
+ * @since 2.3
+ */
+public interface Authorizer
+{
+
+    /**
+     * Returns an array of role Principals this Authorizer knows about.
+     * This method will always return an array; an implementing class may
+     * choose to return an zero-length array if it has no ability to identify
+     * the roles under its control.
+     * @return an array of Principals representing the roles
+     */
+    public Principal[] getRoles();
+
+    /**
+     * Looks up and returns a role Principal matching a given String.
+     * If a matching role cannot be found, this method returns <code>null</code>.
+     * Note that it may not always be feasible for an Authorizer
+     * implementation to return a role Principal.
+     * @param role the name of the role to retrieve
+     * @return the role Principal
+     */
+    public Principal findRole( String role );
+
+    /**
+     * Initializes the authorizer.
+     * @param engine the current wiki engine
+     * @param props the wiki engine initialization properties
+     * @throws WikiSecurityException if the Authorizer could not be initialized
+     */
+    public void initialize( WikiEngine engine, Properties props ) throws WikiSecurityException;
+
+    /**
+     * Determines whether the Subject associated with a WikiSession is in a
+     * particular role. This method takes two parameters: the WikiSession
+     * containing the subject and the desired role ( which may be a Role or a
+     * Group). If either parameter is <code>null</code>, this method must
+     * return <code>false</code>.
+     * @param session the current WikiSession
+     * @param role the role to check
+     * @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 );
+
+}

Added: incubator/jspwiki/branches/JSPWIKI_STRIPES_BRANCH/src/com/ecyrd/jspwiki/auth/GroupPrincipal.java
URL: http://svn.apache.org/viewvc/incubator/jspwiki/branches/JSPWIKI_STRIPES_BRANCH/src/com/ecyrd/jspwiki/auth/GroupPrincipal.java?rev=627255&view=auto
==============================================================================
--- incubator/jspwiki/branches/JSPWIKI_STRIPES_BRANCH/src/com/ecyrd/jspwiki/auth/GroupPrincipal.java (added)
+++ incubator/jspwiki/branches/JSPWIKI_STRIPES_BRANCH/src/com/ecyrd/jspwiki/auth/GroupPrincipal.java Tue Feb 12 21:53:55 2008
@@ -0,0 +1,95 @@
+/*
+ * JSPWiki - a JSP-based WikiWiki clone. Copyright (C) 2001-2003 Janne Jalkanen
+ * (Janne.Jalkanen@iki.fi) This program is free software; you can redistribute
+ * it and/or modify it under the terms of the GNU Lesser General Public License
+ * as published by the Free Software Foundation; either version 2.1 of the
+ * License, or (at your option) any later version. This program is distributed
+ * in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even
+ * the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
+ * See the GNU Lesser General Public License for more details. You should have
+ * received a copy of the GNU Lesser General Public License along with this
+ * program; if not, write to the Free Software Foundation, Inc., 59 Temple
+ * Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+package com.ecyrd.jspwiki.auth;
+
+import java.security.Principal;
+
+/**
+ * Immutable Principal that represents a Group. GroupPrincipals are injected
+ * into a Subject's principal list at the time of authentication (login), and
+ * serve as proxies for Group objects for the purposes of making Java 2 security
+ * policy decisions. We add GroupPrincipals instead of the actual Groups because
+ * calling classes should never be able to obtain a mutable object (Group
+ * memberships can be changed by callers). Administrators who wish to grant
+ * privileges to specific wiki groups via the security policy file should always specify
+ * principals of type GroupPrincipal.
+ * @see com.ecyrd.jspwiki.auth.authorize.Group
+ * @author Andrew Jaquith
+ * @since 2.3.79
+ */
+public final class GroupPrincipal implements Principal
+{
+    private final String m_name;
+
+    /**
+     * Constructs a new GroupPrincipal object with a supplied name.
+     *
+     * @param group the wiki group; cannot be <code>null</code>
+     */
+    public GroupPrincipal( String group )
+    {
+        if ( group == null )
+        {
+            throw new IllegalArgumentException( "Group parameter cannot be null." );
+        }
+        m_name = group;
+    }
+    
+    /**
+     * Returns the name of the group principal.
+     * @return the name
+     * @see java.security.Principal#getName()
+     */
+    public final String getName()
+    {
+        return m_name;
+    }
+
+    /**
+     * Two GroupPrincipals are equal if their names are equal.
+     * @param obj the object to compare
+     * @return the result of the equality test
+     * @see java.lang.Object#equals(java.lang.Object)
+     */
+    public final boolean equals( Object obj )
+    {
+        if ( !( obj instanceof GroupPrincipal ) )
+        {
+            return false;
+        }
+        GroupPrincipal p = (GroupPrincipal)obj;
+        return p.m_name.equals( m_name );
+    }
+
+    /**
+     * Returns the hashcode for this object.
+     * @return the hash code
+     * @see java.lang.Object#hashCode()
+     */
+    public final int hashCode()
+    {
+        return m_name.hashCode();
+    }
+
+    /**
+     * Returns a string representation of this object.
+     * @return the string
+     * @see java.lang.Object#toString()
+     */
+    public final String toString()
+    {
+        return "[GroupPrincipal " + m_name + "]";
+    }
+
+}

Added: incubator/jspwiki/branches/JSPWIKI_STRIPES_BRANCH/src/com/ecyrd/jspwiki/auth/NoSuchPrincipalException.java
URL: http://svn.apache.org/viewvc/incubator/jspwiki/branches/JSPWIKI_STRIPES_BRANCH/src/com/ecyrd/jspwiki/auth/NoSuchPrincipalException.java?rev=627255&view=auto
==============================================================================
--- incubator/jspwiki/branches/JSPWIKI_STRIPES_BRANCH/src/com/ecyrd/jspwiki/auth/NoSuchPrincipalException.java (added)
+++ incubator/jspwiki/branches/JSPWIKI_STRIPES_BRANCH/src/com/ecyrd/jspwiki/auth/NoSuchPrincipalException.java Tue Feb 12 21:53:55 2008
@@ -0,0 +1,40 @@
+/* 
+    JSPWiki - a JSP-based WikiWiki clone.
+
+    Copyright (C) 2001-2003 Janne Jalkanen (Janne.Jalkanen@iki.fi)
+
+    This program is free software; you can redistribute it and/or modify
+    it under the terms of the GNU Lesser General Public License as published by
+    the Free Software Foundation; either version 2.1 of the License, or
+    (at your option) any later version.
+
+    This program is distributed in the hope that it will be useful,
+    but WITHOUT ANY WARRANTY; without even the implied warranty of
+    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+    GNU Lesser General Public License for more details.
+
+    You should have received a copy of the GNU Lesser General Public License
+    along with this program; if not, write to the Free Software
+    Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ */
+package com.ecyrd.jspwiki.auth;
+
+/**
+ *  Thrown in some error situations where a WikiPrincipal object does not exist.
+ *  @author Andrew Jaquith
+ *  @since 2.3
+ */
+public final class NoSuchPrincipalException
+    extends WikiSecurityException
+{
+    private static final long serialVersionUID = 3257849895976186169L;
+
+    /**
+     * Constructs a new exception object with a supplied message.
+     * @param msg the message
+     */
+    public NoSuchPrincipalException( String msg )
+    {
+        super(msg);
+    }
+}

Added: incubator/jspwiki/branches/JSPWIKI_STRIPES_BRANCH/src/com/ecyrd/jspwiki/auth/PolicyLoader.java
URL: http://svn.apache.org/viewvc/incubator/jspwiki/branches/JSPWIKI_STRIPES_BRANCH/src/com/ecyrd/jspwiki/auth/PolicyLoader.java?rev=627255&view=auto
==============================================================================
--- incubator/jspwiki/branches/JSPWIKI_STRIPES_BRANCH/src/com/ecyrd/jspwiki/auth/PolicyLoader.java (added)
+++ incubator/jspwiki/branches/JSPWIKI_STRIPES_BRANCH/src/com/ecyrd/jspwiki/auth/PolicyLoader.java Tue Feb 12 21:53:55 2008
@@ -0,0 +1,373 @@
+/*
+   JSPWiki - a JSP-based WikiWiki clone.
+
+   Copyright (C) 2001-2007 Janne Jalkanen (Janne.Jalkanen@iki.fi)
+
+   This program is free software; you can redistribute it and/or modify
+   it under the terms of the GNU Lesser General Public License as published by
+   the Free Software Foundation; either version 2.1 of the License, or
+   (at your option) any later version.
+
+   This program is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+   GNU Lesser General Public License for more details.
+
+   You should have received a copy of the GNU Lesser General Public License
+   along with this program; if not, write to the Free Software
+   Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ */
+package com.ecyrd.jspwiki.auth;
+
+import java.io.File;
+import java.net.URL;
+import java.security.AccessController;
+import java.security.Policy;
+import java.security.PrivilegedAction;
+import java.security.Security;
+
+import javax.security.auth.login.Configuration;
+
+import org.apache.log4j.Logger;
+
+/**
+ * <p>
+ * Initializes JVM configurations for JAAS and Java 2 security policy. Callers
+ * can use the static methods in this class ({@link #isJaasConfigured()}
+ * &nbsp;and {@link #isSecurityPolicyConfigured()}) to inquire whether a JAAS
+ * login configuration exists, or whether a custom Java security policy is in
+ * use. Additional methods allow callers to set the JAAS and security policy
+ * configurations to supplied URLs ({@link #setJaasConfiguration(URL)}
+ * &nbsp;and {@link #setSecurityPolicy(URL)}).
+ * </p>
+ * <p>
+ * If either the JAAS configuration and security policy are set using methods in
+ * this class, the resulting configuration or policy is <i>global</i> to the
+ * JVM. Thus, in a multi-webapp scenario, this means that the first webapp to be
+ * loaded by the container wins. Thus, for containers hosting multiple wikis,
+ * the administrator will need to manually configure the
+ * <code>java.security.policy</code> and
+ * <code>java.security.auth.login.config properties</code>. In other words,
+ * multi-wiki deployments will always require manual (one-time) configuration.
+ * </p>
+ * <p>
+ * The security policy-related methods {@link #isSecurityPolicyConfigured()}
+ * &nbsp;and {@link #setSecurityPolicy(URL)}) assumes that the web container
+ * doesn't use a "double-equals" command-line assignment
+ * to override the security policy ( <i>e.g. </i>,
+ * <code>-Djava.security.policy==jspwiki.policy</code>). Note that Tomcat 4
+ * and higher, when run using the "-security" option, does this.
+ * </p>
+ * <p>
+ * To interoperate with <i>any</i> container running with a security manager, the
+ * container's JVM security policy should include a short set of permission
+ * grant similar to the following:
+ * </p>
+ * <blockquote><code>keystore "jspwiki.jks";<br/>
+ * &nbsp;&nbsp;...<br/>
+ * grant signedBy "jspwiki" {<br/>
+ * &nbsp;&nbsp;permission java.security.SecurityPermission, "getPolicy";<br/>
+ * &nbsp;&nbsp;permission java.security.SecurityPermission, "setPolicy";<br/>
+ * &nbsp;&nbsp;permission java.util.PropertyPermission "java.security.auth.login.config", "write";<br/>
+ * &nbsp;&nbsp;permission java.util.PropertyPermission "java.security.policy", "read,write";<br/>
+ * &nbsp;&nbsp;permission javax.security.auth.AuthPermission, "getLoginConfiguration";<br/>
+ * &nbsp;&nbsp;permission javax.security.auth.AuthPermission, "setLoginConfiguration";<br/>
+ * };</code>
+ * </blockquote>
+ * <p>
+ * The <code>signedBy</code> value should match the alias of a digital
+ * certificate in the named keystore ( <i>e.g. </i>, <code>jspwiki.jks</code>).
+ * If the full path to the keystore is not suppled, it is assumed to be in the
+ * same directory as the policy file.
+ * </p>
+ *
+ * @author Andrew Jaquith
+ * @since 2.3
+ */
+public final class PolicyLoader
+{
+    protected static final Logger log = Logger.getLogger( PolicyLoader.class );
+
+    /**
+     * Private constructor to prevent direct instantiation.
+     */
+    private PolicyLoader()
+    {
+    }
+
+    /**
+     * <p>
+     * Returns <code>true</code> if the JAAS login configuration exists.
+     * Normally, JAAS is configured by setting the system property
+     * <code>java.security.auth.login.config</code> at JVM startup.
+     * </p>
+     * <p>
+     * This method attempts to perform a highly privileged operation. If the JVM
+     * runs with a SecurityManager, the following permission must be granted to
+     * the codesource containing this class:
+     * </p>
+     * <code><ul>
+     * <li>permission javax.security.auth.AuthPermission,
+     * "getLoginConfiguration"</li>
+     * </ul></code>
+     *
+     * @return <code>true</code> if
+     *         {@link javax.security.auth.login.Configuration#getConfiguration()}
+     *         is not <code>null</code> ;&nbsp; <code>false</code> otherwise.
+     * @throws SecurityException if the codesource containing this class posesses
+     *           insufficient permmissions when running with a SecurityManager
+     */
+    public static final boolean isJaasConfigured() throws SecurityException
+    {
+        Boolean configured = (Boolean) AccessController
+        .doPrivileged(new PrivilegedAction() {
+
+            public Object run()
+            {
+                boolean isConfigured = false;
+                try
+                {
+                    Configuration config = Configuration.getConfiguration();
+                    isConfigured = config != null;
+                }
+                catch (SecurityException e) {}
+                return Boolean.valueOf(isConfigured);
+            }
+        });
+        return configured.booleanValue();
+    }
+
+    /**
+     * <p>
+     * Returns <code>true</code> if a custom Java security policy configuration
+     * exists. Normally, the Java security policy is configured by setting the
+     * system property <code>java.security.policy</code> at JVM startup.
+     * </p>
+     * <p>
+     * This method attempts to perform a highly privileged operation. If the JVM
+     * runs with a SecurityManager, the following permission must be granted to
+     * the codesource containing this class:
+     * </p>
+     * <code><ul>
+     * <li>permission java.util.PropertyPermission
+     * "java.security.policy", "read"</li>
+     * </ul></code>
+     *
+     * @return <code>true</code> if the system property
+     *         <code>java.security.policy</code> is not <code>null</code>;
+     *         &nbsp; <code>false</code> otherwise.
+     * @throws SecurityException if the codesource containing this class posesses
+     *           insufficient permmissions when running with a SecurityManager
+     */
+    public static final boolean isSecurityPolicyConfigured() throws SecurityException
+    {
+        String policy = (String) AccessController
+        .doPrivileged(new PrivilegedAction() {
+
+            public Object run()
+            {
+                return System.getProperty("java.security.policy");
+            }
+        });
+
+        if ( policy != null )
+        {
+            log.info( "Java security policy already set to: " + policy + ". (Leaving it alone...)" );
+
+            //
+            //  Do a bit of a sanity checks to help people who are not familiar with JAAS.
+            //
+            if( policy.startsWith("file:") ) policy = policy.substring("file:".length());
+
+            File f = new File(policy);
+
+            if( !f.exists() )
+            {
+                log.warn("You have set your 'java.security.policy' to point at '"+f.getAbsolutePath()+"', "+
+                         "but that file does not seem to exist.  I'll continue anyway, since this may be "+
+                         "something specific to your servlet container.  Just consider yourself warned." );
+            }
+
+            File jks = new File( f.getParentFile(), "jspwiki.jks" );
+
+            if( !jks.exists() || !jks.canRead() )
+            {
+                log.warn("I could not locate the JSPWiki keystore ('jspwiki.jks') in the same directory "+
+                         "as your jspwiki.policy file. On many servlet containers, such as Tomcat, this "+
+                         "needs to be done.  If you keep having access right permissions, please try "+
+                         "copying your WEB-INF/jspwiki.jks to "+f.getParentFile().getAbsolutePath() );
+            }
+            else
+            {
+                log.info("Found 'jspwiki.jks' from '"+f.getParentFile().getAbsolutePath()+"'.  If you are having "+
+                         "permission issues after an upgrade, please make sure that this file matches the one that "+
+                         "came with your distribution archive.");
+            }
+
+        }
+        return policy != null;
+    }
+
+    /**
+     * Sets the JAAS login configuration file, overwriting the existing
+     * configuration. If the configuration file pointed to by the URL does not
+     * exist, a SecurityException is thrown.
+     * <p>
+     * This method attempts to perform several highly privileged operations. If
+     * the JVM runs with a SecurityManager, the following permissions must be
+     * granted to the codesource containing this class:
+     * </p>
+     * <code><ul>
+     * <li>permission java.util.PropertyPermission
+     * "java.security.auth.login.config", "write"</li>
+     * <li>permission javax.security.auth.AuthPermission,
+     * "getLoginConfiguration"</li>
+     * <li>permission javax.security.auth.AuthPermission,
+     * "setLoginConfiguration"</li>
+     * </ul></code>
+     *
+     * @param url the URL of the login configuration file. If the URL contains
+     *          properties such as <code>${java.home}</code>, they will be
+     *          expanded.
+     * @throws SecurityException if:
+     *           <ul>
+     *           <li>the supplied URL is <code>null</code></li>
+     *           <li>properties cannot be expanded</li>
+     *           <li>the codesource containing this class does not posesses
+     *           sufficient permmissions when running with a SecurityManager</li>
+     *           </ul>
+     */
+    public static final void setJaasConfiguration(final URL url)
+    throws SecurityException
+    {
+        if (url == null)
+        {
+            throw new SecurityException("URL for JAAS configuration cannot be null.");
+        }
+
+        // Get JAAS configuration class; default is Sun provider
+        String defaultConfigClass;
+        defaultConfigClass = (String)AccessController.doPrivileged(
+            new PrivilegedAction()
+            {
+                public Object run()
+                {
+                    return Security.getProperty("login.configuration.provider");
+                }
+            });
+
+        if (defaultConfigClass == null)
+        {
+            defaultConfigClass = "com.sun.security.auth.login.ConfigFile";
+        }
+
+        // Now, set the new config
+        final String config_class = defaultConfigClass;
+        AccessController.doPrivileged(new PrivilegedAction() {
+
+            public Object run()
+            {
+                // Remove old policy, then set our config URL and instantiate new instance
+                try
+                {
+                    Configuration.setConfiguration(null);
+                    System.setProperty("java.security.auth.login.config", url.toExternalForm());
+                    Configuration config = (Configuration)Class.forName(config_class).newInstance();
+                    Configuration.setConfiguration(config);
+                    return null;
+                }
+                catch (Exception e)
+                {
+                    throw new SecurityException(e.getMessage());
+                }
+            }
+        });
+    }
+
+    /**
+     * <p>
+     * Sets the Java security policy, overwriting any custom policy settings. This
+     * method sets the value of the system property
+     * <code>java.security.policy</code> to the supplied URL, then calls
+     * {@link java.security.Policy#setPolicy(java.security.Policy)}&nbsp;with a
+     * newly-instantiated instance of
+     * <code>sun.security.provider.PolicyFile</code> (the J2SE default
+     * implementation). The new Policy, once set, reloads the default system
+     * policies enumerated by the <code>policy.url.<i>n</i></code> entries in
+     * <code><i>JAVA_HOME</i>/lib/security/java.policy</code>, followed by the
+     * user-supplied policy file.
+     * </p>
+     * <p>
+     * This method attempts to perform several highly privileged operations. If
+     * the JVM runs with a SecurityManager, the following permissions must be
+     * granted to the codesource containing this class:
+     * </p>
+     * <code><ul>
+     * <li>permission java.security.SecurityPermission, "getPolicy"
+     * </li>
+     * <li>permission java.security.SecurityPermission, "setPolicy"
+     * </li>
+     * <li>permission java.util.PropertyPermission}
+     * "java.security.policy", "write"</li>
+     * </ul></code>
+     *
+     * @param url the URL of the security policy file. If the URL contains
+     *          properties such as <code>${java.home}</code>, they will be
+     *          expanded.
+     * @throws SecurityException if:
+     *           <ul>
+     *           <li>the supplied URL is <code>null</code></li>
+     *           <li>properties cannot be expanded</li>
+     *           <li>the codesource containing this class does not posesses
+     *           sufficient permmissions when running with a SecurityManager</li>
+     *           <li>the JVM's current Policy implementation is not of type
+     *           <code>sun.security.provider.PolicyFile</code></li>
+     *           </ul>
+     */
+    public static final void setSecurityPolicy(final URL url) throws SecurityException
+    {
+        if (url == null)
+        {
+            throw new SecurityException("URL for security policy cannot be null.");
+        }
+
+        // Get policy class; default is Sun provider
+        String defaultPolicyClass;
+        defaultPolicyClass = (String)AccessController.doPrivileged(
+            new PrivilegedAction() {
+                public Object run()
+                {
+                    return Security.getProperty("policy.provider");
+                }
+            });
+
+        if (defaultPolicyClass == null)
+        {
+            defaultPolicyClass = "sun.security.provider.PolicyFile";
+        }
+
+        // Now, set the new policy
+        final String policyClass = defaultPolicyClass;
+        AccessController.doPrivileged(new PrivilegedAction() {
+
+            public Object run()
+            {
+                // Remove old policy, then set our policy URL and instantiate new instance
+                try
+                {
+                    Policy.setPolicy(null);
+                    System.setProperty("java.security.policy", url.toExternalForm());
+
+                    Policy policy = (Policy)Class.forName(policyClass).newInstance();
+                    Policy.setPolicy(policy);
+                    return null;
+                }
+                catch (Exception e)
+                {
+                    throw new SecurityException(e.getMessage());
+                }
+            }
+        });
+    }
+
+}

Propchange: incubator/jspwiki/branches/JSPWIKI_STRIPES_BRANCH/src/com/ecyrd/jspwiki/auth/PolicyLoader.java
------------------------------------------------------------------------------
    svn:executable = *

Added: incubator/jspwiki/branches/JSPWIKI_STRIPES_BRANCH/src/com/ecyrd/jspwiki/auth/PrincipalComparator.java
URL: http://svn.apache.org/viewvc/incubator/jspwiki/branches/JSPWIKI_STRIPES_BRANCH/src/com/ecyrd/jspwiki/auth/PrincipalComparator.java?rev=627255&view=auto
==============================================================================
--- incubator/jspwiki/branches/JSPWIKI_STRIPES_BRANCH/src/com/ecyrd/jspwiki/auth/PrincipalComparator.java (added)
+++ incubator/jspwiki/branches/JSPWIKI_STRIPES_BRANCH/src/com/ecyrd/jspwiki/auth/PrincipalComparator.java Tue Feb 12 21:53:55 2008
@@ -0,0 +1,50 @@
+/*
+   JSPWiki - a JSP-based WikiWiki clone.
+
+   Copyright (C) 2001-2007 Janne Jalkanen (Janne.Jalkanen@iki.fi)
+
+   This program is free software; you can redistribute it and/or modify
+   it under the terms of the GNU Lesser General Public License as published by
+   the Free Software Foundation; either version 2.1 of the License, or
+   (at your option) any later version.
+
+   This program is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+   GNU Lesser General Public License for more details.
+
+   You should have received a copy of the GNU Lesser General Public License
+   along with this program; if not, write to the Free Software
+   Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ */
+package com.ecyrd.jspwiki.auth;
+
+import java.io.Serializable;
+import java.security.Principal;
+import java.text.Collator;
+import java.util.Comparator;
+
+/**
+ * Comparator class for sorting objects of type Principal.
+ * Used for sorting arrays or collections of Principals.
+ * @since 2.3
+ */
+public class PrincipalComparator 
+    implements Comparator<Principal>, Serializable 
+{
+    private static final long serialVersionUID = 1L;
+
+    /**
+     * Compares two Principal objects.
+     * @param o1 the first Principal
+     * @param o2 the second Principal
+     * @return the result of the comparison
+     * @see java.util.Comparator#compare(Object, Object)
+     */
+    public int compare( Principal o1, Principal o2 )
+    {
+        Collator collator = Collator.getInstance();
+        return collator.compare( o1.getName(), o2.getName() );
+    }
+
+}