WikiSession.java :  » Wiki-Engine » JSPWiki » com » ecyrd » jspwiki » Java Open Source

Java Open Source » Wiki Engine » JSPWiki 
JSPWiki » com » ecyrd » jspwiki » WikiSession.java
/*
    JSPWiki - a JSP-based WikiWiki clone.

    Copyright (C) 2001-2005 The JSPWiki Development Group

    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;

import java.security.AccessControlException;
import java.security.Principal;
import java.security.PrivilegedAction;
import java.util.*;

import javax.security.auth.Subject;
import javax.security.auth.callback.CallbackHandler;
import javax.security.auth.login.LoginContext;
import javax.security.auth.login.LoginException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpSession;

import org.apache.log4j.Logger;

import com.ecyrd.jspwiki.auth.*;
import com.ecyrd.jspwiki.auth.authorize.Group;
import com.ecyrd.jspwiki.auth.authorize.GroupManager;
import com.ecyrd.jspwiki.auth.authorize.Role;
import com.ecyrd.jspwiki.auth.login.CookieAssertionLoginModule;
import com.ecyrd.jspwiki.auth.login.PrincipalWrapper;
import com.ecyrd.jspwiki.auth.user.UserDatabase;
import com.ecyrd.jspwiki.auth.user.UserProfile;
import com.ecyrd.jspwiki.event.WikiEvent;
import com.ecyrd.jspwiki.event.WikiEventListener;
import com.ecyrd.jspwiki.event.WikiSecurityEvent;

/**
 * <p>Represents a long-running wiki session, with an associated user Principal,
 * user Subject, and authentication status. This class is initialized with
 * minimal, default-deny values: authentication is set to <code>false</code>,
 * and the user principal is set to <code>null</code>.</p>
 * <p>The WikiSession class allows callers to:</p>
 * <ul>
 *   <li>Obtain the authentication status of the user via
 *     {@link #isAnonymous()} and {@link #isAuthenticated()}</li>
 *   <li>Query the session for Principals representing the
 *     user's identity via {@link #getLoginPrincipal()},
 *     {@link #getUserPrincipal()} and {@link #getPrincipals()}</li>
 *   <li>Store, retrieve and clear UI messages via
 *     {@link #addMessage(String)}, {@link #getMessages(String)}
 *     and {@link #clearMessages(String)}</li>
 * </ul>
 * <p>To keep track of the Principals each user posseses, each WikiSession
 * stores a JAAS Subject. Various login processes add or remove Principals
 * when users authenticate or log out.</p>
 * <p>WikiSession implements the {@link com.ecyrd.jspwiki.event.WikiEventListener}
 * interface and listens for group add/change/delete events fired by
 * event sources the WikiSession is registered with. Normally,
 * {@link com.ecyrd.jspwiki.auth.AuthenticationManager} registers each WikiSession
 * with the {@link com.ecyrd.jspwiki.auth.authorize.GroupManager}
 * so it can catch group events. Thus, when a user is added to a
 * {@link com.ecyrd.jspwiki.auth.authorize.Group}, a corresponding
 * {@link com.ecyrd.jspwiki.auth.GroupPrincipal} is injected into
 * the Subject's Principal set. Likewise, when the user is removed from
 * the Group or the Group is deleted, the GroupPrincipal is removed
 * from the Subject. The effect that this strategy produces is extremely
 * beneficial: when someone adds a user to a wiki group, that user
 * <em>immediately</em> gains the privileges associated with that
 * group; he or she does not need to re-authenticate.
 * </p>
 * <p>In addition to methods for examining individual <code>WikiSession</code>
 * objects, this class also contains a number of static methods for
 * managing WikiSessions for an entire wiki. These methods allow callers
 * to find, query and remove WikiSession objects, and
 * to obtain a list of the current wiki session users.</p>
 * <p>WikiSession encloses a protected static class, {@link SessionMonitor},
 * to keep track of WikiSessions registered with each wiki.</p>
 * @author Andrew R. Jaquith
 */
public final class WikiSession implements WikiEventListener
{

    /** An anonymous user's session status. */
    public static final String  ANONYMOUS             = "anonymous";

    /** An asserted user's session status. */
    public static final String  ASSERTED              = "asserted";

    /** An authenticated user's session status. */
    public static final String  AUTHENTICATED         = "authenticated";

    private static final int    ONE                   = 48;

    private static final int    NINE                  = 57;

    private static final int    DOT                   = 46;

