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 [7/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/WikiPage.java
URL: http://svn.apache.org/viewvc/incubator/jspwiki/branches/JSPWIKI_STRIPES_BRANCH/src/com/ecyrd/jspwiki/WikiPage.java?rev=627255&view=auto
==============================================================================
--- incubator/jspwiki/branches/JSPWIKI_STRIPES_BRANCH/src/com/ecyrd/jspwiki/WikiPage.java (added)
+++ incubator/jspwiki/branches/JSPWIKI_STRIPES_BRANCH/src/com/ecyrd/jspwiki/WikiPage.java Tue Feb 12 21:53:55 2008
@@ -0,0 +1,414 @@
+/*
+ JSPWiki - a JSP-based WikiWiki clone.
+
+ Copyright (C) 2001 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;
+
+import java.util.Collection;
+import java.util.Date;
+import java.util.HashMap;
+import java.util.Map;
+
+import com.ecyrd.jspwiki.attachment.Attachment;
+import com.ecyrd.jspwiki.auth.acl.Acl;
+import com.ecyrd.jspwiki.providers.ProviderException;
+import com.ecyrd.jspwiki.providers.WikiPageProvider;
+
+/**
+ * Simple wrapper class for the Wiki page attributes. The Wiki page
+ * content is moved around in Strings, though.
+ */
+
+// FIXME: We need to rethink how metadata is being used - probably the
+// author, date, etc. should also be part of the metadata. We also
+// need to figure out the metadata lifecycle.
+
+public class WikiPage
+ implements Cloneable,
+ Comparable
+{
+ private static final long serialVersionUID = 1L;
+
+ private String m_name;
+ private WikiEngine m_engine;
+ private String m_wiki;
+ private Date m_lastModified;
+ private long m_fileSize = -1;
+ private int m_version = WikiPageProvider.LATEST_VERSION;
+ private String m_author = null;
+ private final HashMap m_attributes = new HashMap();
+ private String m_qualifiedName;
+
+ /**
+ * "Summary" is a short summary of the page. It is a String.
+ */
+ public static final String DESCRIPTION = "summary";
+
+ /** A special variable name for storing a page alias. */
+ public static final String ALIAS = "alias";
+
+ /** A special variable name for storing a redirect note */
+ public static final String REDIRECT = "redirect";
+
+ /** A special variable name for storing a changenote. */
+ public static final String CHANGENOTE = "changenote";
+
+ private Acl m_accessList = null;
+
+ /**
+ * Create a new WikiPage using a given engine and name.
+ *
+ * @param engine The WikiEngine that owns this page.
+ * @param name The name of the page.
+ */
+ public WikiPage( WikiEngine engine, String name )
+ {
+ m_engine = engine;
+ m_name = name;
+ m_wiki = engine.getApplicationName();
+ m_qualifiedName =m_wiki + ":" + m_name;
+ }
+
+ /**
+ * Returns the name of the page.
+ *
+ * @return The page name.
+ */
+ public String getName()
+ {
+ return m_name;
+ }
+
+ public String getQualifiedName()
+ {
+ return m_qualifiedName;
+ }
+
+ /**
+ * Convenience method that returns the collection of
+ * Attachment objects associated with this WikiPage.
+ * It simply delegates to
+ * {@link com.ecyrd.jspwiki.attachment.AttachmentManager#listAttachments(WikiPage)}.
+ * @return the collection
+ */
+ public Collection<Attachment> getAttachments() throws ProviderException
+ {
+ return m_engine.getAttachmentManager().listAttachments( this );
+ }
+
+ /**
+ * A WikiPage may have a number of attributes, which might or might not be
+ * available. Typically attributes are things that do not need to be stored
+ * with the wiki page to the page repository, but are generated
+ * on-the-fly. A provider is not required to save them, but they
+ * can do that if they really want.
+ *
+ * @param key The key using which the attribute is fetched
+ * @return The attribute. If the attribute has not been set, returns null.
+ */
+ public Object getAttribute( String key )
+ {
+ return m_attributes.get( key );
+ }
+
+ /**
+ * Sets an metadata attribute.
+ *
+ * @see #getAttribute()
+ * @param key The key for the attribute used to fetch the attribute later on.
+ * @param attribute The attribute value
+ */
+ public void setAttribute( String key, Object attribute )
+ {
+ m_attributes.put( key, attribute );
+ }
+
+ /**
+ * Returns the full attributes Map, in case external code needs
+ * to iterate through the attributes.
+ *
+ * @return The attribute Map. Please note that this is a direct
+ * reference, not a copy.
+ */
+ public Map getAttributes()
+ {
+ return m_attributes;
+ }
+
+ /**
+ * Removes an attribute from the page, if it exists.
+ *
+ * @param key The key for the attribute
+ * @return If the attribute existed, returns the object.
+ * @since 2.1.111
+ */
+ public Object removeAttribute( String key )
+ {
+ return m_attributes.remove( key );
+ }
+
+ /**
+ * Returns the date when this page was last modified.
+ *
+ * @return The last modification date
+ */
+ public Date getLastModified()
+ {
+ return m_lastModified;
+ }
+
+ /**
+ * Sets the last modification date. In general, this is only
+ * changed by the provider.
+ *
+ * @param date The date
+ */
+ public void setLastModified( Date date )
+ {
+ m_lastModified = date;
+ }
+
+ /**
+ * Sets the page version. In general, this is only changed
+ * by the provider.
+ *
+ * @param version The version number
+ */
+ public void setVersion( int version )
+ {
+ m_version = version;
+ }
+
+ /**
+ * Returns the version that this WikiPage instance represents.
+ *
+ * @return the version number of this page.
+ */
+ public int getVersion()
+ {
+ return m_version;
+ }
+
+ /**
+ * Returns the size of the page.
+ *
+ * @return the size of the page.
+ * @since 2.1.109
+ */
+ public long getSize()
+ {
+ return m_fileSize;
+ }
+
+ /**
+ * Sets the size. Typically called by the provider only.
+ *
+ * @param size The size of the page.
+ * @since 2.1.109
+ */
+ public void setSize( long size )
+ {
+ m_fileSize = size;
+ }
+
+ /**
+ * Returns the Acl for this page. May return <code>null</code>,
+ * in case there is no Acl defined, or it has not
+ * yet been set by {@link #setAcl(Acl)}.
+ *
+ * @return The access control list. May return null, if there is
+ * no acl.
+ */
+ public Acl getAcl()
+ {
+ return m_accessList;
+ }
+
+ /**
+ * Sets the Acl for this page. Note that method does <em>not</em>
+ * persist the Acl itself to back-end storage or in page markup;
+ * it merely sets the internal field that stores the Acl. To
+ * persist the Acl, callers should invoke
+ * {@link com.ecyrd.jspwiki.auth.acl.AclManager#setPermissions(WikiPage, Acl)}.
+ * @param acl The Acl to set
+ */
+ public void setAcl( Acl acl )
+ {
+ m_accessList = acl;
+ }
+
+ /**
+ * Sets the author of the page. Typically called only by the provider.
+ *
+ * @param author The author name.
+ */
+ public void setAuthor( String author )
+ {
+ m_author = author;
+ }
+
+ /**
+ * Returns author name, or null, if no author has been defined.
+ *
+ * @return Author name, or possibly null.
+ */
+ public String getAuthor()
+ {
+ return m_author;
+ }
+
+ /**
+ * Returns the wiki nanme for this page
+ *
+ * @return The name of the wiki.
+ */
+ public String getWiki()
+ {
+ return m_wiki;
+ }
+
+ /**
+ * This method will remove all metadata from the page.
+ */
+ public void invalidateMetadata()
+ {
+ m_hasMetadata = false;
+ setAcl( null );
+ m_attributes.clear();
+ }
+
+ private boolean m_hasMetadata = false;
+
+ /**
+ * Returns <code>true</code> if the page has valid metadata; that is, it has been parsed.
+ * Note that this method is a kludge to support our pre-3.0 metadata system, and as such
+ * will go away with the new API.
+ *
+ * @return true, if the page has metadata.
+ */
+ public boolean hasMetadata()
+ {
+ return m_hasMetadata;
+ }
+
+ /**
+ * Sets the metadata flag to true. Never call.
+ */
+ public void setHasMetadata()
+ {
+ m_hasMetadata = true;
+ }
+
+ /**
+ * Returns a debug-suitable version of the page.
+ *
+ * @return A debug string.
+ */
+ public String toString()
+ {
+ return "WikiPage ["+m_wiki+":"+m_name+",ver="+m_version+",mod="+m_lastModified+"]";
+ }
+
+ /**
+ * Creates a deep clone of a WikiPage. Strings are not cloned, since
+ * they're immutable. Attributes are not cloned, only the internal
+ * HashMap (so if you modify the contents of a value of an attribute,
+ * these will reflect back to everyone).
+ *
+ * @return A deep clone of the WikiPage
+ */
+ public Object clone()
+ {
+ try
+ {
+ WikiPage p = (WikiPage)super.clone();
+
+ p.m_engine = m_engine;
+ p.m_name = m_name;
+ p.m_wiki = m_wiki;
+
+ p.m_author = m_author;
+ p.m_version = m_version;
+ p.m_lastModified = m_lastModified != null ? (Date)m_lastModified.clone() : null;
+
+ p.m_fileSize = m_fileSize;
+
+ p.m_attributes.putAll(m_attributes);
+
+ return p;
+ }
+ catch( CloneNotSupportedException e )
+ {}
+
+ return null;
+ }
+
+ /**
+ * Compares a page with another. The primary sorting order
+ * is according to page name, and if they have the same name,
+ * then according to the page version.
+ *
+ * @param o The object to compare against
+ * @return -1, 0 or 1
+ */
+ public int compareTo( Object o )
+ {
+ int res = 0;
+ if( o instanceof WikiPage )
+ {
+ WikiPage c = (WikiPage)o;
+
+ res = this.getName().compareTo(c.getName());
+
+ if( res == 0 ) res = this.getVersion()-c.getVersion();
+ }
+
+ return res;
+ }
+
+ /**
+ * A page is equal to another page if its name and version are equal.
+ *
+ * {@inheritDoc}
+ */
+ public boolean equals( Object o )
+ {
+ if( o != null && o instanceof WikiPage )
+ {
+ WikiPage oo = (WikiPage) o;
+
+ if( oo.getName().equals( getName() ) )
+ {
+ if( oo.getVersion() == getVersion() )
+ {
+ return true;
+ }
+ }
+ }
+
+ return false;
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ public int hashCode()
+ {
+ return m_name.hashCode() * m_version;
+ }
+}
Added: incubator/jspwiki/branches/JSPWIKI_STRIPES_BRANCH/src/com/ecyrd/jspwiki/WikiProvider.java
URL: http://svn.apache.org/viewvc/incubator/jspwiki/branches/JSPWIKI_STRIPES_BRANCH/src/com/ecyrd/jspwiki/WikiProvider.java?rev=627255&view=auto
==============================================================================
--- incubator/jspwiki/branches/JSPWIKI_STRIPES_BRANCH/src/com/ecyrd/jspwiki/WikiProvider.java (added)
+++ incubator/jspwiki/branches/JSPWIKI_STRIPES_BRANCH/src/com/ecyrd/jspwiki/WikiProvider.java Tue Feb 12 21:53:55 2008
@@ -0,0 +1,61 @@
+/*
+ JSPWiki - a JSP-based WikiWiki clone.
+
+ Copyright (C) 2001-2002 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;
+
+import java.util.Properties;
+import java.io.IOException;
+
+/**
+ * A generic Wiki provider for all sorts of things that the Wiki can
+ * store.
+ *
+ * @author Janne Jalkanen
+ * @since 2.0
+ */
+public interface WikiProvider
+{
+ /**
+ * Passing this to any method should get the latest version
+ */
+ public static final int LATEST_VERSION = -1;
+
+ /**
+ * Initializes the page provider.
+ *
+ * @param engine WikiEngine to own this provider
+ * @param properties A set of properties used to initialize this provider
+ * @throws NoRequiredPropertyException If the provider needs a property which is not found in the property set
+ * @throws IOException If there is an IO problem
+ */
+ public void initialize( WikiEngine engine, Properties properties )
+ throws NoRequiredPropertyException,
+ IOException;
+
+ /**
+ * Return a valid HTML string for information. May
+ * be anything.
+ * @since 1.6.4
+ * @return A string describing the provider.
+ */
+
+ public String getProviderInfo();
+}
+
+
Added: incubator/jspwiki/branches/JSPWIKI_STRIPES_BRANCH/src/com/ecyrd/jspwiki/WikiSession.java
URL: http://svn.apache.org/viewvc/incubator/jspwiki/branches/JSPWIKI_STRIPES_BRANCH/src/com/ecyrd/jspwiki/WikiSession.java?rev=627255&view=auto
==============================================================================
--- incubator/jspwiki/branches/JSPWIKI_STRIPES_BRANCH/src/com/ecyrd/jspwiki/WikiSession.java (added)
+++ incubator/jspwiki/branches/JSPWIKI_STRIPES_BRANCH/src/com/ecyrd/jspwiki/WikiSession.java Tue Feb 12 21:53:55 2008
@@ -0,0 +1,1038 @@
+/*
+ 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<WikiSession> c_guestSession = new ThreadLocal<WikiSession>();
+
+ private final Subject m_subject = new Subject();
+
+ private final Map<String,Set<String>> m_messages = new HashMap<String,Set<String>>();
+
+ 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 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;
+ }
+
+ /**
+ * 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<String> messages = m_messages.get( topic );
+ if (messages == null )
+ {
+ messages = new LinkedHashSet<String>();
+ 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<String> messages = 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<String> messages = m_messages.get( topic );
+ if ( messages == null || messages.size() == 0 )
+ {
+ return new String[0];
+ }
+ return 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()
+ {
+ List<Principal> principals = new ArrayList<Principal>();
+
+ // Take the first non Role as the main Principal
+ for( Principal principal : m_subject.getPrincipals() )
+ {
+ if ( AuthenticationManager.isUserPrincipal( principal ) )
+ {
+ principals.add( principal );
+ }
+ }
+
+ return 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<Principal> roles = new HashSet<Principal>();
+
+ // 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 = 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<Principal> oldPrincipals = m_subject.getPrincipals();
+ Set<Principal> newPrincipals = new HashSet<Principal>();
+ for ( Principal principal : oldPrincipals )
+ {
+ 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<Principal> principals = m_subject.getPrincipals();
+ m_loginPrincipal = null;
+ m_userPrincipal = null;
+ Principal wikinamePrincipal = null;
+ Principal fullnamePrincipal = null;
+ Principal otherPrincipal = null;
+
+ for( Principal currentPrincipal : principals )
+ {
+ 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 = 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;
+ }
+
+}
Added: incubator/jspwiki/branches/JSPWIKI_STRIPES_BRANCH/src/com/ecyrd/jspwiki/action/AbstractActionBean.java
URL: http://svn.apache.org/viewvc/incubator/jspwiki/branches/JSPWIKI_STRIPES_BRANCH/src/com/ecyrd/jspwiki/action/AbstractActionBean.java?rev=627255&view=auto
==============================================================================
--- incubator/jspwiki/branches/JSPWIKI_STRIPES_BRANCH/src/com/ecyrd/jspwiki/action/AbstractActionBean.java (added)
+++ incubator/jspwiki/branches/JSPWIKI_STRIPES_BRANCH/src/com/ecyrd/jspwiki/action/AbstractActionBean.java Tue Feb 12 21:53:55 2008
@@ -0,0 +1,238 @@
+package com.ecyrd.jspwiki.action;
+
+import java.security.Principal;
+import java.util.*;
+
+import javax.servlet.http.HttpServletRequest;
+
+import net.sourceforge.stripes.action.ActionBeanContext;
+
+import com.ecyrd.jspwiki.WikiEngine;
+import com.ecyrd.jspwiki.WikiSession;
+
+/**
+ * <p>Abstract ActionBean superclass for all wiki actions, such as page actions ({@link com.ecyrd.jspwiki.WikiContext} and subclasses),
+ * group actions (e.g., {@link ViewGroupActionBean}), user actions (e.g., {@link UserPreferencesActionBean}) and others.</p>
+ *
+ *
+ * @author Andrew Jaquith
+ */
+@WikiRequestContext("none")
+public abstract class AbstractActionBean implements WikiActionBean
+{
+ protected Map<String,Object> m_variableMap = new HashMap<String,Object>();
+
+ private WikiActionBeanContext m_actionBeanContext = null;
+
+ /**
+ * The JSP for this WikiContext.
+ */
+ private String m_skin = null;
+
+ private String m_template = null;
+
+ private static final String DEFAULT_TEMPLATE = "default";
+
+ private final String m_requestContext;
+
+ /**
+ * Creates a new instance of this class, without a WikiEngine, Request or
+ * WikiPage.
+ */
+ protected AbstractActionBean()
+ {
+ super();
+ m_requestContext = getClass().getAnnotation( WikiRequestContext.class ).value();
+ }
+
+ /**
+ * Returns the content template for this ActionBean.
+ */
+ public final String getContentTemplate()
+ {
+ return this.getClass().getAnnotation(WikiRequestContext.class).value();
+ }
+
+ /**
+ * Returns the Stripes ActionBeanContext associated this WikiContext, lazily
+ * creating one if necessary.
+ *
+ * @throws IllegalStateException
+ */
+ public WikiActionBeanContext getContext()
+ {
+ if (m_actionBeanContext == null)
+ {
+ setContext(new WikiActionBeanContext());
+ }
+ return m_actionBeanContext;
+ }
+
+ /**
+ * Convenience method that gets the current user. Delegates the lookup to
+ * the WikiSession associated with this WikiContect. May return null, in
+ * case the current user has not yet been determined; or this is an internal
+ * system. If the WikiSession has not been set, <em>always</em> returns
+ * null.
+ */
+ public Principal getCurrentUser()
+ {
+ return getWikiSession().getUserPrincipal();
+ }
+
+ /**
+ * Returns the WikiEngine, which may be <code>null</code> if this instance
+ * was created without invoking the WikiActionBeanContext methods
+ * {@link WikiActionBeanContext#setRequest(HttpServletRequest)} or
+ * {@link WikiActionBeanContext#setServletContext(javax.servlet.ServletContext)}.
+ */
+ public WikiEngine getEngine()
+ {
+ return getContext().getWikiEngine();
+ }
+
+ /**
+ * Returns the request context for this ActionBean by looking up the
+ * value of the annotation {@link WikiRequestContext}. Note that if the
+ * annotation is not present, this method will return {@link ecyrd.jspwiki.WikiContext#NONE}.
+ */
+ public String getRequestContext()
+ {
+ return m_requestContext;
+ }
+
+ /**
+ * Returns the "skin" to be used for this ActionBean.
+ *
+ * @return the skin
+ */
+ public String getSkin()
+ {
+ return m_skin;
+ }
+
+ /**
+ * <p>
+ * Gets the template that is to be used throughout this request. The value
+ * returned depends on the whether the current HTTP request has supplied a
+ * custom skin or template name. In order of preference:
+ * </p>
+ * <ul>
+ * <li>The "skin", if set by {@link #setSkin(String)} or if the HTTP
+ * parameter <code>skin</code> was bound by Stripes</li>
+ * <li>The template, if set by {@link #setTemplate(String)} or if the HTTP
+ * parameter <code>template</code> was bound by Stripes</li>
+ * <li>The WikiEngine's default template, as returned by
+ * {@link WikiEngine#getTemplateDir()}</li>
+ * <li>The value <code>default</code></li>
+ * </ul>
+ *
+ * @since 2.1.15.
+ */
+ public String getTemplate()
+ {
+ if ( m_skin != null )
+ {
+ return m_skin;
+ }
+ else if ( m_template != null )
+ {
+ return m_template;
+ }
+ else if ( getEngine() != null )
+ {
+ return getEngine().getTemplateDir();
+ }
+ return DEFAULT_TEMPLATE;
+ }
+
+ /**
+ * Returns the WikiSession associated with the context. This method is
+ * guaranteed to always return a valid WikiSession. If this context was
+ * constructed without an associated HttpServletRequest, it will return
+ * {@link WikiSession#guestSession(WikiEngine)}.
+ */
+ public WikiSession getWikiSession()
+ {
+ return getContext().getWikiSession();
+ }
+
+ /**
+ * Gets a previously set variable.
+ *
+ * @param key The variable name.
+ * @return The variable contents.
+ */
+ public Object getVariable( String key )
+ {
+ return m_variableMap.get( key );
+ }
+
+ /**
+ * Sets a variable. The variable is valid while the WikiContext is valid,
+ * i.e. while page processing continues. The variable data is discarded
+ * once the page processing is finished.
+ *
+ * @param key The variable name.
+ * @param data The variable value.
+ */
+ public void setVariable( String key, Object data )
+ {
+ m_variableMap.put( key, data );
+ }
+
+ /**
+ * Sets the Stripes ActionBeanContext associated with this WikiContext. It
+ * will also update the cached HttpRequest.
+ */
+ public void setContext(ActionBeanContext context)
+ {
+ m_actionBeanContext = ((WikiActionBeanContext) context);
+ }
+
+ /**
+ * Sets the skin to be used with this ActionBean. This value will override
+ * the template returned by {@link #getTemplate()}. Normally, this method
+ * is invoked by Stripes when binding request parameters to the ActionBean.
+ *
+ * @param skin
+ * the skin to use
+ */
+ public void setSkin(String skin)
+ {
+ m_skin = skin;
+ }
+
+ /**
+ * Sets the template to be used for this request.
+ *
+ * @since 2.1.15.
+ */
+ public void setTemplate(String dir)
+ {
+ m_template = dir;
+ }
+
+ /**
+ * Locates the i18n ResourceBundle given. This method interprets
+ * the request locale, and uses that to figure out which language the
+ * user wants.
+ * @see com.ecyrd.jspwiki.i18n.InternationalizationManager
+ * @param bundle The name of the bundle you are looking for.
+ * @return A resource bundle object
+ * @throws MissingResourceException If the bundle cannot be found
+ */
+ // FIXME: This method should really cache the ResourceBundles or something...
+ public ResourceBundle getBundle( String bundle ) throws MissingResourceException
+ {
+ Locale loc = null;
+
+ if( m_actionBeanContext != null && m_actionBeanContext.getRequest() != null )
+ {
+ loc = m_actionBeanContext.getRequest().getLocale();
+ }
+ ResourceBundle b = getEngine().getInternationalizationManager().getBundle(bundle, loc);
+
+ return b;
+ }
+}
Added: incubator/jspwiki/branches/JSPWIKI_STRIPES_BRANCH/src/com/ecyrd/jspwiki/action/AdminActionBean.java
URL: http://svn.apache.org/viewvc/incubator/jspwiki/branches/JSPWIKI_STRIPES_BRANCH/src/com/ecyrd/jspwiki/action/AdminActionBean.java?rev=627255&view=auto
==============================================================================
--- incubator/jspwiki/branches/JSPWIKI_STRIPES_BRANCH/src/com/ecyrd/jspwiki/action/AdminActionBean.java (added)
+++ incubator/jspwiki/branches/JSPWIKI_STRIPES_BRANCH/src/com/ecyrd/jspwiki/action/AdminActionBean.java Tue Feb 12 21:53:55 2008
@@ -0,0 +1,12 @@
+package com.ecyrd.jspwiki.action;
+
+import net.sourceforge.stripes.action.UrlBinding;
+
+import com.ecyrd.jspwiki.WikiContext;
+
+@WikiRequestContext("admin")
+@UrlBinding("/admin/Admin.action")
+public class AdminActionBean extends WikiContext
+{
+
+}
Added: incubator/jspwiki/branches/JSPWIKI_STRIPES_BRANCH/src/com/ecyrd/jspwiki/action/AdministerProfilesActionBean.java
URL: http://svn.apache.org/viewvc/incubator/jspwiki/branches/JSPWIKI_STRIPES_BRANCH/src/com/ecyrd/jspwiki/action/AdministerProfilesActionBean.java?rev=627255&view=auto
==============================================================================
--- incubator/jspwiki/branches/JSPWIKI_STRIPES_BRANCH/src/com/ecyrd/jspwiki/action/AdministerProfilesActionBean.java (added)
+++ incubator/jspwiki/branches/JSPWIKI_STRIPES_BRANCH/src/com/ecyrd/jspwiki/action/AdministerProfilesActionBean.java Tue Feb 12 21:53:55 2008
@@ -0,0 +1,87 @@
+package com.ecyrd.jspwiki.action;
+
+import java.util.List;
+
+import net.sourceforge.stripes.action.*;
+import net.sourceforge.stripes.validation.EmailTypeConverter;
+import net.sourceforge.stripes.validation.Validate;
+import net.sourceforge.stripes.validation.ValidateNestedProperties;
+
+import com.ecyrd.jspwiki.auth.NoSuchPrincipalException;
+import com.ecyrd.jspwiki.auth.WikiSecurityException;
+import com.ecyrd.jspwiki.auth.user.DefaultUserProfile;
+import com.ecyrd.jspwiki.auth.user.UserDatabase;
+import com.ecyrd.jspwiki.auth.user.UserProfile;
+
+/**
+ * Manages the administration of UserProfiles, from the Administer Profiles page. Receives a List
+ * of UserProfiles, which may include a new profile, and persists the changes. Also receives an
+ * Array of Strings (login names) for UserProfiles that are to be deleted, and deletes them.
+ *
+ * @author Andrew Jaquith
+ */
+@WikiRequestContext("adminProfiles")
+@UrlBinding("/AdministerProfiles.action")
+public class AdministerProfilesActionBean extends AbstractActionBean {
+
+ private String[] m_deleteLoginNames;
+ private List<UserProfile> m_profiles;
+
+ @ValidateNestedProperties ({
+ @Validate(field="loginName", required=true, minlength=3, maxlength=50),
+ @Validate(field="password", required=true, minlength=6, maxlength=128),
+ @Validate(field="fullName", required=true, minlength=3, maxlength=50),
+ @Validate(field="wikiName", required=true, minlength=3, maxlength=50),
+ @Validate(field="email", required=false, converter=EmailTypeConverter.class)
+ })
+
+ public String[] getDeleteLoginNames() { return m_deleteLoginNames; }
+ public void setDeleteLoginNames(String[] deleteLoginNames) { m_deleteLoginNames = deleteLoginNames; }
+
+ public List<UserProfile> getUserProfiles() { return m_profiles; }
+
+ public void setUserProfiles(List<UserProfile> profiles) { this.m_profiles = profiles; }
+
+ @HandlesEvent("Save") @DefaultHandler
+ public Resolution saveChanges() throws WikiSecurityException {
+ UserDatabase db = super.getContext().getWikiEngine().getUserManager().getUserDatabase();
+
+ // Apply any changes to existing profiles (and create new ones)
+ for (UserProfile profile : m_profiles) {
+
+ // Look up profile; create new if not found
+ UserProfile existingProfile;
+ try {
+ existingProfile = db.findByLoginName(profile.getLoginName());
+ }
+ catch (NoSuchPrincipalException e)
+ {
+ existingProfile = new DefaultUserProfile();
+ }
+
+ // Make changes to things that have changed
+ existingProfile.setLoginName(profile.getLoginName());
+ existingProfile.setFullname(profile.getFullname());
+ existingProfile.setEmail(profile.getEmail());
+ if (profile.getPassword() != null && profile.getPassword().length() > 0) {
+ existingProfile.setPassword(profile.getPassword());
+ }
+ db.save(existingProfile);
+ }
+
+ // Then, if the user checked anyone off to be deleted, delete them
+ if (m_deleteLoginNames != null) {
+ for (String loginName : m_deleteLoginNames) {
+ try {
+ db.deleteByLoginName(loginName);
+ }
+ catch (NoSuchPrincipalException e)
+ {
+ throw new WikiSecurityException(e.getMessage());
+ }
+ }
+ }
+
+ return new RedirectResolution("/AdministerProfiles.jsp");
+ }
+}
\ No newline at end of file
Added: incubator/jspwiki/branches/JSPWIKI_STRIPES_BRANCH/src/com/ecyrd/jspwiki/action/AttachActionBean.java
URL: http://svn.apache.org/viewvc/incubator/jspwiki/branches/JSPWIKI_STRIPES_BRANCH/src/com/ecyrd/jspwiki/action/AttachActionBean.java?rev=627255&view=auto
==============================================================================
--- incubator/jspwiki/branches/JSPWIKI_STRIPES_BRANCH/src/com/ecyrd/jspwiki/action/AttachActionBean.java (added)
+++ incubator/jspwiki/branches/JSPWIKI_STRIPES_BRANCH/src/com/ecyrd/jspwiki/action/AttachActionBean.java Tue Feb 12 21:53:55 2008
@@ -0,0 +1,20 @@
+package com.ecyrd.jspwiki.action;
+
+import net.sourceforge.stripes.action.HandlesEvent;
+import net.sourceforge.stripes.action.Resolution;
+import net.sourceforge.stripes.action.UrlBinding;
+
+import com.ecyrd.jspwiki.WikiContext;
+import com.ecyrd.jspwiki.auth.permissions.PagePermission;
+
+@WikiRequestContext("att")
+@UrlBinding("/attach/")
+public class AttachActionBean extends WikiContext
+{
+ @HandlesEvent("upload")
+ @EventPermission(permissionClass=PagePermission.class,target="${page.qualifiedName}",actions=PagePermission.UPLOAD_ACTION)
+ public Resolution upload()
+ {
+ return null;
+ }
+}
Added: incubator/jspwiki/branches/JSPWIKI_STRIPES_BRANCH/src/com/ecyrd/jspwiki/action/CommentActionBean.java
URL: http://svn.apache.org/viewvc/incubator/jspwiki/branches/JSPWIKI_STRIPES_BRANCH/src/com/ecyrd/jspwiki/action/CommentActionBean.java?rev=627255&view=auto
==============================================================================
--- incubator/jspwiki/branches/JSPWIKI_STRIPES_BRANCH/src/com/ecyrd/jspwiki/action/CommentActionBean.java (added)
+++ incubator/jspwiki/branches/JSPWIKI_STRIPES_BRANCH/src/com/ecyrd/jspwiki/action/CommentActionBean.java Tue Feb 12 21:53:55 2008
@@ -0,0 +1,20 @@
+package com.ecyrd.jspwiki.action;
+
+import net.sourceforge.stripes.action.HandlesEvent;
+import net.sourceforge.stripes.action.Resolution;
+import net.sourceforge.stripes.action.UrlBinding;
+
+import com.ecyrd.jspwiki.WikiContext;
+import com.ecyrd.jspwiki.auth.permissions.PagePermission;
+
+@WikiRequestContext("comment")
+@UrlBinding("/Comment.action")
+public class CommentActionBean extends WikiContext
+{
+ @HandlesEvent("comment")
+ @EventPermission(permissionClass=PagePermission.class, target="${page.qualifiedName}", actions=PagePermission.COMMENT_ACTION)
+ public Resolution comment()
+ {
+ return null;
+ }
+}
Added: incubator/jspwiki/branches/JSPWIKI_STRIPES_BRANCH/src/com/ecyrd/jspwiki/action/DeleteActionBean.java
URL: http://svn.apache.org/viewvc/incubator/jspwiki/branches/JSPWIKI_STRIPES_BRANCH/src/com/ecyrd/jspwiki/action/DeleteActionBean.java?rev=627255&view=auto
==============================================================================
--- incubator/jspwiki/branches/JSPWIKI_STRIPES_BRANCH/src/com/ecyrd/jspwiki/action/DeleteActionBean.java (added)
+++ incubator/jspwiki/branches/JSPWIKI_STRIPES_BRANCH/src/com/ecyrd/jspwiki/action/DeleteActionBean.java Tue Feb 12 21:53:55 2008
@@ -0,0 +1,20 @@
+package com.ecyrd.jspwiki.action;
+
+import net.sourceforge.stripes.action.HandlesEvent;
+import net.sourceforge.stripes.action.Resolution;
+import net.sourceforge.stripes.action.UrlBinding;
+
+import com.ecyrd.jspwiki.WikiContext;
+import com.ecyrd.jspwiki.auth.permissions.PagePermission;
+
+@WikiRequestContext("del")
+@UrlBinding("/Delete.action")
+public class DeleteActionBean extends WikiContext
+{
+ @HandlesEvent("delete")
+ @EventPermission(permissionClass=PagePermission.class, target="${page.qualifiedName}", actions=PagePermission.DELETE_ACTION)
+ public Resolution delete()
+ {
+ return null;
+ }
+}
Added: incubator/jspwiki/branches/JSPWIKI_STRIPES_BRANCH/src/com/ecyrd/jspwiki/action/DiffActionBean.java
URL: http://svn.apache.org/viewvc/incubator/jspwiki/branches/JSPWIKI_STRIPES_BRANCH/src/com/ecyrd/jspwiki/action/DiffActionBean.java?rev=627255&view=auto
==============================================================================
--- incubator/jspwiki/branches/JSPWIKI_STRIPES_BRANCH/src/com/ecyrd/jspwiki/action/DiffActionBean.java (added)
+++ incubator/jspwiki/branches/JSPWIKI_STRIPES_BRANCH/src/com/ecyrd/jspwiki/action/DiffActionBean.java Tue Feb 12 21:53:55 2008
@@ -0,0 +1,20 @@
+package com.ecyrd.jspwiki.action;
+
+import net.sourceforge.stripes.action.HandlesEvent;
+import net.sourceforge.stripes.action.Resolution;
+import net.sourceforge.stripes.action.UrlBinding;
+
+import com.ecyrd.jspwiki.WikiContext;
+import com.ecyrd.jspwiki.auth.permissions.PagePermission;
+
+@WikiRequestContext("diff")
+@UrlBinding("/Diff.action")
+public class DiffActionBean extends WikiContext
+{
+ @HandlesEvent("diff")
+ @EventPermission(permissionClass=PagePermission.class, target="${page.qualifiedName}", actions=PagePermission.VIEW_ACTION)
+ public Resolution diff()
+ {
+ return null;
+ }
+}
Added: incubator/jspwiki/branches/JSPWIKI_STRIPES_BRANCH/src/com/ecyrd/jspwiki/action/EditActionBean.java
URL: http://svn.apache.org/viewvc/incubator/jspwiki/branches/JSPWIKI_STRIPES_BRANCH/src/com/ecyrd/jspwiki/action/EditActionBean.java?rev=627255&view=auto
==============================================================================
--- incubator/jspwiki/branches/JSPWIKI_STRIPES_BRANCH/src/com/ecyrd/jspwiki/action/EditActionBean.java (added)
+++ incubator/jspwiki/branches/JSPWIKI_STRIPES_BRANCH/src/com/ecyrd/jspwiki/action/EditActionBean.java Tue Feb 12 21:53:55 2008
@@ -0,0 +1,20 @@
+package com.ecyrd.jspwiki.action;
+
+import net.sourceforge.stripes.action.HandlesEvent;
+import net.sourceforge.stripes.action.Resolution;
+import net.sourceforge.stripes.action.UrlBinding;
+
+import com.ecyrd.jspwiki.WikiContext;
+import com.ecyrd.jspwiki.auth.permissions.PagePermission;
+
+@WikiRequestContext("edit")
+@UrlBinding("/Edit.action")
+public class EditActionBean extends WikiContext
+{
+ @HandlesEvent("edit")
+ @EventPermission(permissionClass=PagePermission.class, target="${page.qualifiedName}", actions=PagePermission.EDIT_ACTION)
+ public Resolution edit()
+ {
+ return null;
+ }
+}
Added: incubator/jspwiki/branches/JSPWIKI_STRIPES_BRANCH/src/com/ecyrd/jspwiki/action/ErrorActionBean.java
URL: http://svn.apache.org/viewvc/incubator/jspwiki/branches/JSPWIKI_STRIPES_BRANCH/src/com/ecyrd/jspwiki/action/ErrorActionBean.java?rev=627255&view=auto
==============================================================================
--- incubator/jspwiki/branches/JSPWIKI_STRIPES_BRANCH/src/com/ecyrd/jspwiki/action/ErrorActionBean.java (added)
+++ incubator/jspwiki/branches/JSPWIKI_STRIPES_BRANCH/src/com/ecyrd/jspwiki/action/ErrorActionBean.java Tue Feb 12 21:53:55 2008
@@ -0,0 +1,9 @@
+package com.ecyrd.jspwiki.action;
+
+import net.sourceforge.stripes.action.UrlBinding;
+
+@WikiRequestContext("error")
+@UrlBinding("/Error.action")
+public class ErrorActionBean extends AbstractActionBean
+{
+}
Added: incubator/jspwiki/branches/JSPWIKI_STRIPES_BRANCH/src/com/ecyrd/jspwiki/action/EventPermission.java
URL: http://svn.apache.org/viewvc/incubator/jspwiki/branches/JSPWIKI_STRIPES_BRANCH/src/com/ecyrd/jspwiki/action/EventPermission.java?rev=627255&view=auto
==============================================================================
--- incubator/jspwiki/branches/JSPWIKI_STRIPES_BRANCH/src/com/ecyrd/jspwiki/action/EventPermission.java (added)
+++ incubator/jspwiki/branches/JSPWIKI_STRIPES_BRANCH/src/com/ecyrd/jspwiki/action/EventPermission.java Tue Feb 12 21:53:55 2008
@@ -0,0 +1,76 @@
+/**
+ *
+ */
+package com.ecyrd.jspwiki.action;
+
+import java.lang.annotation.*;
+import java.security.Permission;
+
+/**
+ * <p>Permission annotation that specifies the the class, target and actions of a
+ * Permission required to execute a Stripes event method. Annotations are
+ * specified as follows:</p>
+ * <blockquote>
+ * <code>
+ * @EventPermission(permissionClass="<var>className</var>",target="<var>target</var>",actions="<var>actions</var>")
+ * </code>
+ * </blockquote>
+ * </p>
+ * <p>The target and actions can be expression language (EL) expressions;
+ * if so, they must start with <code>${</code> and end with <code>}</code>.
+ * The expression is evaluated with the ActionBean as the "root." Thus, the expression
+ * <code>${foo}</code> evaluates to the value of the bean's <code>foo</code>
+ * property, typically as returned by an accessor such as <code>getFoo()</code>.
+ * Mapped properties can also be used. For example, if the <code>Map</code>
+ * property's accessor method <code>getBarMap()</code> returns a Map, the expression
+ * <code>${barMap['Alice']}</code> evaluates to the Map value whose key is
+ * <code>Alice</code>. <em>EL expressions that do not evaluate to valid values
+ * should be discarded by callers.</em></p>
+ * <p>When the target or actions parameter values are <em>not</em> EL expressions, they
+ * are interpreted as literals.</p>
+ * <p>For example, the following are all valid annotations:</p>
+ * <blockquote>
+ * <code>
+ * @EventPermission(permissionClass="java.io.FilePermission", target="/tmp/-", actions="read,write")
+ * <br/><br/>
+ * @EventPermission(permissionClass="com.ecyrd.jspwiki.auth.permissions.PagePermission", target="${page.name}", actions = "edit")
+ * <br/><br/>
+ * @EventPermission(permissionClass="com.ecyrd.jspwiki.auth.permissions.GroupPermission", target="${context.request.parameterMap['group'][0]}",actions="view")
+ * </code>
+ * </blockquote>
+ * <p>These examples assume that the ActionBeans they annotate contain the appropriate properties;
+ * in this case, <code>page</code> is assumed to be a {@link com.ecyrd.jspwiki.WikiPage} property,
+ * and <code>context</code> is assumed to be a {@link com.ecyrd.jspwiki.action.WikiActionBeanContext}
+ * property.</p>
+ * <p>This Annotation class does not parse, process or instantiate Permissions; it merely specifies
+ * the syntax for annotating event handler methods. The collaborating class {@link EventPermissionInfo}
+ * actually does the heavy lifting.</p>
+ * @author Andrew Jaquith
+ */
+@Retention(RetentionPolicy.RUNTIME)
+@Target( { ElementType.METHOD })
+@Documented
+@Inherited
+public @interface EventPermission
+{
+ /**
+ * The class of the Permission.
+ */
+ Class<? extends Permission> permissionClass();
+
+ /**
+ * The Permission target, supplied as a static String or as an EL
+ * expression.
+ *
+ * @return the target
+ */
+ String target();
+
+ /**
+ * The Permission actions, supplied as a static String or as an EL
+ * expression.
+ *
+ * @return the actions
+ */
+ String actions();
+}