    private static final Logger log                   = Logger.getLogger( WikiSession.class );

    private static final String ALL                   = "*";

    private static ThreadLocal  c_guestSession        = new ThreadLocal();

    private final Subject       m_subject             = new Subject();

    private final Map           m_messages            = new HashMap();

    private String              m_cachedCookieIdentity= null;

    private String              m_cachedRemoteUser    = null;

    private Principal           m_cachedUserPrincipal = null;

    /** The WikiEngine that created this session. */
    private WikiEngine          m_engine              = null;

    private boolean             m_isNew               = true;

    private String              m_status              = ANONYMOUS;

    private Principal           m_userPrincipal       = WikiPrincipal.GUEST;

    private Principal           m_loginPrincipal      = WikiPrincipal.GUEST;

    private Locale              m_cachedLocale        = Locale.getDefault();

    /**
     * Returns <code>true</code> if one of this WikiSession's user Principals
     * can be shown to belong to a particular wiki group. If the user is
     * not authenticated, this method will always return <code>false</code>.
     * @param group the group to test
     * @return the result
     */
    protected final boolean isInGroup( Group group )
    {
        Principal[] principals = getPrincipals();
        for ( int i = 0; i < principals.length; i++ )
        {
          if ( isAuthenticated() && group.isMember( principals[i] ) )
          {
              return true;
          }
        }
        return false;
    }

    /**
     * Returns <code>true</code> if the wiki session is newly initialized.
     *
     * @return True, if this is a new session.
     */
    protected final boolean isNew()
    {
        return m_isNew;
    }

    /**
     * Sets the status of this wiki session.
     * @param isNew whether this session should be considered "new".
     */
    protected final void setNew( boolean isNew )
    {
        m_isNew = isNew;
    }

    /**
     * Private constructor to prevent WikiSession from being instantiated
     * directly.
     */
    private WikiSession()
    {
    }

    /**
     * Returns <code>true</code> if the user is considered asserted via
     * a session cookie; that is, the Subject contains the Principal
     * Role.ASSERTED.
     * @return Returns <code>true</code> if the user is asserted
     */
    public final boolean isAsserted()
    {
        return m_subject.getPrincipals().contains( Role.ASSERTED );
    }

    /**
     * Returns the authentication status of the user's session. The user is
     * considered authenticated if the Subject contains the Principal
     * Role.AUTHENTICATED. If this method determines that an earlier
     * LoginModule did not inject Role.AUTHENTICATED, it will inject one
     * if the user is not anonymous <em>and</em> not asserted.
     * @return Returns <code>true</code> if the user is authenticated
     */
    public final boolean isAuthenticated()
    {
        // If Role.AUTHENTICATED is in principals set, always return true.
        if ( m_subject.getPrincipals().contains( Role.AUTHENTICATED ) )
        {
            return true;
        }

        // With non-JSPWiki LoginModules, the role may not be there, so
        // we need to add it if the user really is authenticated.
        if ( !isAnonymous() && !isAsserted() )
        {
            // Inject AUTHENTICATED role
            m_subject.getPrincipals().add( Role.AUTHENTICATED );
            return true;
        }

        return false;
    }

    /**
     * <p>Determines whether the current session is anonymous. This will be
     * true if any of these conditions are true:</p>
     * <ul>
     *   <li>The session's Principal set contains
     *       {@link com.ecyrd.jspwiki.auth.authorize.Role#ANONYMOUS}</li>
     *   <li>The session's Principal set contains
     *       {@link com.ecyrd.jspwiki.auth.WikiPrincipal#GUEST}</li>
     *   <li>The Principal returned by {@link #getUserPrincipal()} evaluates
     *       to an IP address.</li>
     * </ul>
     * <p>The criteria above are listed in the order in which they are
     * evaluated.</p>
     * @return whether the current user's identity is equivalent to an IP
     * address
     */
    public final boolean isAnonymous()
    {
        Set principals = m_subject.getPrincipals();
        return principals.contains( Role.ANONYMOUS ) ||
                 principals.contains( WikiPrincipal.GUEST ) ||
                 isIPV4Address( getUserPrincipal().getName() );
    }

    /**
     * Creates and returns a new login context for this wiki session.
     * This method is called by
     * {@link com.ecyrd.jspwiki.auth.AuthenticationManager}.
     * @param application the name of the application
     * @param handler the callback handler
     * @return A new login context
     * @throws LoginException If the login context cannot be created
     */
    public final LoginContext getLoginContext( String application, CallbackHandler handler ) throws LoginException
    {
        return new LoginContext( application, m_subject, handler );
    }

    /**
     * <p> Returns the Principal used to log in to an authenticated session. The
     * login principal is determined by examining the Subject's Principal set
     * for PrincipalWrappers or WikiPrincipals with type designator
     * <code>LOGIN_NAME</code>; the first one found is the login principal.
     * If one is not found, this method returns the first principal that isn't
     * of type Role or GroupPrincipal. If neither of these conditions hold, this method returns
     * {@link com.ecyrd.jspwiki.auth.WikiPrincipal#GUEST}.
     * @return the login Principal. If it is a PrincipalWrapper containing an
     * externally-provided Principal, the object returned is the Principal, not
     * the wrapper around it.
     */
    public final Principal getLoginPrincipal()
    {
        if ( m_loginPrincipal instanceof PrincipalWrapper )
        {
            return ((PrincipalWrapper)m_loginPrincipal).getPrincipal();
        }
        return m_loginPrincipal;
    }

    /**
     * <p>Returns the primary user Principal associated with this session. The
     * primary user principal is determined as follows:</p> <ol> <li>If the
     * Subject's Principal set contains WikiPrincipals, the first WikiPrincipal
     * with type designator <code>WIKI_NAME</code> or (alternatively)
     * <code>FULL_NAME</code> is the primary Principal.</li>
     *   <li>For all other cases, the first Principal in the Subject's principal
     *       collection that that isn't of type Role or GroupPrincipal is the primary.</li>
     * </ol>
     * If no primary user Principal is found, this method returns
     * {@link com.ecyrd.jspwiki.auth.WikiPrincipal#GUEST}.
     * @return the primary user Principal
     */
    public final Principal getUserPrincipal()
    {
        if ( m_loginPrincipal instanceof PrincipalWrapper )
        {
            return ((PrincipalWrapper)m_userPrincipal).getPrincipal();
        }
        return m_userPrincipal;
    }

    /**
     *  Returns a cached Locale object for this user.  It's better to use
     *  WikiContext's corresponding getBundle() method, since that will actually
     *  react if the user changes the locale in the middle, but if that's not
     *  available (or, for some reason, you need the speed), this method can
     *  also be used.  The Locale expires when the WikiSession expires, and
     *  currently there is no way to reset the Locale.
     *
     *  @return A cached Locale object
     *  @since 2.5.96
     */
    public final Locale getLocale()
    {
        return m_cachedLocale;
    }

    /**
     * Adds a message to the generic list of messages associated with the
     * session. These messages retain their order of insertion and remain until
     * the {@link #clearMessages()} method is called.
     * @param message the message to add; if <code>null</code> it is ignored.
     */
    public final void addMessage(String message)
    {
        addMessage( ALL, message );
    }


    /**
     * Adds a message to the specific set of messages associated with the
     * session. These messages retain their order of insertion and remain until
     * the {@link #clearMessages()} method is called.
     * @param topic the topic to associate the message to;
     * @param message the message to add
     */
    public final void addMessage(String topic, String message)
    {
        if ( topic == null )
        {
            throw new IllegalArgumentException( "addMessage: topic cannot be null." );
        }
        if ( message == null )
        {
            message = "";
        }
        Set messages = (Set)m_messages.get( topic );
        if (messages == null )
        {
            messages = new LinkedHashSet();
            m_messages.put( topic, messages );
        }
        messages.add( message );
    }

    /**
     * Clears all messages associated with this session.
     */
    public final void clearMessages()
    {
        m_messages.clear();
    }

    /**
     * Clears all messages associated with a session topic.
     * @param topic the topic whose messages should be cleared.
     */
    public final void clearMessages( String topic )
    {
        Set messages = (Set)m_messages.get( topic );
        if ( messages != null )
        {
            m_messages.clear();
        }
    }

    /**
     * Returns all generic messages associated with this session.
     * The messages stored with the session persist throughout the
     * session unless they have been reset with {@link #clearMessages()}.
     * @return the current messsages.
     */
    public final String[] getMessages()
    {
        return getMessages( ALL );
    }

    /**
     * Returns all messages associated with a session topic.
     * The messages stored with the session persist throughout the
     * session unless they have been reset with {@link #clearMessages(String)}.
     * @return the current messsages.
     * @param topic The topic
     */
    public final String[] getMessages( String topic )
    {
        Set messages = (Set)m_messages.get( topic );
        if ( messages == null || messages.size() == 0 )
        {
            return new String[0];
        }
        return (String[])messages.toArray( new String[messages.size()] );
    }

    /**
     * Returns all user Principals associated with this session. User principals
     * are those in the Subject's principal collection that aren't of type Role or
     * of type GroupPrincipal. This is a defensive copy.
     * @return Returns the user principal
     * @see com.ecyrd.jspwiki.auth.AuthenticationManager#isUserPrincipal(Principal)
     */
    public final Principal[] getPrincipals()
    {
        ArrayList principals = new ArrayList();

        // Take the first non Role as the main Principal
        for( Iterator it = m_subject.getPrincipals().iterator(); it.hasNext(); )
        {
            Principal principal = (Principal) it.next();
            if ( AuthenticationManager.isUserPrincipal( principal ) )
            {
                principals.add( principal );
            }
        }

        return (Principal[]) principals.toArray( new Principal[principals.size()] );
    }

    /**
     * Returns an array of Principal objects that represents the groups and
     * roles that the user associated with a WikiSession possesses. The array is
     * built by iterating through the Subject's Principal set and extracting all
     * Role and GroupPrincipal objects into a list. The list is returned as an
     * array sorted in the natural order implied by each Principal's
     * <code>getName</code> method. 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.
     * @return an array of Principal objects corresponding to the roles the
     *         Subject possesses
     */
    public final Principal[] getRoles()
    {
        Set roles = new HashSet();

        // Add all of the Roles possessed by the Subject directly
        roles.addAll( m_subject.getPrincipals( Role.class ) );

        // Add all of the GroupPrincipals possessed by the Subject directly
        roles.addAll( m_subject.getPrincipals( GroupPrincipal.class ) );

        // Return a defensive copy
        Principal[] roleArray = ( Principal[] )roles.toArray( new Principal[roles.size()] );
        Arrays.sort( roleArray, WikiPrincipal.COMPARATOR );
        return roleArray;
    }

    /**
     * Removes the wiki session associated with the user's HTTP request
     * from the cache of wiki sessions, typically as part of a logout
     * process.
     * @param engine the wiki engine
     * @param request the users's HTTP request
     */
    public static final void removeWikiSession( WikiEngine engine, HttpServletRequest request )
    {
        if ( engine == null || request == null )
        {
            throw new IllegalArgumentException( "Request or engine cannot be null." );
        }
        SessionMonitor monitor = SessionMonitor.getInstance( engine );
        monitor.remove( request.getSession() );
    }

    /**
     * Returns <code>true</code> if the WikiSession's Subject
     * possess a supplied Principal. This method eliminates the need
     * to externally request and inspect the JAAS subject.
     * @param principal the Principal to test
     * @return the result
     */
    public final boolean hasPrincipal( Principal principal )
    {
        return m_subject.getPrincipals().contains( principal );

    }

    /**
     * Listens for WikiEvents generated by source objects such as the
     * GroupManager. This method adds Principals to the private Subject managed
     * by the WikiSession.
     * @see com.ecyrd.jspwiki.event.WikiEventListener#actionPerformed(com.ecyrd.jspwiki.event.WikiEvent)
     * {@inheritDoc}
     */
    public final void actionPerformed( WikiEvent event )
    {
        if ( event instanceof WikiSecurityEvent )
        {
            WikiSecurityEvent e = (WikiSecurityEvent)event;
            if ( e.getTarget() != null )
            {
                switch (e.getType() )
                {
                    case WikiSecurityEvent.GROUP_ADD:
                    {
                        Group group = (Group)e.getTarget();
                        if ( isInGroup( group ) )
                        {
                            m_subject.getPrincipals().add( group.getPrincipal() );
                        }
                        break;
                    }
                    case WikiSecurityEvent.GROUP_REMOVE:
                    {
                        Group group = (Group)e.getTarget();
                        if ( m_subject.getPrincipals().contains( group.getPrincipal() ) )
                        {
                            m_subject.getPrincipals().remove( group.getPrincipal() );
                        }
                        break;
                    }
                    case WikiSecurityEvent.GROUP_CLEAR_GROUPS:
                    {
                        m_subject.getPrincipals().removeAll( m_subject.getPrincipals( GroupPrincipal.class ) );
                        break;
                    }
                    case WikiSecurityEvent.LOGIN_INITIATED:
                    {
                        WikiSession target = (WikiSession)e.getTarget();
                        if ( this.equals( target ) )
                        {
                            updatePrincipals();
                        }
                        break;
                    }
                    case WikiSecurityEvent.LOGIN_ASSERTED:
                    {
                        WikiSession target = (WikiSession)e.getTarget();
                        if ( this.equals( target ) )
                        {
                            m_status = ASSERTED;
                        }
                        break;
                    }
                    case WikiSecurityEvent.LOGIN_AUTHENTICATED:
                    {
                        WikiSession target = (WikiSession)e.getTarget();
                        if ( this.equals( target ) )
                        {
                            m_status = AUTHENTICATED;
                            injectUserProfilePrincipals();  // Add principals for the user profile
                            injectRolePrincipals();  // Inject role and group principals
                            updatePrincipals();
                        }
                        break;
                    }
                    case WikiSecurityEvent.PROFILE_SAVE:
                    {
                        WikiSession source = (WikiSession)e.getSource();
                        if ( this.equals( source ) )
                        {
                            injectUserProfilePrincipals();  // Add principals for the user profile
                            updatePrincipals();
                        }
                        break;
                    }
                    case WikiSecurityEvent.PROFILE_NAME_CHANGED:
                    {
                        // Refresh user principals based on new user profile
                        WikiSession source = (WikiSession)e.getSource();
                        if ( this.equals( source ) )
                        {
                            // To prepare for refresh, set the new full name as the primary principal
                            UserProfile[] profiles = (UserProfile[])e.getTarget();
                            UserProfile newProfile = profiles[1];
                            if ( newProfile.getFullname() == null )
                            {
                                throw new IllegalStateException( "User profile FullName cannot be null." );
                            }
                            m_userPrincipal = new WikiPrincipal( newProfile.getFullname() );

                            // Refresh principals for the user profile
                            injectUserProfilePrincipals();
                            updatePrincipals();
                        }
                        break;
                    }

                    //
                    //  No action, if the event is not recognized.
                    //
                    default:
                        break;
                }
            }
        }
    }

    /**
     * Invalidates the WikiSession and resets its Subject's
     * Principals to the equivalent of a "guest session".
     */
    public final void invalidate()
    {
        m_subject.getPrincipals().clear();
        m_subject.getPrincipals().add( WikiPrincipal.GUEST );
        m_subject.getPrincipals().add( Role.ANONYMOUS );
        m_subject.getPrincipals().add( Role.ALL );
        m_cachedCookieIdentity = null;
        m_cachedRemoteUser = null;
        m_cachedUserPrincipal = null;
    }

    /**
     * Returns whether the HTTP servlet container's authentication status has
     * changed. Used to detect whether the container has logged in a user since
     * the last call to this function. This method is stateful. After calling
     * this function, the cached values are set to those in the current request.
     * If the servlet request is <code>null</code>, this method always returns
     * <code>false</code>. Note that once a user authenticates, the container
     * status for the session will <em>never</em> change again, unless the
     * session is invalidated by timeout or logout.
     * @param request the servlet request
     * @return <code>true</code> if the status has changed, <code>false</code>
     * otherwise
     */
    protected final boolean isContainerStatusChanged( HttpServletRequest request )
    {
        if ( request == null || m_status.equals( AUTHENTICATED ) )
        {
            return false;
        }

        String remoteUser = request.getRemoteUser();
        Principal userPrincipal = request.getUserPrincipal();
        String cookieIdentity = CookieAssertionLoginModule.getUserCookie( request );
        boolean changed = false;

        // If request contains non-null remote user, update cached value if changed
        if ( remoteUser != null && !remoteUser.equals( m_cachedRemoteUser) )
        {
            m_cachedRemoteUser = remoteUser;
            log.info( "Remote user changed to " + remoteUser );
            changed = true;
        }

        // If request contains non-null user principal, updated cached value if changed
        if ( userPrincipal != null && !userPrincipal.equals( m_cachedUserPrincipal ) )
        {
            m_cachedUserPrincipal = userPrincipal;
            log.info( "User principal changed to " + userPrincipal.getName() );
            changed = true;
        }

        // If cookie identity changed (to a different value or back to null), update cache
        if ( ( cookieIdentity != null && !cookieIdentity.equals( m_cachedCookieIdentity ) )
               || ( cookieIdentity == null && m_cachedCookieIdentity != null ) )
        {
            m_cachedCookieIdentity = cookieIdentity;
            log.info( "Cookie changed to " + cookieIdentity );
            changed = true;
        }
        return changed;
    }

    /**
     * Injects GroupPrincipal and Role objects into the user's Principal set
     * based on the groups and roles the user belongs to.
     * For Roles, the algorithm first calls the
     * {@link Authorizer#getRoles()} to obtain the array of
     * Principals the authorizer knows about. Then, the method
     * {@link Authorizer#isUserInRole(WikiSession, Principal)} is
     * called for each Principal. If the user possesses the role,
     * an equivalent role Principal is injected into the user's
     * principal set.
     * Reloads user Principals into the suppplied WikiSession's Subject.
     * Existing Role principals are preserved; all other Principal
     * types are flushed and replaced by those returned by
     * {@link com.ecyrd.jspwiki.auth.user.UserDatabase#getPrincipals(String)}.
     * This method should generally be called after a user's {@link com.ecyrd.jspwiki.auth.user.UserProfile}
     * is saved. If the wiki session is null, or there is no matching user profile, the
     * method returns silently.
     */
    protected final void injectRolePrincipals()
    {
        // Get the GroupManager and test for each Group
        GroupManager manager = m_engine.getGroupManager();
        Principal[] groups = manager.getRoles();
        for ( int i = 0; i < groups.length; i++ )
        {
            if ( manager.isUserInRole( this, groups[i] ) )
            {
                m_subject.getPrincipals().add( groups[i] );
            }
        }

        // Get the authorizer's known roles, then test for each
        try
        {
            Authorizer authorizer = m_engine.getAuthorizationManager().getAuthorizer();
            Principal[] roles = authorizer.getRoles();
            for ( int i = 0; i < roles.length; i++ )
            {
                Principal role = roles[i];
                if ( authorizer.isUserInRole( this, role ) )
                {
                    String roleName = role.getName();
                    if ( !Role.isReservedName( roleName ) )
                    {
                        m_subject.getPrincipals().add( new Role( roleName ) );
                    }
                }
            }
        }
        catch ( WikiException e )
        {
            log.error( "Could not refresh role principals: " + e.getMessage() );
        }
    }

    /**
     * Adds Principal objects to the Subject that correspond to the
     * logged-in user's profile attributes for the wiki name, full name
     * and login name. These Principals will be WikiPrincipals, and they
     * will replace all other WikiPrincipals in the Subject. <em>Note:
     * this method is never called during anonymous or asserted sessions.</em>
     */
    protected final void injectUserProfilePrincipals()
    {
        // Copy all Role and GroupPrincipal principals into a temporary cache
        Set oldPrincipals = m_subject.getPrincipals();
        Set newPrincipals = new HashSet();
        for (Iterator it = oldPrincipals.iterator(); it.hasNext();)
        {
            Principal principal = (Principal)it.next();
            if ( AuthenticationManager.isRolePrincipal( principal ) )
            {
                newPrincipals.add( principal );
            }
        }
        String searchId = getUserPrincipal().getName();
        if ( searchId == null )
        {
            // Oh dear, this wasn't an authenticated user after all
            log.info("Refresh principals failed because WikiSession had no user Principal; maybe not logged in?");
            return;
        }

        // Look up the user and go get the new Principals
        UserDatabase database = m_engine.getUserManager().getUserDatabase();
        if ( database == null )
        {
            throw new IllegalStateException( "User database cannot be null." );
        }
        try
        {
            UserProfile profile = database.find( searchId );
            Principal[] principals = database.getPrincipals( profile.getLoginName() );
            for (int i = 0; i < principals.length; i++)
            {
                Principal principal = principals[i];
                newPrincipals.add( principal );
            }

            // Replace the Subject's old Principals with the new ones
            oldPrincipals.clear();
            oldPrincipals.addAll( newPrincipals );
        }
        catch ( NoSuchPrincipalException e )
        {
            // We will get here if the user has a principal but not a profile
            // For example, it's a container-managed user who hasn't set up a profile yet
            log.warn("User profile '" + searchId + "' not found. This is normal for container-auth users who haven't set up a profile yet.");
        }
    }

    /**
     * Updates the internally cached principals returned by {@link #getUserPrincipal()} and
     * {@link #getLoginPrincipal()}. This method is called when the WikiSession receives
     * the {@link com.ecyrd.jspwiki.event.WikiSecurityEvent#LOGIN_INITIATED} message.
     */
    protected final void updatePrincipals()
    {
        Set principals = m_subject.getPrincipals();
        m_loginPrincipal = null;
        m_userPrincipal = null;
        Principal wikinamePrincipal = null;
        Principal fullnamePrincipal = null;
        Principal otherPrincipal = null;

        for( Iterator it = principals.iterator(); it.hasNext(); )
        {
            Principal currentPrincipal = (Principal) it.next();
            if ( !( currentPrincipal instanceof Role || currentPrincipal instanceof GroupPrincipal ) )
            {
                // For login principal, take the first PrincipalWrapper or WikiPrincipal of type LOGIN_NAME
                // For user principal take the  first WikiPrincipal of type WIKI_NAME
                if ( currentPrincipal instanceof PrincipalWrapper )
                {
                    m_loginPrincipal = currentPrincipal;
                }
                else if ( currentPrincipal instanceof WikiPrincipal )
                {
                    WikiPrincipal wp = (WikiPrincipal) currentPrincipal;
                    if ( wp.getType().equals( WikiPrincipal.LOGIN_NAME ) )
                    {
                        m_loginPrincipal = wp;
                    }
                    else if ( wp.getType().equals( WikiPrincipal.WIKI_NAME ) )
                    {
                        wikinamePrincipal = currentPrincipal;
                        m_userPrincipal = wp;
                    }
                    else if ( wp.getType().equals( WikiPrincipal.FULL_NAME ) )
                    {
                        fullnamePrincipal = currentPrincipal;
                    }
                    else
                    {
                        otherPrincipal = currentPrincipal;
                    }
                }
                else if ( otherPrincipal == null )
                {
                    otherPrincipal = currentPrincipal;
                }
            }
        }
        if ( otherPrincipal == null )
        {
            otherPrincipal = WikiPrincipal.GUEST;
        }

        // If no login principal, use wiki name, full name or other principal (in that order)
        if ( m_loginPrincipal == null )
        {
            m_loginPrincipal = wikinamePrincipal;
            if ( m_loginPrincipal == null )
            {
                m_loginPrincipal = fullnamePrincipal;
                if ( m_loginPrincipal == null )
                {
                    m_loginPrincipal = otherPrincipal;
                }
            }
        }

        // If no user principal, use wiki name, full name or login principal (in that order)
        if ( m_userPrincipal == null )
        {
            m_userPrincipal = wikinamePrincipal;
            if ( m_userPrincipal == null )
            {
                m_userPrincipal = fullnamePrincipal;
                if ( m_userPrincipal == null )
                {
                    m_userPrincipal = m_loginPrincipal;
                }
            }
        }
    }

    /**
     * <p>Returns the status of the wiki session as a text string. Valid values are:</p>
     * <ul>
     *   <li>{@link #AUTHENTICATED}</li>
     *   <li>{@link #ASSERTED}</li>
     *   <li>{@link #ANONYMOUS}</li>
     * </ul>
     * @return the user's session status
     */
    public final String getStatus()
    {
        return m_status;
    }

    /**
     * <p>Static factory method that returns the WikiSession object associated with
     * the current HTTP request. This method looks up the associated HttpSession
     * in an internal WeakHashMap and attempts to retrieve the WikiSession. If
     * not found, one is created. This method is guaranteed to always return a
     * WikiSession, although the authentication status is unpredictable until
     * the user attempts to log in. If the servlet request parameter is
     * <code>null</code>, a synthetic {@link #guestSession(WikiEngine)}is returned.</p>
     * <p>When a session is created, this method attaches a WikiEventListener
     * to the GroupManager so that changes to groups are detected automatically.</p>
     * @param engine the wiki engine
     * @param request the servlet request object
     * @return the existing (or newly created) wiki session
     */
    public static final WikiSession getWikiSession( WikiEngine engine, HttpServletRequest request )
    {
        // If request is null, return guest session
        if ( request == null )
        {
            if ( log.isDebugEnabled() )
            {
                log.debug( "Looking up WikiSession for NULL HttpRequest: returning guestSession()" );
            }
            return staticGuestSession( engine );
        }

        // Look for a WikiSession associated with the user's Http Session
        // and create one if it isn't there yet.
        HttpSession session = request.getSession();
        SessionMonitor monitor = SessionMonitor.getInstance( engine );
        WikiSession wikiSession = monitor.find( session );

        // Attach reference to wiki engine
        wikiSession.m_engine = engine;

        wikiSession.m_cachedLocale = request.getLocale();

        return wikiSession;
    }

    /**
     * Static factory method that creates a new "guest" session containing a single
     * user Principal {@link com.ecyrd.jspwiki.auth.WikiPrincipal#GUEST},
     * plus the role principals {@link Role#ALL} and
     * {@link Role#ANONYMOUS}. This method also adds the session as a listener
     * for GroupManager, AuthenticationManager and UserManager events.
     * @param engine the wiki engine
     * @return the guest wiki session
     */
    public static final WikiSession guestSession( WikiEngine engine )
    {
        WikiSession session = new WikiSession();
        session.m_engine = engine;
        session.invalidate();

        // Add the session as listener for GroupManager, AuthManager, UserManager events
        GroupManager groupMgr = engine.getGroupManager();
        AuthenticationManager authMgr = engine.getAuthenticationManager();
        UserManager userMgr = engine.getUserManager();
        groupMgr.addWikiEventListener( session );
        authMgr.addWikiEventListener( session );
        userMgr.addWikiEventListener( session );

        return session;
    }

    /**
     *  Returns a static guest session, which is available for this
     *  thread only.  This guest session is used internally whenever
     *  there is no HttpServletRequest involved, but the request is
     *  done e.g. when embedding JSPWiki code.
     *
     *  @param engine WikiEngine for this session
     *  @return A static WikiSession which is shared by all in this
     *          same Thread.
     */
    // FIXME: Should really use WeakReferences to clean away unused sessions.

    private static WikiSession staticGuestSession( WikiEngine engine )
    {
        WikiSession session = (WikiSession) c_guestSession.get();

        if( session == null )
        {
            session = guestSession( engine );

            c_guestSession.set( session );
        }

        return session;
    }

    /**
     * Returns the total number of active wiki sessions for a
     * particular wiki. This method delegates to the wiki's
     * {@link SessionMonitor#sessions()} method.
     * @param engine the wiki session
     * @return the number of sessions
     */
    public static final int sessions( WikiEngine engine )
    {
        SessionMonitor monitor = SessionMonitor.getInstance( engine );
        return monitor.sessions();
    }

    /**
     * Returns Principals representing the current users known
     * to a particular wiki. Each Principal will correspond to the
     * value returned by each WikiSession's {@link #getUserPrincipal()}
     * method. This method delegates to {@link SessionMonitor#userPrincipals()}.
     * @param engine the wiki engine
     * @return an array of Principal objects, sorted by name
     */
    public static final Principal[] userPrincipals( WikiEngine engine )
    {
        SessionMonitor monitor = SessionMonitor.getInstance( engine );
        return monitor.userPrincipals();
    }

    /**
     * Wrapper for
     * {@link javax.security.auth.Subject#doAsPrivileged(Subject, java.security.PrivilegedExceptionAction, java.security.AccessControlContext)}
     * that executes an action with the privileges posssessed by a
     * WikiSession's Subject. The action executes with a <code>null</code>
     * AccessControlContext, which has the effect of running it "cleanly"
     * without the AccessControlContexts of the caller.
     * @param session the wiki session
     * @param action the privileged action
     * @return the result of the privileged action; may be <code>null</code>
     * @throws java.security.AccessControlException if the action is not permitted
     * by the security policy
     */
    public static final Object doPrivileged( WikiSession session, PrivilegedAction action ) throws AccessControlException
    {
        return Subject.doAsPrivileged( session.m_subject, action, null );
    }

    /**
     * Verifies whether a String represents an IPv4 address. The algorithm is
     * extremely efficient and does not allocate any objects.
     * @param name the address to test
     * @return the result
     */
    protected static final boolean isIPV4Address( String name )
    {
        if ( name.charAt( 0 ) == DOT || name.charAt( name.length() - 1 ) == DOT )
        {
            return false;
        }

        int[] addr = new int[]
        { 0, 0, 0, 0 };
        int currentOctet = 0;
        for( int i = 0; i < name.length(); i++ )
        {
            int ch = name.charAt( i );
            boolean isDigit = ch >= ONE && ch <= NINE;
            boolean isDot = ch == DOT;
            if ( !isDigit && !isDot )
            {
                return false;
            }
            if ( isDigit )
            {
                addr[currentOctet] = 10 * addr[currentOctet] + ( ch - ONE );
                if ( addr[currentOctet] > 255 )
                {
                    return false;
                }
            }
            else if ( name.charAt( i - 1 ) == DOT )
            {
                return false;
            }
            else
            {
                currentOctet++;
            }
        }
        return  currentOctet == 3;
    }

}
java2s.com  | Contact Us | Privacy Policy
Copyright 2009 - 12 Demo Source and Support. All rights reserved.
All other trademarks are property of their respective owners.